solutious-rudy 0.9.0 → 0.9.1

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.
Files changed (48) hide show
  1. data/CHANGES.txt +61 -4
  2. data/README.rdoc +91 -53
  3. data/Rakefile +0 -92
  4. data/Rudyfile +15 -25
  5. data/bin/rudy +52 -41
  6. data/examples/gem-test.rb +92 -0
  7. data/lib/rudy.rb +15 -7
  8. data/lib/rudy/aws.rb +2 -2
  9. data/lib/rudy/aws/ec2.rb +2 -2
  10. data/lib/rudy/aws/ec2/instance.rb +3 -3
  11. data/lib/rudy/aws/ec2/volume.rb +4 -4
  12. data/lib/rudy/cli/aws/ec2/candy.rb +13 -13
  13. data/lib/rudy/cli/base.rb +10 -4
  14. data/lib/rudy/cli/config.rb +13 -3
  15. data/lib/rudy/cli/disks.rb +1 -1
  16. data/lib/rudy/cli/execbase.rb +5 -2
  17. data/lib/rudy/cli/machines.rb +231 -30
  18. data/lib/rudy/cli/networks.rb +34 -0
  19. data/lib/rudy/cli/routines.rb +1 -1
  20. data/lib/rudy/cli/status.rb +60 -0
  21. data/lib/rudy/config.rb +42 -14
  22. data/lib/rudy/exceptions.rb +5 -1
  23. data/lib/rudy/global.rb +29 -13
  24. data/lib/rudy/huxtable.rb +2 -2
  25. data/lib/rudy/machines.rb +2 -2
  26. data/lib/rudy/metadata/disk.rb +2 -1
  27. data/lib/rudy/routines.rb +3 -3
  28. data/lib/rudy/routines/base.rb +7 -4
  29. data/lib/rudy/routines/handlers/disks.rb +16 -6
  30. data/lib/rudy/routines/handlers/group.rb +5 -3
  31. data/lib/rudy/routines/handlers/host.rb +14 -16
  32. data/lib/rudy/routines/handlers/script.rb +2 -2
  33. data/lib/rudy/routines/handlers/user.rb +4 -0
  34. data/lib/rudy/routines/reboot.rb +26 -9
  35. data/lib/rudy/routines/shutdown.rb +4 -0
  36. data/lib/rudy/routines/startup.rb +3 -2
  37. data/lib/rudy/utils.rb +23 -9
  38. data/rudy.gemspec +10 -29
  39. data/tryouts/10_require_time/10_rudy_tryouts.rb +1 -1
  40. data/tryouts/{misc/console_tryout.rb → exploration/console.rb} +0 -0
  41. data/tryouts/{misc/usage_tryout.rb → exploration/machine.rb} +0 -0
  42. data/tryouts/failer +1 -1
  43. metadata +8 -70
  44. data/tryouts/misc/disks_tryout.rb +0 -48
  45. data/tryouts/misc/drydock_tryout.rb +0 -48
  46. data/tryouts/misc/nested_methods.rb +0 -103
  47. data/tryouts/misc/session_tryout.rb +0 -46
  48. data/tryouts/misc/tryouts.rb +0 -33
@@ -30,10 +30,16 @@ module Rudy::CLI
30
30
  STDERR.puts ex.backtrace if @@global.verbose > 0
31
31
  exit 81
32
32
  end
33
-
33
+
34
34
  @@global.nocolor ? String.disable_color : String.enable_color
35
- @@global.yes ? Annoy.enable_skip : Annoy.disable_skip
36
-
35
+ @@global.auto ? Annoy.enable_skip : Annoy.disable_skip
36
+
37
+ # ANSI codes look like garbage in DOS
38
+ if Rudy.sysinfo.os.to_s == 'win32'
39
+ String.disable_color
40
+ raise Rudy::Error, 'Ruby 1.9 is not supported (yet)' if Rudy.sysinfo.ruby == [1,9,1]
41
+ end
42
+
37
43
  unless @@global.accesskey && @@global.secretkey
38
44
  STDERR.puts "No AWS credentials. Check your configs!"
39
45
  STDERR.puts "Try: rudy init"
@@ -51,7 +57,7 @@ module Rudy::CLI
51
57
  gcopy.secretkey = "[HIDDEN]"
52
58
  puts "# GLOBALS: ", gcopy.dump(format)
53
59
  end
54
-
60
+
55
61
  Rudy::Metadata.connect @@global.accesskey, @@global.secretkey, @@global.region
56
62
  Rudy::AWS::EC2.connect @@global.accesskey, @@global.secretkey, @@global.region
57
63
  end
@@ -21,7 +21,7 @@ module Rudy
21
21
  #
22
22
  # It will return the most specific configuration available. If the
23
23
  # attribute isn'e found it will check each parent for the same attribute.
24
- # i.e. if [prod][app][ami] is not available, it will check [prod][ami]
24
+ # e.g. if [prod][app][ami] is not available, it will check [prod][ami]
25
25
  # and then [ami].
26
26
  #
27
27
  # # Display all configuration
@@ -61,7 +61,11 @@ module Rudy
61
61
  types.each do |conftype|
62
62
  puts "# #{conftype.to_s.upcase}"
63
63
  next unless @@config[conftype] # Nothing to output
64
- @@config[conftype][:aws][:secretkey] = '[hidden]' if conftype == :accounts
64
+ if conftype == :accounts
65
+ skey = @@config[conftype][:aws][:secretkey]
66
+ @@config[conftype][:aws][:secretkey] = hide_secret_key(skey)
67
+ end
68
+
65
69
  puts @@config[conftype].to_hash.send(outform)
66
70
  end
67
71
  end
@@ -76,10 +80,16 @@ module Rudy
76
80
  end
77
81
  gtmp = @@global.clone
78
82
  gtmp.format = "yaml" if gtmp.format == :s || gtmp.format == :string
79
- gtmp.accesskey = gtmp.secretkey = "[not displayed]"
83
+ gtmp.secretkey = hide_secret_key(gtmp.secretkey)
80
84
  puts gtmp.dump(gtmp.format)
81
85
  end
82
86
 
87
+ private
88
+ def hide_secret_key(skey)
89
+ skey = skey.to_s
90
+ "%s%s%s" % [skey[0], '.'*18, skey[-1]]
91
+ end
92
+
83
93
  end
84
94
  end
85
95
  end
@@ -25,7 +25,7 @@ module Rudy
25
25
  seen << d.name
26
26
  puts @@global.verbose > 0 ? d.inspect : d.dump(@@global.format)
27
27
  if @option.backups
28
- d.list_backups.each_with_index do |b, index|
28
+ d.backups.each_with_index do |b, index|
29
29
  puts ' %s' % b.name
30
30
  ##break if @option.all.nil? && index >= 2 # display only 3, unless all
31
31
  end
@@ -34,10 +34,13 @@ module Rudy::CLI
34
34
  global :c, :cert, String, "AWS Private Certificate (cert-****.pem)"
35
35
  global :f, :format, String, "Output format"
36
36
  global :n, :nocolor, "Disable output colors"
37
- global :C, :config, String, "Specify another configuration file to read (e.g. #{Rudy::CONFIG_FILE})"
38
- global :Y, :yes, "Assume a correct answer to confirmation questions"
37
+ global :Y, :auto, "Skip interactive confirmation"
39
38
  global :q, :quiet, "Run with less output"
40
39
  global :O, :offline, "Be cool about the internet being down"
40
+ global :C, :config, String, "Specify another configuration file to read (e.g. #{Rudy::CONFIG_FILE})" do |val|
41
+ @configs ||= []
42
+ @configs << val
43
+ end
41
44
  global :v, :verbose, "Increase verbosity of output (e.g. -v or -vv or -vvv)" do
42
45
  @verbose ||= 0
43
46
  @verbose += 1
@@ -4,6 +4,7 @@ module Rudy
4
4
  module CLI
5
5
  class Machines < Rudy::CLI::CommandBase
6
6
 
7
+
7
8
  def machines
8
9
  # Rudy::Machines.list takes two optional args for adding or
9
10
  # removing metadata attributes to modify the select query.
@@ -15,15 +16,10 @@ module Rudy
15
16
 
16
17
  mlist = Rudy::Machines.list(fields, less) || []
17
18
  if mlist.empty?
18
- if @option.all
19
- puts "No machines running"
20
- else
21
- puts "No machines running in #{current_machine_group}"
22
- puts "Try: rudy machines --all"
23
- end
19
+ raise( NoMachines, @option.all ? nil : current_group_name)
24
20
  end
25
21
  mlist.each do |m|
26
- puts @@global.verbose > 0 ? m.inspect : m.dump(@@global.format)
22
+ puts @@global.verbose > 0 ? m.to_yaml : "#{m.name}: #{m.dns_public}"
27
23
  end
28
24
  end
29
25
 
@@ -44,6 +40,157 @@ module Rudy
44
40
 
45
41
  end
46
42
 
43
+ def associate_machines_valid?
44
+ @mlist = Rudy::Machines.list || []
45
+ @alist = Rudy::AWS::EC2::Addresses.list || []
46
+ @alist_used = @alist.select { |a| a.associated? }
47
+ @alist_unused = @alist.select { |a| !a.associated? }
48
+ @alist_unused.collect! { |a| a.ipaddress }
49
+ @alist_instids = @alist_used.collect { |a| a.instid }
50
+ @mlist_static = @mlist.select do |m|
51
+ @alist_instids.member?(m.instid)
52
+ end
53
+
54
+ unless @@global.force
55
+ unless @mlist_static.empty?
56
+ msg = "Some machines already have static IP addresses: #{$/}"
57
+ msg << @mlist_static.collect { |m| "#{m.name}: #{m.dns_public}" }.join($/)
58
+ raise Rudy::Error, msg
59
+ end
60
+
61
+ if !@argv.empty? && @mlist.size > @argv.size
62
+ msg = "You supplied #{@argv.size} addresses for #{@mlist.size} "
63
+ msg << "machines. Try: rudy --force machines -S #{@argv.join(' ')}"
64
+ raise Rudy::Error, msg
65
+ end
66
+
67
+ if @alist_unused.size > 0 && @alist_unused.size < @mlist.size
68
+ msg = "There are only #{@alist_unused.size} available addresses for "
69
+ msg << "#{@mlist.size} machines. Try: rudy --force machines -S #{@argv.join(' ')}"
70
+ raise Rudy::Error, msg
71
+ end
72
+ end
73
+
74
+ @argv.each do |address|
75
+ unless Rudy::AWS::EC2::Addresses.exists?(address)
76
+ raise "#{address} is not allocated to you"
77
+ end
78
+ if Rudy::AWS::EC2::Addresses.associated?(address)
79
+ raise "#{address} is already associated!"
80
+ end
81
+ end
82
+
83
+ @alist_unused = @argv unless @argv.empty?
84
+
85
+ true
86
+ end
87
+
88
+ def associate_machines
89
+
90
+ puts "Assigning static IP addresses for:"
91
+ puts @mlist.collect { |m| m.name }
92
+
93
+ execute_check(:medium)
94
+
95
+ @mlist.each do |m|
96
+ next if @mlist_static.member?(m)
97
+ address = @alist_unused.shift
98
+ address ||= Rudy::AWS::EC2::Addresses.create.ipaddress
99
+ puts "Associating #{address} to #{m.name} (#{m.instid})"
100
+ Rudy::AWS::EC2::Addresses.associate(address, m.instid)
101
+ sleep 2
102
+ m.refresh!
103
+ end
104
+
105
+ @alist = Rudy::AWS::EC2::Addresses.list || []
106
+ @alist_used = @alist.select { |a| a.associated? }
107
+ @alist_instids = @alist_used.collect { |a| a.instid }
108
+ @mlist_static = @mlist.select do |m|
109
+ @alist_instids.member?(m.instid)
110
+ end
111
+
112
+ unless @mlist_static.empty?
113
+ @mlist_static.each do |m|
114
+ puts "%s: %s" % [m.name, m.dns_public]
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ def disassociate_machines_valid?
121
+ @mlist = Rudy::Machines.list || []
122
+ @alist = Rudy::AWS::EC2::Addresses.list || []
123
+ @alist_used = @alist.select { |a| a.associated? }
124
+ @alist_instids = @alist_used.collect { |a| a.instid }
125
+ @mlist_static = @mlist.select do |m|
126
+ @alist_instids.member?(m.instid)
127
+ end
128
+ raise NoMachines, current_group_name if @mlist.empty?
129
+ true
130
+ end
131
+
132
+
133
+ def disassociate_machines
134
+ if @mlist_static.empty?
135
+ puts "No machines in #{current_group_name} have static IP addresses"
136
+ else
137
+ puts "The following machines will be updated:"
138
+ puts @mlist_static.collect { |m| m.name }
139
+ puts "NOTE: Unassigned IP addresses are not removed from your account"
140
+ execute_check(:medium)
141
+ @mlist_static.each do |m|
142
+ address = Resolv.getaddress m.dns_public
143
+ puts "Disassociating #{address} from #{m.name} (#{m.instid})"
144
+ Rudy::AWS::EC2::Addresses.disassociate(address)
145
+ end
146
+ end
147
+ end
148
+
149
+ def update_machines
150
+ fields, less = {}, []
151
+ less = Rudy::Metadata::COMMON_FIELDS if @option.all
152
+ mlist = Rudy::Machines.list(fields, less) || []
153
+ rset = Rye::Set.new(current_group_name, :parallel => @@global.parallel, :user => 'root')
154
+ os = current_machine_os
155
+ mlist.each do |m|
156
+ m.refresh!
157
+ rbox = Rye::Box.new(m.dns_public, :user => 'root')
158
+ rbox.add_key user_keypairpath('root')
159
+ rbox.nickname = m.name
160
+ rbox.stash = m
161
+ rset.add_boxes rbox
162
+ puts "Updating metadata"
163
+ if m.os.to_s != os.to_s
164
+ puts "os: #{os}"
165
+ m.os = os
166
+ end
167
+ m.save :replace
168
+ end
169
+
170
+ unless os.to_s == 'win32'
171
+ puts "Updating hostnames for #{current_group_name}"
172
+ Rudy::Routines::Handlers::Host.set_hostname rset
173
+ puts rset.hostname.flatten
174
+ end
175
+
176
+ end
177
+
178
+ def available_machines
179
+ fields, less = {}, []
180
+ less = Rudy::Metadata::COMMON_FIELDS if @option.all
181
+ mlist = Rudy::Machines.list(fields, less) || []
182
+ mlist.each do |m|
183
+ print "#{m.name}: "
184
+ m.refresh!
185
+ Rudy::Utils.waiter(2, 60, STDOUT, nil, 0) {
186
+ Rudy::Utils.service_available?(m.dns_public, 22)
187
+ }
188
+ available = Rudy::Utils.service_available?(m.dns_public, 22)
189
+ puts available ? 'up' : 'down'
190
+ end
191
+
192
+ end
193
+
47
194
 
48
195
  def ssh
49
196
  # TODO: Give this method a good look over
@@ -52,15 +199,16 @@ module Rudy
52
199
  puts "No private key configured for #{current_machine_user} in #{current_machine_group}"
53
200
  end
54
201
 
55
- # Options to be sent to Net::SSH
56
- ssh_opts = { :user => current_machine_user, :debug => nil }
202
+ # Options to be sent to Rye::Box
203
+ rye_opts = { :user => current_machine_user, :debug => nil }
57
204
  if pkey
58
205
  raise "Cannot find file #{pkey}" unless File.exists?(pkey)
59
- raise InsecureKeyPermissions, @pkey unless File.stat(pkey).mode == 33152
60
- ssh_opts[:keys] = pkey
206
+ if Rudy.sysinfo.os != :win32 && File.stat(pkey).mode != 33152
207
+ raise InsecureKeyPermissions, pkey
208
+ end
209
+ rye_opts[:keys] = pkey
61
210
  end
62
-
63
-
211
+
64
212
  # The user specified a command to run. We won't create an interactive
65
213
  # session so we need to prepare the command and its arguments
66
214
  if @argv.first
@@ -71,42 +219,95 @@ module Rudy
71
219
  else
72
220
  command, command_args = :interactive_ssh, @option.print.nil?
73
221
  end
74
-
75
-
222
+
223
+ if command == :interactive_ssh && @global.parallel
224
+ raise "Cannot run interactive sessions in parallel"
225
+ end
226
+
76
227
  checked = false
77
228
  lt = Rudy::Machines.list
78
229
  unless lt
79
230
  puts "No machines running in #{current_machine_group}"
80
- exit
231
+ return
81
232
  end
233
+
234
+ rset = Rye::Set.new(current_machine_group, :parallel => @global.parallel)
82
235
  lt.each do |machine|
83
236
  machine.refresh! # make sure we have the latest DNS info
84
-
85
- # mount -t ext3 /dev/sdr /rudy/disk1
86
-
87
- # Print header
88
- if @@global.quiet
89
- print "You are #{ssh_opts[:user].to_s.bright}. " if !checked # only the 1st
237
+ rbox = Rye::Box.new(machine.dns_public, rye_opts)
238
+ rbox.nickname = machine.name
239
+ if command == :interactive_ssh
240
+ # Print header
241
+ if @@global.quiet
242
+ print "You are #{rye_opts[:user].to_s.bright}. " if !checked # only the 1st
243
+ else
244
+ puts machine_separator(machine.name, machine.instid)
245
+ puts "Connecting #{rye_opts[:user].to_s.bright}@#{machine.dns_public} "
246
+ puts
247
+ end
90
248
  else
91
- puts machine_separator(machine.name, machine.instid)
92
- puts "Connecting #{ssh_opts[:user].to_s.bright}@#{machine.dns_public} "
93
- puts
249
+ unless @global.parallel
250
+ rbox.pre_command_hook do |cmd,user,host,nickname|
251
+ print_command user, nickname, cmd
252
+ end
253
+ end
254
+ rbox.post_command_hook do |ret|
255
+ print_response ret
256
+ end
94
257
  end
95
258
 
96
259
  # Make sure we want to run this command on all instances
97
260
  if !checked && command != :interactive_ssh
98
- execute_check(:low) if ssh_opts[:user] == "root"
261
+ execute_check(:low) if rye_opts[:user] == "root"
99
262
  checked = true
100
263
  end
101
264
 
102
- # Open the connection and run the command
103
- rbox = Rye::Box.new(machine.dns_public, ssh_opts)
104
- ret = rbox.send(command, command_args)
105
- puts ret unless command == :interactive_ssh
265
+ # Open the connection and run the command
266
+ if command == :interactive_ssh
267
+ rbox.send(command, command_args)
268
+ else
269
+ rset.add_box rbox
270
+ end
106
271
  end
272
+
273
+ rset.send(command, command_args) unless command == :interactive_ssh
274
+
107
275
  end
108
276
 
277
+
278
+ private
279
+ # Returns a formatted string for printing command info
280
+ def print_command(user, host, cmd)
281
+ #return if @@global.parallel
282
+ cmd ||= ""
283
+ cmd, user = cmd.to_s, user.to_s
284
+ prompt = user == "root" ? "#" : "$"
285
+ li ("%s@%s%s %s" % [user, host, prompt, cmd.bright])
286
+ end
287
+
288
+
289
+ def print_response(rap)
290
+ # Non zero exit codes raise exceptions so
291
+ # the erorrs have already been handled.
292
+ return if rap.exit_code != 0
109
293
 
294
+ if @@global.parallel
295
+ cmd, user = cmd.to_s, user.to_s
296
+ prompt = user == "root" ? "#" : "$"
297
+ li "%s@%s%s %s%s%s" % [rap.box.user, rap.box.nickname, prompt, rap.cmd.bright, $/, rap.stdout.inspect]
298
+ unless rap.stderr.empty?
299
+ le "#{rap.box.nickname}: " << rap.stderr.join("#{rap.box.nickname}: ")
300
+ end
301
+ else
302
+ li ' ' << rap.stdout.join("#{$/} ") if !rap.stdout.empty?
303
+ colour = rap.exit_code != 0 ? :red : :normal
304
+ unless rap.stderr.empty?
305
+ le (" STDERR " << '-'*38).color(colour).bright
306
+ le " " << rap.stderr.join("#{$/} ").color(colour)
307
+ end
308
+ end
309
+ end
310
+
110
311
  end
111
312
  end
112
313
  end
@@ -0,0 +1,34 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Networks < Rudy::CLI::CommandBase
6
+
7
+ def networks
8
+ name = current_group_name
9
+ Rudy::AWS::EC2::Groups.list(name).each do |group|
10
+ puts @@global.verbose > 0 ? group.inspect : group.dump(@@global.format)
11
+ end
12
+ end
13
+
14
+ def update_networks
15
+ Rudy::Routines::Handlers::Group.authorize rescue nil
16
+ end
17
+
18
+ def local_networks
19
+ ea = Rudy::Utils::external_ip_address || ''
20
+ ia = Rudy::Utils::internal_ip_address || ''
21
+ if @global.quiet
22
+ puts ia unless @option.external && !@option.internal
23
+ puts ea unless @option.internal && !@option.external
24
+ else
25
+ puts "%10s: %s" % ['Internal', ia] unless @option.external && !@option.internal
26
+ puts "%10s: %s" % ['External', ea] unless @option.internal && !@option.external
27
+ end
28
+ @global.quiet = true # don't print elapsed time
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -90,7 +90,7 @@ module Rudy; module CLI;
90
90
  true
91
91
  end
92
92
  def shutdown
93
- routine = fetch_routine_config(:shutdown)
93
+ routine = fetch_routine_config(:shutdown) rescue {}
94
94
 
95
95
  puts "All machines in #{current_machine_group} will be shutdown".bright
96
96
  if routine && routine.disks