rudy 0.4.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|