solutious-rudy 0.9.0 → 0.9.1

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