rudy 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES.txt +54 -30
- data/README.rdoc +100 -12
- data/Rakefile +103 -8
- data/Rudyfile +119 -0
- data/bin/ird +175 -0
- data/bin/rudy +259 -156
- data/bin/rudy-ec2 +228 -95
- data/bin/rudy-s3 +76 -0
- data/bin/rudy-sdb +67 -0
- data/lib/annoy.rb +270 -0
- data/lib/console.rb +30 -9
- data/lib/escape.rb +305 -0
- data/lib/rudy.rb +151 -182
- data/lib/rudy/aws.rb +56 -49
- data/lib/rudy/aws/ec2.rb +47 -292
- data/lib/rudy/aws/ec2/address.rb +157 -0
- data/lib/rudy/aws/ec2/group.rb +301 -0
- data/lib/rudy/aws/ec2/image.rb +168 -0
- data/lib/rudy/aws/ec2/instance.rb +434 -0
- data/lib/rudy/aws/ec2/keypair.rb +104 -0
- data/lib/rudy/aws/ec2/snapshot.rb +98 -0
- data/lib/rudy/aws/ec2/volume.rb +230 -0
- data/lib/rudy/aws/ec2/zone.rb +77 -0
- data/lib/rudy/aws/s3.rb +54 -0
- data/lib/rudy/aws/sdb.rb +298 -0
- data/lib/rudy/aws/sdb/error.rb +46 -0
- data/lib/rudy/{metadata/backup.rb → backup.rb} +26 -51
- data/lib/rudy/cli.rb +157 -0
- data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
- data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
- data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
- data/lib/rudy/cli/aws/ec2/images.rb +196 -0
- data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
- data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
- data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
- data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
- data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
- data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
- data/lib/rudy/cli/aws/s3/store.rb +22 -0
- data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
- data/lib/rudy/cli/candy.rb +8 -0
- data/lib/rudy/{command → cli}/config.rb +34 -24
- data/lib/rudy/cli/disks.rb +35 -0
- data/lib/rudy/cli/machines.rb +94 -0
- data/lib/rudy/cli/routines.rb +57 -0
- data/lib/rudy/config.rb +77 -72
- data/lib/rudy/config/objects.rb +29 -0
- data/lib/rudy/disks.rb +248 -0
- data/lib/rudy/global.rb +121 -0
- data/lib/rudy/huxtable.rb +340 -0
- data/lib/rudy/machines.rb +245 -0
- data/lib/rudy/metadata.rb +123 -13
- data/lib/rudy/routines.rb +47 -0
- data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
- data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
- data/lib/rudy/routines/release.rb +34 -0
- data/lib/rudy/routines/shutdown.rb +57 -0
- data/lib/rudy/routines/startup.rb +58 -0
- data/lib/rudy/scm/svn.rb +1 -1
- data/lib/rudy/utils.rb +322 -4
- data/lib/storable.rb +26 -17
- data/lib/sysinfo.rb +274 -0
- data/lib/tryouts.rb +6 -13
- data/rudy.gemspec +128 -42
- data/support/randomize-root-password +45 -0
- data/support/rudy-ec2-startup +9 -9
- data/support/update-ec2-ami-tools +20 -0
- data/test/05_config/00_setup_test.rb +20 -0
- data/test/05_config/30_machines_test.rb +69 -0
- data/test/20_sdb/00_setup_test.rb +16 -0
- data/test/20_sdb/10_domains_test.rb +115 -0
- data/test/25_ec2/00_setup_test.rb +29 -0
- data/test/25_ec2/10_keypairs_test.rb +41 -0
- data/test/25_ec2/20_groups_test.rb +131 -0
- data/test/25_ec2/30_addresses_test.rb +38 -0
- data/test/25_ec2/40_volumes_test.rb +49 -0
- data/test/25_ec2/50_snapshots_test.rb +74 -0
- data/test/26_ec2_instances/00_setup_test.rb +28 -0
- data/test/26_ec2_instances/10_instances_test.rb +83 -0
- data/test/26_ec2_instances/50_images_test.rb +13 -0
- data/test/30_sdb_metadata/00_setup_test.rb +21 -0
- data/test/30_sdb_metadata/10_disks_test.rb +109 -0
- data/test/30_sdb_metadata/20_backups_test.rb +102 -0
- data/test/coverage.txt +51 -0
- data/test/helper.rb +36 -0
- data/vendor/highline-1.5.1/CHANGELOG +222 -0
- data/vendor/highline-1.5.1/INSTALL +35 -0
- data/vendor/highline-1.5.1/LICENSE +7 -0
- data/vendor/highline-1.5.1/README +63 -0
- data/vendor/highline-1.5.1/Rakefile +82 -0
- data/vendor/highline-1.5.1/TODO +6 -0
- data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
- data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
- data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
- data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
- data/vendor/highline-1.5.1/examples/limit.rb +12 -0
- data/vendor/highline-1.5.1/examples/menus.rb +65 -0
- data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
- data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
- data/vendor/highline-1.5.1/examples/password.rb +7 -0
- data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
- data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
- data/vendor/highline-1.5.1/lib/highline.rb +758 -0
- data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
- data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
- data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
- data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
- data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
- data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
- data/vendor/highline-1.5.1/setup.rb +1360 -0
- data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
- data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
- data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
- data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
- data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
- metadata +141 -38
- data/lib/aws_sdb.rb +0 -3
- data/lib/aws_sdb/error.rb +0 -42
- data/lib/aws_sdb/service.rb +0 -215
- data/lib/rudy/aws/simpledb.rb +0 -53
- data/lib/rudy/command/addresses.rb +0 -46
- data/lib/rudy/command/backups.rb +0 -175
- data/lib/rudy/command/base.rb +0 -841
- data/lib/rudy/command/deploy.rb +0 -12
- data/lib/rudy/command/disks.rb +0 -213
- data/lib/rudy/command/environment.rb +0 -73
- data/lib/rudy/command/groups.rb +0 -61
- data/lib/rudy/command/images.rb +0 -91
- data/lib/rudy/command/instances.rb +0 -85
- data/lib/rudy/command/machines.rb +0 -161
- data/lib/rudy/command/metadata.rb +0 -41
- data/lib/rudy/command/release.rb +0 -174
- data/lib/rudy/command/volumes.rb +0 -66
- data/lib/rudy/metadata/disk.rb +0 -138
- data/tryouts/console_tryout.rb +0 -91
data/lib/rudy/cli.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
|
2
|
+
require 'drydock'
|
3
|
+
|
4
|
+
module Rudy
|
5
|
+
|
6
|
+
# = CLI
|
7
|
+
#
|
8
|
+
# These classes provide the functionality for the Command
|
9
|
+
# line interfaces. See the bin/ files if you're interested.
|
10
|
+
#
|
11
|
+
module CLI
|
12
|
+
class NoCred < RuntimeError #:nodoc
|
13
|
+
end
|
14
|
+
|
15
|
+
class Output < Storable
|
16
|
+
# TODO: Use for all CLI responses
|
17
|
+
# Messages and errors should be in @@global.format
|
18
|
+
# Should print messages as they come
|
19
|
+
end
|
20
|
+
|
21
|
+
class CommandBase < Drydock::Command
|
22
|
+
include Rudy::Huxtable
|
23
|
+
|
24
|
+
attr_reader :config
|
25
|
+
|
26
|
+
protected
|
27
|
+
def init
|
28
|
+
|
29
|
+
# The CLI wants output!
|
30
|
+
Rudy::Huxtable.update_logger STDOUT
|
31
|
+
|
32
|
+
# Send The Huxtables the global values from the command-line
|
33
|
+
Rudy::Huxtable.update_global @global
|
34
|
+
|
35
|
+
unless @@global.accesskey && @@global.secretkey
|
36
|
+
STDERR.puts "No AWS credentials. Check your configs!"
|
37
|
+
STDERR.puts "Try: rudy init"
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
if @@global.environment =~ /^prod/ && Drydock.debug?
|
42
|
+
puts Rudy::Utils.banner("PRODUCTION ACCESS IS DISABLED IN DEBUG MODE")
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute_action(emsg="Failed", &action)
|
49
|
+
begin
|
50
|
+
ret = action.call
|
51
|
+
raise emsg unless ret
|
52
|
+
ret
|
53
|
+
rescue Rudy::AWS::EC2::NoAMI => ex
|
54
|
+
raise Drydock::OptError.new('-a', @alias)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute_check(level=:medium)
|
59
|
+
ret = Annoy.are_you_sure?(level)
|
60
|
+
exit 0 unless ret
|
61
|
+
ret
|
62
|
+
end
|
63
|
+
|
64
|
+
# Print a default header to the screen for every command.
|
65
|
+
#
|
66
|
+
def print_header
|
67
|
+
|
68
|
+
# Send The Huxtables the global values again because they could be
|
69
|
+
# updated after initialization but before the command was executed
|
70
|
+
Rudy::Huxtable.update_global @global
|
71
|
+
|
72
|
+
puts Rudy::CLI.generate_header(@@global, @@config) if @@global.print_header
|
73
|
+
|
74
|
+
unless @@global.quiet
|
75
|
+
if @@global.environment == "prod"
|
76
|
+
msg = "YOU ARE PLAYING WITH PRODUCTION"
|
77
|
+
puts Rudy::Utils.banner(msg, :huge, :red), $/
|
78
|
+
end
|
79
|
+
puts Rudy::Utils.banner("THIS IS EC2"), $/ if Rudy.in_situ?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.generate_header(global, config)
|
86
|
+
return "" if global.quiet
|
87
|
+
header = StringIO.new
|
88
|
+
title, name = "RUDY v#{Rudy::VERSION}", config.accounts.aws.name
|
89
|
+
now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
|
90
|
+
criteria = []
|
91
|
+
[:region, :zone, :environment, :role, :position].each do |n|
|
92
|
+
key, val = n.to_s.slice(0,1).att, global.send(n)
|
93
|
+
key = 'R' if n == :region
|
94
|
+
next unless val
|
95
|
+
criteria << "#{key.att}:#{val.to_s.bright}"
|
96
|
+
end
|
97
|
+
if config.accounts && config.accounts.aws
|
98
|
+
if global.verbose > 0
|
99
|
+
header.puts '%s -- %s -- %s UTC' % [title, name, now_utc]
|
100
|
+
end
|
101
|
+
header.puts '[%s]' % [criteria.join(" ")], $/
|
102
|
+
end
|
103
|
+
header.rewind
|
104
|
+
header.read
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# A base for all Drydock executables (bin/rudy etc...).
|
109
|
+
class Base
|
110
|
+
extend Drydock
|
111
|
+
|
112
|
+
before do |obj|
|
113
|
+
# Don't print Rudy header unless requested to
|
114
|
+
obj.global.print_header = false if (obj.global.verbose == 0)
|
115
|
+
@start = Time.now
|
116
|
+
end
|
117
|
+
|
118
|
+
after do |obj|
|
119
|
+
if obj.global.verbose > 0
|
120
|
+
puts
|
121
|
+
@elapsed = Time.now - @start
|
122
|
+
puts "Elapsed: %.2f seconds" % @elapsed.to_f if @elapsed > 0.1
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# These globals are used by all bin/ executables
|
128
|
+
global :A, :accesskey, String, "AWS Access Key"
|
129
|
+
global :S, :secretkey, String, "AWS Secret Access Key"
|
130
|
+
global :R, :region, String, "Amazon service region (ie: #{Rudy::DEFAULT_REGION})"
|
131
|
+
global :z, :zone, String, "Amazon Availability zone (ie: #{Rudy::DEFAULT_ZONE})"
|
132
|
+
global :u, :user, String, "Provide a username (ie: #{Rudy::DEFAULT_USER})"
|
133
|
+
global :k, :pkey, String, "Path to the private SSH key"
|
134
|
+
global :f, :format, String, "Output format"
|
135
|
+
global :n, :nocolor, "Disable output colors"
|
136
|
+
global :C, :config, String, "Specify another configuration file to read (ie: #{Rudy::CONFIG_FILE})"
|
137
|
+
global :Y, :yes, "Assume a correct answer to confirmation questions"
|
138
|
+
global :q, :quiet, "Run with less output"
|
139
|
+
global :v, :verbose, "Increase verbosity of output (i.e. -v or -vv or -vvv)" do
|
140
|
+
@verbose ||= 0
|
141
|
+
@verbose += 1
|
142
|
+
end
|
143
|
+
global :V, :version, "Display version number" do
|
144
|
+
puts "Rudy version: #{Rudy::VERSION}"
|
145
|
+
exit 0
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'cli', '**', '*.rb')
|
156
|
+
|
157
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Rudy; module CLI;
|
4
|
+
module AWS; module EC2;
|
5
|
+
|
6
|
+
class Addresses < Rudy::CLI::CommandBase
|
7
|
+
|
8
|
+
def addresses_create
|
9
|
+
radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
10
|
+
address = radd.create
|
11
|
+
puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
|
12
|
+
end
|
13
|
+
|
14
|
+
def addresses_destroy_valid?
|
15
|
+
raise Drydock::ArgError.new("IP address", @alias) unless @argv.ipaddress
|
16
|
+
@radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
17
|
+
raise "#{@argv.ipaddress} is not allocated to you" unless @radd.exists?(@argv.ipaddress)
|
18
|
+
raise "#{@argv.ipaddress} is associated!" if @radd.associated?(@argv.ipaddress)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
def addresses_destroy
|
22
|
+
address = @radd.get(@argv.ipaddress)
|
23
|
+
raise "Could not fetch #{address.ipaddress}" unless address
|
24
|
+
|
25
|
+
puts "Destroying address: #{@argv.ipaddress}"
|
26
|
+
puts "NOTE: this IP address will become available to other EC2 customers.".color(:blue)
|
27
|
+
execute_check(:medium)
|
28
|
+
execute_action { @radd.destroy(@argv.ipaddress) }
|
29
|
+
self.addresses
|
30
|
+
end
|
31
|
+
|
32
|
+
def associate_addresses_valid?
|
33
|
+
raise Drydock::ArgError.new('IP address', @alias) if !@argv.ipaddress && !@option.newaddress
|
34
|
+
raise Drydock::OptError.new('instance ID', @alias) if !@option.instance
|
35
|
+
true
|
36
|
+
end
|
37
|
+
def associate_addresses
|
38
|
+
radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
39
|
+
rinst = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
40
|
+
|
41
|
+
raise "Instance #{@argv.instid} does not exist!" unless rinst.exists?(@option.instance)
|
42
|
+
|
43
|
+
if @option.newaddress
|
44
|
+
print "Creating address... "
|
45
|
+
tmp = radd.create
|
46
|
+
puts "#{tmp.ipaddress}"
|
47
|
+
address = tmp.ipaddress
|
48
|
+
else
|
49
|
+
address = @argv.ipaddress
|
50
|
+
end
|
51
|
+
|
52
|
+
raise "#{address} is not allocated to you" unless radd.exists?(address)
|
53
|
+
raise "#{address} is already associated!" if radd.associated?(address)
|
54
|
+
|
55
|
+
instance = rinst.get(@option.instance)
|
56
|
+
|
57
|
+
# If an instance was recently disassoiciated, the dns_public may
|
58
|
+
# not be updated yet
|
59
|
+
instance_name = instance.dns_public
|
60
|
+
instance_name = instance.awsid if !instance_name || instance_name.empty?
|
61
|
+
|
62
|
+
puts "Associating #{address} to #{instance_name} (#{instance.groups.join(', ')})"
|
63
|
+
execute_check(:low)
|
64
|
+
execute_action { radd.associate(address, instance.awsid) }
|
65
|
+
address = radd.get(address)
|
66
|
+
puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
|
67
|
+
end
|
68
|
+
|
69
|
+
def disassociate_addresses_valid?
|
70
|
+
raise "You have not supplied an IP addresses" unless @argv.ipaddress
|
71
|
+
true
|
72
|
+
end
|
73
|
+
def disassociate_addresses
|
74
|
+
radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
75
|
+
rinst = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
76
|
+
raise "#{@argv.ipaddress} is not allocated to you" unless radd.exists?(@argv.ipaddress)
|
77
|
+
raise "#{@argv.ipaddress} is not associated!" unless radd.associated?(@argv.ipaddress)
|
78
|
+
|
79
|
+
address = radd.get(@argv.ipaddress)
|
80
|
+
instance = rinst.get(address.instid)
|
81
|
+
|
82
|
+
puts "Disassociating #{address.ipaddress} from #{instance.awsid} (#{instance.groups.join(', ')})"
|
83
|
+
execute_check(:medium)
|
84
|
+
execute_action { radd.disassociate(@argv.ipaddress) }
|
85
|
+
address = radd.get(@argv.ipaddress)
|
86
|
+
puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
|
87
|
+
end
|
88
|
+
|
89
|
+
def addresses
|
90
|
+
radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
91
|
+
addresses = radd.list || []
|
92
|
+
|
93
|
+
addresses.each do |address|
|
94
|
+
puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
|
95
|
+
end
|
96
|
+
|
97
|
+
puts "No Addresses" if addresses.empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end; end
|
104
|
+
end; end
|
105
|
+
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
module Rudy; module CLI;
|
3
|
+
module AWS; module EC2;
|
4
|
+
|
5
|
+
class Candy < Rudy::CLI::CommandBase
|
6
|
+
|
7
|
+
def status_valid?
|
8
|
+
avail = Rudy::Utils.service_available?('status.aws.amazon.com', 80, 5)
|
9
|
+
raise ServiceUnavailable, 'status.aws.amazon.com' unless avail
|
10
|
+
true
|
11
|
+
end
|
12
|
+
def status
|
13
|
+
url = 'http://status.aws.amazon.com/rss/EC2.rss'
|
14
|
+
# TODO: Move to Rudy::AWS
|
15
|
+
ec2 = Rudy::Utils::RSSReader.run(url) || {}
|
16
|
+
|
17
|
+
# TODO: Create Storable object
|
18
|
+
if @@global.format == 'yaml'
|
19
|
+
puts ec2.to_yaml
|
20
|
+
elsif @@global.format == 'json'
|
21
|
+
require 'json'
|
22
|
+
puts ec2.to_json
|
23
|
+
else
|
24
|
+
puts "Updated: #{ec2[:pubdate]} (updated every #{ec2[:ttl]} minutes)"
|
25
|
+
ec2[:items].each do |i|
|
26
|
+
puts
|
27
|
+
puts '%s' % i[:title]
|
28
|
+
puts ' %s: %s' % [i[:pubdate], i[:description]]
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ssh_valid?
|
35
|
+
if @@global.pkey
|
36
|
+
raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
|
37
|
+
raise "Insecure permissions for #{@@global.pkey}" unless (File.stat(@@global.pkey).mode & 600) == 0
|
38
|
+
end
|
39
|
+
if @option.group
|
40
|
+
rgroup = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
41
|
+
raise "Cannot supply group and instance ID" if @option.instid
|
42
|
+
raise "Group #{@option.group} does not exist" unless rgroup.exists?(@option.group)
|
43
|
+
end
|
44
|
+
if @option.instid && !Rudy::Utils.is_id?(:instance, @option.instid)
|
45
|
+
raise "#{@option.instid} is not an instance ID"
|
46
|
+
end
|
47
|
+
true
|
48
|
+
end
|
49
|
+
def ssh
|
50
|
+
opts = {}
|
51
|
+
opts[:group] = @option.group if @option.group
|
52
|
+
opts[:group] = :any if @option.all
|
53
|
+
opts[:id] = @option.instid if @option.instid
|
54
|
+
|
55
|
+
# Options to be sent to Net::SSH
|
56
|
+
ssh_opts = { :user => @global.user || Rudy.sysinfo.user, :debug => nil }
|
57
|
+
if @@global.pkey
|
58
|
+
raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
|
59
|
+
raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
|
60
|
+
ssh_opts[:keys] = @@global.pkey
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# The user specified a command to run. We won't create an interactive
|
65
|
+
# session so we need to prepare the command and its arguments
|
66
|
+
if @argv.first
|
67
|
+
command, command_args = @argv.shift, @argv || []
|
68
|
+
puts "#{command} #{command_args.join(' ')}" if @@global.verbose > 1
|
69
|
+
|
70
|
+
# otherwise, we'll open an ssh session or print command
|
71
|
+
else
|
72
|
+
command, command_args = :interactive_ssh, @option.print.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
checked = false
|
76
|
+
rudy = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
77
|
+
lt = rudy.list_group(opts[:group], :running, opts[:id]) do |inst|
|
78
|
+
|
79
|
+
# Print header
|
80
|
+
if @@global.quiet
|
81
|
+
print "You are #{ssh_opts[:user].bright}. " if !checked # only the 1st
|
82
|
+
else
|
83
|
+
print "Connecting #{ssh_opts[:user].bright}@#{inst.dns_public} "
|
84
|
+
puts "(#{inst.awsid}, groups: #{inst.groups.join(', ')})"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Make sure we want to run this command on all instances
|
88
|
+
if !checked && command != :interactive_ssh
|
89
|
+
execute_check(:medium) if ssh_opts[:user] == "root"
|
90
|
+
checked = true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Open the connection and run the command
|
94
|
+
rbox = Rye::Box.new(inst.dns_public, ssh_opts)
|
95
|
+
ret = rbox.send(command, command_args)
|
96
|
+
puts ret unless command == :interactive_ssh
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def copy_valid?
|
101
|
+
raise "You must supply a source and a target. See rudy-ec2 #{@alias} -h" unless @argv.size >= 2
|
102
|
+
raise "You cannot download and upload at the same time" if @option.download && @alias == 'upload'
|
103
|
+
raise "You cannot download and upload at the same time" if @option.upload && @alias == 'download'
|
104
|
+
true
|
105
|
+
end
|
106
|
+
def copy
|
107
|
+
|
108
|
+
opts = {}
|
109
|
+
opts[:group] = @option.group if @option.group
|
110
|
+
opts[:group] = :any if @option.all
|
111
|
+
|
112
|
+
opts[:id] = @argv.shift if Rudy::Utils.is_id?(:instance, @argv.first)
|
113
|
+
opts[:id] &&= [opts[:id]].flatten
|
114
|
+
|
115
|
+
# * +:recursive: recursively transfer directories (default: false)
|
116
|
+
# * +:preserve: preserve atimes and ctimes (default: false)
|
117
|
+
# * +:task+ one of: :upload (default), :download.
|
118
|
+
# * +:paths+ an array of paths to copy. The last element is the "to" path.
|
119
|
+
opts[:recursive] = @option.recursive ? true : false
|
120
|
+
opts[:preserve] = @option.preserve ? true : false
|
121
|
+
|
122
|
+
opts[:paths] = @argv
|
123
|
+
opts[:dest] = opts[:paths].pop
|
124
|
+
|
125
|
+
opts[:task] = :download if %w(dl download).member?(@alias) || @option.download
|
126
|
+
opts[:task] = :upload if %w(ul upload).member?(@alias)
|
127
|
+
opts[:task] ||= :upload
|
128
|
+
opts[:user] = @global.user || Rudy.sysinfo.user
|
129
|
+
|
130
|
+
# Options to be sent to Net::SSH
|
131
|
+
ssh_opts = { :user => opts[:user], :debug => nil }
|
132
|
+
ssh_opts[:keys] = @@global.pkey if @@global.pkey
|
133
|
+
|
134
|
+
if @@global.pkey
|
135
|
+
raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
|
136
|
+
raise "Insecure permissions for #{@@global.pkey}" unless (File.stat(@@global.pkey).mode & 600) == 0
|
137
|
+
end
|
138
|
+
|
139
|
+
checked = false
|
140
|
+
rudy = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
|
141
|
+
lt = rudy.list_group(opts[:group], :running, opts[:id]) do |inst|
|
142
|
+
|
143
|
+
if @option.print
|
144
|
+
Rudy::Utils.scp_command inst.dns_public, @@global.pkey, opts[:user], opts[:paths], opts[:dest], (opts[:task] == :download), false, @option.print
|
145
|
+
next
|
146
|
+
end
|
147
|
+
|
148
|
+
# Print header
|
149
|
+
if @@global.quiet
|
150
|
+
print "You are #{ssh_opts[:user].bright}. " if !checked # only the 1st
|
151
|
+
else
|
152
|
+
print "Connecting #{ssh_opts[:user].bright}@#{inst.dns_public} "
|
153
|
+
puts "(#{inst.awsid}, groups: #{inst.groups.join(', ')})"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Make sure we want to run this command on all instances
|
157
|
+
if !checked
|
158
|
+
#execute_check(:medium) if opts[:user] == "root"
|
159
|
+
checked = true
|
160
|
+
end
|
161
|
+
|
162
|
+
scp_opts = {
|
163
|
+
:recursive => opts[:recursive],
|
164
|
+
:preserve => opts[:preserve],
|
165
|
+
:chunk_size => 16384
|
166
|
+
}
|
167
|
+
|
168
|
+
Candy.scp(opts[:task], inst.dns_public, opts[:user], @@global.pkey, opts[:paths], opts[:dest], scp_opts)
|
169
|
+
puts
|
170
|
+
puts unless @@global.quiet
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def Candy.scp(task, host, user, keypairpath, paths, dest, opts)
|
179
|
+
|
180
|
+
connect_opts = {}
|
181
|
+
connect_opts[:keys] = [keypairpath] if keypairpath
|
182
|
+
|
183
|
+
Net::SCP.start(host, user, connect_opts) do |scp|
|
184
|
+
|
185
|
+
paths.each do |path|
|
186
|
+
prev_path = nil
|
187
|
+
scp.send("#{task}", path, dest, opts) do |ch, name, sent, total|
|
188
|
+
#print "#{name}: #{sent}/#{total}\r"
|
189
|
+
msg = ((prev_path == name) ? "\r" : "\n") # new line for new file
|
190
|
+
msg << "#{name}: #{sent}/#{total}" # otherwise, update the same line
|
191
|
+
print msg
|
192
|
+
STDOUT.flush # update the screen every cycle
|
193
|
+
prev_path = name
|
194
|
+
break if sent == total
|
195
|
+
end
|
196
|
+
puts unless prev_path == path
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end; end
|
208
|
+
end; end
|