rudy 0.9.1 → 0.9.2

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 (84) hide show
  1. data/CHANGES.txt +32 -17
  2. data/README.rdoc +74 -40
  3. data/bin/rudy +66 -10
  4. data/bin/rudy-ec2 +3 -1
  5. data/examples/authorize.rb +15 -0
  6. data/examples/gem-test.rb +11 -5
  7. data/examples/solaris.rb +35 -0
  8. data/examples/windows.rb +101 -0
  9. data/lib/rudy.rb +7 -1
  10. data/lib/rudy/aws.rb +2 -2
  11. data/lib/rudy/aws/ec2.rb +29 -22
  12. data/lib/rudy/aws/ec2/group.rb +1 -1
  13. data/lib/rudy/aws/ec2/image.rb +1 -29
  14. data/lib/rudy/aws/ec2/instance.rb +4 -32
  15. data/lib/rudy/aws/ec2/keypair.rb +1 -6
  16. data/lib/rudy/aws/ec2/snapshot.rb +2 -20
  17. data/lib/rudy/aws/ec2/volume.rb +11 -19
  18. data/lib/rudy/aws/ec2/zone.rb +1 -6
  19. data/lib/rudy/aws/sdb.rb +1 -1
  20. data/lib/rudy/cli/aws/ec2/addresses.rb +4 -10
  21. data/lib/rudy/cli/aws/ec2/groups.rb +0 -1
  22. data/lib/rudy/cli/aws/ec2/images.rb +1 -4
  23. data/lib/rudy/cli/aws/ec2/info.rb +63 -0
  24. data/lib/rudy/cli/aws/ec2/instances.rb +3 -5
  25. data/lib/rudy/cli/aws/ec2/keypairs.rb +3 -5
  26. data/lib/rudy/cli/aws/ec2/snapshots.rb +2 -6
  27. data/lib/rudy/cli/aws/ec2/zones.rb +2 -4
  28. data/lib/rudy/cli/backups.rb +20 -9
  29. data/lib/rudy/cli/base.rb +60 -3
  30. data/lib/rudy/cli/candy.rb +1 -1
  31. data/lib/rudy/cli/disks.rb +65 -7
  32. data/lib/rudy/cli/execbase.rb +0 -2
  33. data/lib/rudy/cli/images.rb +97 -0
  34. data/lib/rudy/cli/info.rb +48 -0
  35. data/lib/rudy/cli/keypairs.rb +43 -0
  36. data/lib/rudy/cli/machines.rb +41 -36
  37. data/lib/rudy/cli/networks.rb +68 -0
  38. data/lib/rudy/cli/routines.rb +3 -10
  39. data/lib/rudy/config/objects.rb +0 -1
  40. data/lib/rudy/disks.rb +4 -0
  41. data/lib/rudy/global.rb +1 -1
  42. data/lib/rudy/huxtable.rb +9 -3
  43. data/lib/rudy/machines.rb +1 -1
  44. data/lib/rudy/metadata.rb +4 -1
  45. data/lib/rudy/metadata/backup.rb +2 -2
  46. data/lib/rudy/metadata/disk.rb +7 -4
  47. data/lib/rudy/metadata/machine.rb +66 -2
  48. data/lib/rudy/routines.rb +2 -1
  49. data/lib/rudy/routines/base.rb +4 -157
  50. data/lib/rudy/routines/handlers/base.rb +6 -3
  51. data/lib/rudy/routines/handlers/disks.rb +127 -42
  52. data/lib/rudy/routines/handlers/group.rb +45 -10
  53. data/lib/rudy/routines/handlers/host.rb +16 -10
  54. data/lib/rudy/routines/handlers/keypair.rb +26 -10
  55. data/lib/rudy/routines/handlers/rye.rb +171 -0
  56. data/lib/rudy/routines/handlers/script.rb +2 -1
  57. data/lib/rudy/routines/passthrough.rb +2 -2
  58. data/lib/rudy/routines/reboot.rb +2 -2
  59. data/lib/rudy/routines/shutdown.rb +2 -2
  60. data/lib/rudy/routines/startup.rb +4 -2
  61. data/rudy.gemspec +17 -10
  62. data/tryouts/10_require_time/10_rudy_tryouts.rb +1 -1
  63. data/tryouts/12_config/20_defaults_tryouts.rb +1 -1
  64. data/tryouts/12_config/40_machines_tryouts.rb +1 -1
  65. data/tryouts/15_huxtable/20_user_tryouts.rb +1 -1
  66. data/tryouts/25_ec2/10_keypairs_tryouts.rb +1 -0
  67. data/tryouts/30_metadata/10_include_tryouts.rb +1 -1
  68. data/tryouts/30_metadata/13_object_tryouts.rb +4 -0
  69. data/tryouts/30_metadata/50_disk_tryouts.rb +4 -2
  70. data/tryouts/30_metadata/51_disk_digest_tryouts.rb +1 -1
  71. data/tryouts/30_metadata/53_disk_list_tryouts.rb +2 -1
  72. data/tryouts/30_metadata/56_disk_volume_tryouts.rb +1 -1
  73. data/tryouts/30_metadata/60_backup_tryouts.rb +4 -2
  74. data/tryouts/30_metadata/63_backup_list_tryouts.rb +1 -1
  75. data/tryouts/30_metadata/64_backup_disk_tryouts.rb +3 -1
  76. data/tryouts/30_metadata/66_backup_snapshot_tryouts.rb +1 -1
  77. data/tryouts/30_metadata/70_machine_tryouts.rb +5 -2
  78. data/tryouts/30_metadata/73_machine_list_tryouts.rb +1 -1
  79. data/tryouts/30_metadata/76_machine_instance_tryouts.rb +15 -3
  80. data/tryouts/30_metadata/77_machines_tryouts.rb +1 -1
  81. data/tryouts/40_routines/10_keypair_handler_tryouts.rb +6 -5
  82. data/tryouts/40_routines/11_group_handler_tryouts.rb +1 -1
  83. metadata +14 -7
  84. data/lib/rudy/cli/status.rb +0 -60
@@ -177,7 +177,6 @@ class Rudy::Config
177
177
  chill :xremote
178
178
 
179
179
  forced_hash :network
180
- chill :network
181
180
 
182
181
  # Startup, Shutdown, Reboot routines
183
182
  forced_hash :before_local
@@ -21,5 +21,9 @@ module Rudy
21
21
  Rudy::Disk.from_hash h
22
22
  end
23
23
 
24
+ def exists?(path)
25
+ !get(path).nil?
26
+ end
27
+
24
28
  end
25
29
  end
@@ -70,7 +70,7 @@ module Rudy
70
70
  # value from the defaults config.
71
71
  # WARNING: Don't add bucket either or any machines configuration param
72
72
  # TODO: investigate removing this apply_config method
73
- %w[region zone environment role position
73
+ %w[region zone environment role position bucket
74
74
  localhost nocolor quiet auto force parallel].each do |name|
75
75
  curval, defval = self.send(name), config.defaults.send(name)
76
76
  if curval.nil? && !defval.nil?
@@ -177,8 +177,8 @@ module Rudy
177
177
  end
178
178
 
179
179
  def current_machine_address(position='01')
180
- raise NoConfig unless @@config
181
- raise NoMachinesConfig unless @@config.machines
180
+ #raise NoConfig unless @@config
181
+ #raise NoMachinesConfig unless @@config.machines
182
182
  raise "Position cannot be nil" if position.nil?
183
183
  addresses = [fetch_machine_param(:addresses)].flatten.compact
184
184
  addresses[position.to_i-1]
@@ -275,7 +275,7 @@ module Rudy
275
275
  end
276
276
  disks.each_pair do |path, props|
277
277
  unless disk_defs.has_key?(path)
278
- li "#{path} is not defined. Check your #{action} machines config.".color(:red)
278
+ li "#{path} is not defined. Check your machines config.".color(:red)
279
279
  routine.disks[raction].delete(path)
280
280
  next
281
281
  end
@@ -288,6 +288,12 @@ module Rudy
288
288
  routine
289
289
  end
290
290
 
291
+ def self.generate_rudy_command(name, *args)
292
+ cmd = "rudy "
293
+ cmd << "-C " << @@global.config.join(' -C ') if @@global.config
294
+ "#{cmd} #{name} " << args.join(' ')
295
+ end
296
+
291
297
  # Is +action+ a valid routine for the current machine group?
292
298
  def valid_routine?(action)
293
299
  !fetch_routine_config(action).nil?
@@ -55,7 +55,7 @@ module Rudy
55
55
 
56
56
  def restart
57
57
  group = list
58
- raise MachineGroupNotRunning, current_group_name if group.nil?
58
+ raise MachineGroupNotRunning, current_machine_group if group.nil?
59
59
  group.each do |inst|
60
60
  inst.restart
61
61
  end
@@ -106,11 +106,14 @@ module Rudy
106
106
  criteria
107
107
  end
108
108
 
109
+
110
+ # These methods are common to all plural metadata classes:
111
+ # Rudy::Machines, Rudy::Disks, Rudy::Backups, etc...
112
+ #
109
113
  module ClassMethods
110
114
  extend self
111
115
  extend Rudy::Huxtable
112
116
 
113
- # TODO: MOVE TO Rudy:Disks etc...
114
117
  def list(fields={}, less=[], &block)
115
118
  fields = Rudy::Metadata.build_criteria self::RTYPE, fields, less
116
119
  records_raw, records = Rudy::Metadata.select(fields), []
@@ -67,8 +67,8 @@ module Rudy
67
67
  @second = @created.sec.to_s.rjust(2, '0')
68
68
  end
69
69
 
70
- def to_s(with_titles=true)
71
- "%s; %s" % [self.name, self.to_hash.inspect]
70
+ def to_s(*args)
71
+ [self.name.bright, self.snapid, self.volid, self.size, self.fstype].join '; '
72
72
  end
73
73
 
74
74
  def name
@@ -16,6 +16,9 @@ module Rudy
16
16
  field :size
17
17
  field :fstype
18
18
 
19
+ field :name # Windows, used for label
20
+ field :index # Windows, used for diskpart
21
+
19
22
  #field :backups => Array
20
23
 
21
24
  # Is the associated volume formatted? One of: true, false
@@ -45,7 +48,7 @@ module Rudy
45
48
 
46
49
  opts = {
47
50
  :size => 1,
48
- :device => '/dev/sdh'
51
+ :device => current_machine_os.to_s == 'windows' ? DEFAULT_WINDOWS_DEVICE : DEFAULT_LINUX_DEVICE
49
52
  }.merge opts
50
53
 
51
54
  super Rudy::Disks::RTYPE, opts # Rudy::Metadata#initialize
@@ -68,8 +71,8 @@ module Rudy
68
71
  @mounted = (@mounted == "true") unless @mounted.is_a?(TrueClass)
69
72
  end
70
73
 
71
- def to_s(with_titles=true)
72
- self.name
74
+ def to_s(*args)
75
+ [self.name.bright, self.volid, self.size, self.fstype].join '; '
73
76
  end
74
77
 
75
78
  def name
@@ -83,7 +86,7 @@ module Rudy
83
86
  end
84
87
 
85
88
  def create(size=nil, zone=nil, snapshot=nil)
86
- raise DuplicateRecord, self.name if exists?
89
+ raise DuplicateRecord, self.name if exists? && !@@global.force
87
90
  vol = Rudy::AWS::EC2::Volumes.create(size || @size, zone || @zone, snapshot)
88
91
  #vol = Rudy::AWS::EC2::Volumes.list(:available).first # debugging
89
92
  @volid, @raw = vol.awsid, true
@@ -77,16 +77,55 @@ module Rudy
77
77
 
78
78
  def postprocess
79
79
  @position &&= @position.to_s.rjust(2, '0')
80
+ @os &&= @os.to_sym
80
81
  end
81
82
 
82
83
  def to_s(*args)
83
- self.name
84
+ [self.name.bright, self.instid, self.dns_public].join '; '
84
85
  end
85
86
 
87
+ def rbox
88
+ r = Rudy::Routines::Handlers::RyeTools.create_box self
89
+ end
90
+
91
+ def disks
92
+ Rudy::Disks.list
93
+ end
94
+
95
+
86
96
  def get_instance
87
97
  Rudy::AWS::EC2::Instances.get @instid
88
98
  end
89
99
 
100
+ def get_console
101
+ raise "Instance not running" unless instance_running?
102
+ raw = Rudy::AWS::EC2::Instances.console @instid
103
+ console = Base64.decode64(raw)
104
+ # The linux console can include ANSI escape codes for color,
105
+ # clear screen etc... We strip them out to get rid of the
106
+ # clear specifically. Otherwise the display is messed!
107
+ console &&= console.noansi if console.respond_to? :noansi
108
+ console
109
+ end
110
+
111
+ def get_password
112
+ unless windows?
113
+ raise "Password support is Windows only (this is #{@os})"
114
+ end
115
+ console = get_console
116
+
117
+ raise "Console output not yet available. Please wait." if console.nil?
118
+
119
+ unless console.match(/<Password>(.+)<\/Password>/m)
120
+ # /m, match multiple lines
121
+ raise "Password not yet available. Is this a custom AMI?"
122
+ end
123
+
124
+ encrtypted_text = ($1 || '').strip
125
+ k = Rye::Key.from_file root_keypairpath
126
+ k.decrypt encrtypted_text
127
+ end
128
+
90
129
  def create
91
130
  raise "#{name} is already running" if instance_running?
92
131
 
@@ -103,6 +142,10 @@ module Rudy
103
142
 
104
143
  Rudy::Huxtable.ld "OPTS: #{opts.inspect}"
105
144
 
145
+ #@dns_public = @dns_private = nil
146
+ #inst = Rudy::AWS::EC2::Instances.list(:running).first
147
+ #@instid = inst.awsid
148
+
106
149
  Rudy::AWS::EC2::Instances.create(opts) do |inst|
107
150
  @instid = inst.awsid
108
151
  @created = @started = Time.now
@@ -125,6 +168,7 @@ module Rudy
125
168
  STDERR.puts ex.backtrace if Rudy.debug?
126
169
  end
127
170
  end
171
+
128
172
  self.save
129
173
  self
130
174
  end
@@ -138,9 +182,15 @@ module Rudy
138
182
  Rudy::AWS::EC2::Instances.restart(@instid) if instance_running?
139
183
  end
140
184
 
185
+ def attached_volumes
186
+ volumes = []
187
+ return volumes if @instid.nil?
188
+ Rudy::AWS::EC2::Volumes.list_by_instance( @instid) || []
189
+ end
190
+
141
191
  def refresh!(metadata=true)
142
192
  ## Updating the metadata isn't necessary
143
- ##super if metadata # update metadata
193
+ super() if metadata # update metadata
144
194
  @instance = get_instance
145
195
  if @instance.is_a?(Rudy::AWS::EC2::Instance)
146
196
  @dns_public, @dns_private = @instance.dns_public, @instance.dns_private
@@ -161,6 +211,20 @@ module Rudy
161
211
  d
162
212
  end
163
213
 
214
+ def default_fstype
215
+ windows? ? Rudy::DEFAULT_WINDOWS_FS : Rudy::DEFAULT_LINUX_FS
216
+ end
217
+
218
+ def default_device
219
+ windows? ? Rudy::DEFAULT_WINDOWS_DEVICE : Rudy::DEFAULT_LINUX_DEVICE
220
+ end
221
+
222
+
223
+ def os?(v); @os.to_s == v.to_s; end
224
+ def windows?; os? 'windows'; end
225
+ def linux?; os? 'linux'; end
226
+ def solaris?; os? 'solaris'; end
227
+
164
228
  def dns_public?; !@dns_public.nil? && !@dns_public.empty?; end
165
229
  def dns_private?; !@dns_private.nil? && !@dns_private.empty?; end
166
230
 
@@ -95,9 +95,10 @@ module Rudy
95
95
  end
96
96
 
97
97
  def self.rescue(ret=nil, &bloc_party)
98
+
98
99
  begin
99
100
  ret = bloc_party.call
100
- rescue NameError, ArgumentError, RuntimeError => ex
101
+ rescue NameError, ArgumentError, RuntimeError, Errno::ECONNREFUSED => ex
101
102
  STDERR.puts " #{ex.class}: #{ex.message}".color(:red)
102
103
  STDERR.puts ex.backtrace if Rudy.debug?
103
104
  unless Rudy::Huxtable.global.parallel
@@ -49,7 +49,10 @@ module Rudy; module Routines;
49
49
  end
50
50
 
51
51
  # Share one Rye::Box instance for localhost across all routines
52
- @@lbox = create_rye_box @@global.localhost unless defined?(@@lbox)
52
+ unless defined?(@@lbox)
53
+ host, opts = @@global.localhost, { :user => Rudy.sysinfo.user }
54
+ @@lbox = Rudy::Routines::Handlers::RyeTools.create_box host, opts
55
+ end
53
56
 
54
57
  disable_run if @@global.testrun
55
58
 
@@ -68,162 +71,6 @@ module Rudy; module Routines;
68
71
  def raise_early_exceptions; raise "Please override"; end
69
72
  def execute; raise "Please override"; end
70
73
 
71
- # Create an instance of Rye::Box for +hostname+. +opts+ is
72
- # an optional Hash of options. See Rye::Box.initialize
73
- #
74
- # This method should be used throughout the Rudy::Routines
75
- # namespace rather than creating instances manually b/c it
76
- # applies some fancy pants defaults like command hooks.
77
- def create_rye_box(hostname, opts={})
78
- ld [:hostname, hostname, opts, caller[0]]
79
- opts = {
80
- :info => (@@global.verbose >= 3), # rudy -vvv
81
- :debug => false,
82
- :user => Rudy.sysinfo.user
83
- }.merge opts
84
-
85
- box = Rye::Box.new hostname, opts
86
-
87
-
88
- # We define hooks so we can still print each command and its output
89
- # when running the command blocks. NOTE: We only print this in
90
- # verbosity mode.
91
- if @@global.verbose > 0 && !@@global.parallel
92
- # This block gets called for every command method call.
93
- box.pre_command_hook do |cmd, user, host, nickname|
94
- print_command user, nickname, cmd
95
- end
96
- end
97
-
98
- if @@global.verbose > 1
99
- # And this one gets called after each command method call.
100
- box.post_command_hook do |ret|
101
- print_response ret
102
- end
103
- end
104
-
105
- box.exception_hook(Rye::CommandError, &rbox_exception_handler)
106
- box.exception_hook(Exception, &rbox_exception_handler)
107
-
108
- ## It'd better for unknown commands to be handled elsewhere
109
- ## because it doesn't make sense to retry a method that doesn't exist
110
- ##box.exception_hook(Rye::CommandNotFound, &rbox_exception_handler)
111
-
112
- box
113
- end
114
-
115
-
116
-
117
- # Create an instance of Rye::Set from a list of +hostnames+.
118
- # +hostnames+ can contain hostnames or Rudy::Machine objects.
119
- # +opts+ is an optional Hash of options. See Rye::Box.initialize
120
- #
121
- # NOTE: Windows machines are skipped and not added to the set.
122
- def create_rye_set(hostnames, opts={})
123
- hostnames ||= []
124
-
125
- opts = {
126
- :user => (current_machine_user).to_s,
127
- :parallel => @@global.parallel
128
- }.merge(opts)
129
- set = Rye::Set.new current_machine_group, opts
130
-
131
- opts.delete(:parallel) # Not used by Rye::Box.new
132
-
133
- hostnames.each do |m|
134
- # This is a short-circuit for Windows instances. We don't support
135
- # disks for windows yet and there's no SSH so routines are out of
136
- # the picture too.
137
- next if (m.os || '').to_s == 'win32'
138
-
139
- if m.is_a?(Rudy::Machine)
140
- m.refresh! if m.dns_public.nil? || m.dns_public.empty?
141
- if m.dns_public.nil? || m.dns_public.empty?
142
- ld "Cannot find public DNS for #{m.name} (continuing...)"
143
- ##next
144
- end
145
- ld [:dns_public, m.dns_public, m.instid]
146
- rbox = create_rye_box(m.dns_public, opts)
147
- rbox.stash = m # Store the machine instance in the stash
148
- rbox.nickname = m.name
149
- else
150
- # Otherwise we assume it's a hostname
151
- rbox = create_rye_box(m)
152
- end
153
- rbox.add_key user_keypairpath(opts[:user])
154
- set.add_box rbox
155
- end
156
-
157
- ld "Machines Set: %s" % [set.empty? ? '[empty]' : set.inspect]
158
-
159
- set
160
- end
161
-
162
-
163
-
164
-
165
- # Returns a formatted string for printing command info
166
- def print_command(user, host, cmd)
167
- #return if @@global.parallel
168
- cmd ||= ""
169
- cmd, user = cmd.to_s, user.to_s
170
- prompt = user == "root" ? "#" : "$"
171
- li ("%s@%s%s %s" % [user, host, prompt, cmd.bright])
172
- end
173
-
174
- def print_response(rap)
175
- # Non zero exit codes raise exceptions so
176
- # the erorrs have already been handled.
177
- return if rap.exit_code != 0
178
-
179
- if @@global.parallel
180
- cmd, user = cmd.to_s, user.to_s
181
- prompt = user == "root" ? "#" : "$"
182
- li "%s@%s%s %s%s%s" % [rap.box.user, rap.box.nickname, prompt, rap.cmd.bright, $/, rap.stdout.inspect]
183
- unless rap.stderr.empty?
184
- le "#{rap.box.nickname}: " << rap.stderr.join("#{rap.box.nickname}: ")
185
- end
186
- else
187
- li ' ' << rap.stdout.join("#{$/} ") if !rap.stdout.empty?
188
- colour = rap.exit_code != 0 ? :red : :normal
189
- unless rap.stderr.empty?
190
- le (" STDERR " << '-'*38).color(colour).bright
191
- le " " << rap.stderr.join("#{$/} ").color(colour)
192
- end
193
- end
194
- end
195
-
196
- private
197
-
198
- def rbox_exception_handler
199
- Proc.new do |ex, cmd, user, host, nickname|
200
- print_exception(user, host, cmd, nickname, ex)
201
- unless @@global.parallel
202
- choice = Annoy.get_user_input('(S)kip (R)etry (F)orce (A)bort: ', nil, 3600) || ''
203
- if choice.match(/\AS/i)
204
- :skip
205
- elsif choice.match(/\AR/i)
206
- :retry # Tells Rye::Box#run_command to retry
207
- elsif choice.match(/\AF/i)
208
- @@global.force = true
209
- :retry
210
- else
211
- exit 12
212
- end
213
- end
214
- end
215
- end
216
-
217
- def print_exception(user, host, cmd, nickname, ex)
218
- prefix = @@global.parallel ? "#{nickname}: #{cmd}: " : ""
219
- if ex.is_a?(Rye::CommandError)
220
- le prefix << ex.message.color(:red)
221
- else
222
- le prefix << "#{ex.class}: #{ex.message}".color(:red)
223
- end
224
- le *ex.backtrace if @@global.verbose > 2
225
- end
226
-
227
74
  end
228
75
 
229
76
  end; end;
@@ -12,11 +12,14 @@ module Rudy; module Routines; module Handlers;
12
12
  print_response(ret)
13
13
  rescue IOError => ex
14
14
  STDERR.puts " Connection Error (#{ex.message})".color(:red)
15
- choice = Annoy.get_user_input('(S)kip (A)bort: ', nil, 3600) || ''
15
+ choice = Annoy.get_user_input('(S)kip (R)etry (F)orce (A)bort: ', nil, 3600) || ''
16
16
  if choice.match(/\AS/i)
17
17
  return
18
- #elsif choice.match(/\AR/i)
19
- # retry
18
+ elsif choice.match(/\AR/i)
19
+ retry
20
+ elsif choice.match(/\AF/i)
21
+ @@global.force = true
22
+ retry
20
23
  else
21
24
  exit 12
22
25
  end