rudy 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. data/CHANGES.txt +54 -30
  2. data/README.rdoc +100 -12
  3. data/Rakefile +103 -8
  4. data/Rudyfile +119 -0
  5. data/bin/ird +175 -0
  6. data/bin/rudy +259 -156
  7. data/bin/rudy-ec2 +228 -95
  8. data/bin/rudy-s3 +76 -0
  9. data/bin/rudy-sdb +67 -0
  10. data/lib/annoy.rb +270 -0
  11. data/lib/console.rb +30 -9
  12. data/lib/escape.rb +305 -0
  13. data/lib/rudy.rb +151 -182
  14. data/lib/rudy/aws.rb +56 -49
  15. data/lib/rudy/aws/ec2.rb +47 -292
  16. data/lib/rudy/aws/ec2/address.rb +157 -0
  17. data/lib/rudy/aws/ec2/group.rb +301 -0
  18. data/lib/rudy/aws/ec2/image.rb +168 -0
  19. data/lib/rudy/aws/ec2/instance.rb +434 -0
  20. data/lib/rudy/aws/ec2/keypair.rb +104 -0
  21. data/lib/rudy/aws/ec2/snapshot.rb +98 -0
  22. data/lib/rudy/aws/ec2/volume.rb +230 -0
  23. data/lib/rudy/aws/ec2/zone.rb +77 -0
  24. data/lib/rudy/aws/s3.rb +54 -0
  25. data/lib/rudy/aws/sdb.rb +298 -0
  26. data/lib/rudy/aws/sdb/error.rb +46 -0
  27. data/lib/rudy/{metadata/backup.rb → backup.rb} +26 -51
  28. data/lib/rudy/cli.rb +157 -0
  29. data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
  30. data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
  31. data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
  32. data/lib/rudy/cli/aws/ec2/images.rb +196 -0
  33. data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
  34. data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
  35. data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
  36. data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
  37. data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
  38. data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
  39. data/lib/rudy/cli/aws/s3/store.rb +22 -0
  40. data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
  41. data/lib/rudy/cli/candy.rb +8 -0
  42. data/lib/rudy/{command → cli}/config.rb +34 -24
  43. data/lib/rudy/cli/disks.rb +35 -0
  44. data/lib/rudy/cli/machines.rb +94 -0
  45. data/lib/rudy/cli/routines.rb +57 -0
  46. data/lib/rudy/config.rb +77 -72
  47. data/lib/rudy/config/objects.rb +29 -0
  48. data/lib/rudy/disks.rb +248 -0
  49. data/lib/rudy/global.rb +121 -0
  50. data/lib/rudy/huxtable.rb +340 -0
  51. data/lib/rudy/machines.rb +245 -0
  52. data/lib/rudy/metadata.rb +123 -13
  53. data/lib/rudy/routines.rb +47 -0
  54. data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
  55. data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
  56. data/lib/rudy/routines/release.rb +34 -0
  57. data/lib/rudy/routines/shutdown.rb +57 -0
  58. data/lib/rudy/routines/startup.rb +58 -0
  59. data/lib/rudy/scm/svn.rb +1 -1
  60. data/lib/rudy/utils.rb +322 -4
  61. data/lib/storable.rb +26 -17
  62. data/lib/sysinfo.rb +274 -0
  63. data/lib/tryouts.rb +6 -13
  64. data/rudy.gemspec +128 -42
  65. data/support/randomize-root-password +45 -0
  66. data/support/rudy-ec2-startup +9 -9
  67. data/support/update-ec2-ami-tools +20 -0
  68. data/test/05_config/00_setup_test.rb +20 -0
  69. data/test/05_config/30_machines_test.rb +69 -0
  70. data/test/20_sdb/00_setup_test.rb +16 -0
  71. data/test/20_sdb/10_domains_test.rb +115 -0
  72. data/test/25_ec2/00_setup_test.rb +29 -0
  73. data/test/25_ec2/10_keypairs_test.rb +41 -0
  74. data/test/25_ec2/20_groups_test.rb +131 -0
  75. data/test/25_ec2/30_addresses_test.rb +38 -0
  76. data/test/25_ec2/40_volumes_test.rb +49 -0
  77. data/test/25_ec2/50_snapshots_test.rb +74 -0
  78. data/test/26_ec2_instances/00_setup_test.rb +28 -0
  79. data/test/26_ec2_instances/10_instances_test.rb +83 -0
  80. data/test/26_ec2_instances/50_images_test.rb +13 -0
  81. data/test/30_sdb_metadata/00_setup_test.rb +21 -0
  82. data/test/30_sdb_metadata/10_disks_test.rb +109 -0
  83. data/test/30_sdb_metadata/20_backups_test.rb +102 -0
  84. data/test/coverage.txt +51 -0
  85. data/test/helper.rb +36 -0
  86. data/vendor/highline-1.5.1/CHANGELOG +222 -0
  87. data/vendor/highline-1.5.1/INSTALL +35 -0
  88. data/vendor/highline-1.5.1/LICENSE +7 -0
  89. data/vendor/highline-1.5.1/README +63 -0
  90. data/vendor/highline-1.5.1/Rakefile +82 -0
  91. data/vendor/highline-1.5.1/TODO +6 -0
  92. data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
  93. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
  94. data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
  95. data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
  96. data/vendor/highline-1.5.1/examples/limit.rb +12 -0
  97. data/vendor/highline-1.5.1/examples/menus.rb +65 -0
  98. data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
  99. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
  100. data/vendor/highline-1.5.1/examples/password.rb +7 -0
  101. data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
  102. data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
  103. data/vendor/highline-1.5.1/lib/highline.rb +758 -0
  104. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
  105. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
  106. data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
  107. data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
  108. data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
  109. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
  110. data/vendor/highline-1.5.1/setup.rb +1360 -0
  111. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
  112. data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
  113. data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
  114. data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
  115. data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
  116. metadata +141 -38
  117. data/lib/aws_sdb.rb +0 -3
  118. data/lib/aws_sdb/error.rb +0 -42
  119. data/lib/aws_sdb/service.rb +0 -215
  120. data/lib/rudy/aws/simpledb.rb +0 -53
  121. data/lib/rudy/command/addresses.rb +0 -46
  122. data/lib/rudy/command/backups.rb +0 -175
  123. data/lib/rudy/command/base.rb +0 -841
  124. data/lib/rudy/command/deploy.rb +0 -12
  125. data/lib/rudy/command/disks.rb +0 -213
  126. data/lib/rudy/command/environment.rb +0 -73
  127. data/lib/rudy/command/groups.rb +0 -61
  128. data/lib/rudy/command/images.rb +0 -91
  129. data/lib/rudy/command/instances.rb +0 -85
  130. data/lib/rudy/command/machines.rb +0 -161
  131. data/lib/rudy/command/metadata.rb +0 -41
  132. data/lib/rudy/command/release.rb +0 -174
  133. data/lib/rudy/command/volumes.rb +0 -66
  134. data/lib/rudy/metadata/disk.rb +0 -138
  135. data/tryouts/console_tryout.rb +0 -91
@@ -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