rudy 0.9.1 → 0.9.2

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