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,434 @@
1
+
2
+
3
+
4
+ module Rudy::AWS
5
+ class EC2::Instance < Storable
6
+ @@sformat = " -> %10s; %10s; %12s; %10s; groups: %s"
7
+ field :aki
8
+ field :ari
9
+ field :launch_index => Time
10
+ field :launch_time
11
+ field :keyname
12
+ field :size
13
+ field :ami
14
+ field :dns_private
15
+ field :dns_public
16
+ field :awsid
17
+ field :state
18
+ field :zone
19
+ field :reason
20
+ field :groups => Array
21
+
22
+ # Groups aren't returned when creating an instance so this
23
+ # method returns an empty Array if +@groups+ is not set.
24
+ def groups
25
+ @groups || []
26
+ end
27
+
28
+ def liner_note
29
+ info = self.running? ? self.dns_public : self.state
30
+ "%s %s" % [self.awsid.bright, info]
31
+ end
32
+
33
+ def to_s(with_title=false)
34
+ lines = []
35
+ lines << "%s (%s)" % [liner_note, @groups.join(', ')]
36
+ #if self.running?
37
+ # k, g = @keyname || 'no-keypair', self.groups.join(', ')
38
+ # lines << @@sformat % %w{zone size ami keyname groups} if with_title
39
+ # lines << @@sformat % [@zone, @size, @ami, k, g]
40
+ #end
41
+ lines.join($/)
42
+ end
43
+
44
+ def inspect
45
+ lines = []
46
+ lines << liner_note
47
+ field_names.each do |key|
48
+ next unless self.respond_to?(key)
49
+ val = self.send(key)
50
+ lines << sprintf(" %22s: %s", key, (val.is_a?(Array) ? val.join(', ') : val))
51
+ end
52
+ lines.join($/)
53
+ end
54
+
55
+ def running?; self.state && self.state == 'running'; end
56
+ def pending?; self.state && self.state == 'pending'; end
57
+ def terminated?; self.state && self.state == 'terminated'; end
58
+ def degraded?; self.state && self.state == 'degraded'; end
59
+ def shutting_down?; self.state && self.state == 'shutting-down'; end
60
+
61
+ end
62
+
63
+
64
+ module EC2
65
+ class Instances
66
+ include Rudy::AWS::ObjectBase
67
+ include Rudy::AWS::EC2::Base
68
+
69
+ unless defined?(KNOWN_STATES)
70
+ KNOWN_STATES = [:running, :pending, :shutting_down, :terminated, :degraded].freeze
71
+ end
72
+
73
+ # Return an Array of Instance objects. Note: These objects will not have
74
+ # DNS data because they will still be in pending state. The DNS info becomes
75
+ # available once the instance enters the running state.
76
+ #
77
+ # +opts+ supports the following parameters:
78
+ #
79
+ # * +:ami+
80
+ # * +:group+
81
+ # * +:size+
82
+ # * +:keypair+
83
+ # * +:address+
84
+ # * +:private+ true or false (default)
85
+ # * +:machine_data+
86
+ # * +:min+ count
87
+ # * +:max+ count
88
+ #
89
+ def create(opts={}, &each_inst)
90
+ raise NoAMI unless opts[:ami]
91
+ raise NoGroup unless opts[:group]
92
+
93
+ opts = {
94
+ :size => 'm1.small',
95
+ :min => 1,
96
+ :max => nil
97
+ }.merge(opts)
98
+
99
+ old_opts = {
100
+ :image_id => opts[:ami].to_s,
101
+ :min_count => opts[:min],
102
+ :max_count => opts[:max] || opts[:min],
103
+ :key_name => (opts[:keypair] || '').to_s,
104
+ :group_id => [opts[:group]].flatten.compact,
105
+ #:user_data => opts[:machine_data], # Error: Invalid BASE64 encoding of user data ??
106
+ :availability_zone => opts[:zone].to_s,
107
+ :addressing_type => opts[:private] ? 'private' : 'public',
108
+ :instance_type => opts[:size].to_s,
109
+ :kernel_id => nil
110
+ }
111
+ #p opts[:machine_data]
112
+ #exit
113
+
114
+ response = execute_request({}) { @ec2.run_instances(old_opts) }
115
+ return nil unless response['instancesSet'].is_a?(Hash)
116
+ instances = response['instancesSet']['item'].collect do |inst|
117
+ self.class.from_hash(inst)
118
+ end
119
+ instances.each { |inst|
120
+ each_inst.call(inst)
121
+ } if each_inst
122
+ instances
123
+ end
124
+
125
+ def restart(inst_ids=[], &each_inst)
126
+ instances = list(:running, inst_ids, &each_inst) || []
127
+ raise NoRunningInstances if instances.empty?
128
+ inst_ids = objects_to_instance_ids(inst_ids)
129
+ response = execute_request({}) {
130
+ @ec2.reboot_instances(:instance_id => inst_ids)
131
+ }
132
+ response['return'] == 'true'
133
+ end
134
+
135
+ def destroy(inst_ids=[], &each_inst)
136
+ instances = list(:running, inst_ids, &each_inst) || []
137
+ raise NoRunningInstances if instances.empty?
138
+
139
+ inst_ids = objects_to_instance_ids(inst_ids)
140
+
141
+ response = execute_request({}) {
142
+ @ec2.terminate_instances(:instance_id => inst_ids)
143
+ }
144
+
145
+ #instancesSet:
146
+ # item:
147
+ # - instanceId: i-ebdcb882
148
+ # shutdownState:
149
+ # code: "48"
150
+ # name: terminated
151
+ # previousState:
152
+ # code: "48"
153
+ # name: terminated
154
+
155
+ raise MalformedResponse unless response['instancesSet'].is_a?(Hash)
156
+ instances_shutdown = []
157
+ response['instancesSet']['item'].collect do |inst|
158
+ next unless inst['shutdownState'].is_a?(Hash) && inst['shutdownState']['name'] == 'shutting-down'
159
+ instances_shutdown << inst['instanceId']
160
+ end
161
+ success = instances_shutdown.size == inst_ids.size
162
+ success
163
+ end
164
+
165
+ def restart_group(group, &each_inst)
166
+ instances = list_group(group, :running, &each_inst) || []
167
+ inst_ids = objects_to_instance_ids(instances)
168
+ restart(inst_ids, :skip_check)
169
+ end
170
+
171
+ def destroy_group(group, &each_inst)
172
+ instances = list_group(group, :running, &each_inst) || []
173
+ inst_ids = objects_to_instance_ids(instances)
174
+ destroy(inst_ids, :skip_check)
175
+ end
176
+
177
+ # * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
178
+ # * +inst_ids+ is an Array of instance IDs.
179
+ # Returns an Array of Rudy::AWS::EC2::Instance objects.
180
+ def list(state=nil, inst_ids=[], &each_inst)
181
+ instances = list_as_hash(state, inst_ids, &each_inst)
182
+ instances &&= instances.values
183
+ instances = nil if instances && instances.empty? # Don't return an empty hash
184
+ instances
185
+ end
186
+
187
+ # * +group+ is a security group name.
188
+ # * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
189
+ # * +inst_ids+ is an Array of instance IDs.
190
+ def list_group(group=nil, state=nil, inst_ids=[], &each_inst)
191
+ instances = list_group_as_hash(group, state, inst_ids, &each_inst)
192
+ instances &&= instances.values
193
+ instances = nil if instances && instances.empty? # Don't return an empty hash
194
+ instances
195
+ end
196
+
197
+
198
+ # * +group+ is a security group name.
199
+ # * +state+ is an optional instance state. If specified, must be one of: running (default), pending, terminated.
200
+ # * +inst_ids+ is an Array of instance IDs.
201
+ def list_group_as_hash(group=nil, state=nil, inst_ids=[], &each_inst)
202
+ instances = list_as_hash(state, inst_ids)
203
+ # Remove instances that are not in the specified group
204
+ if instances
205
+ instances = instances.reject { |id,inst| !inst.groups.member?(group) } if group
206
+ instances.each_value { |inst| each_inst.call(inst) } if each_inst
207
+ end
208
+ instances = nil if instances && instances.empty? # Don't return an empty hash
209
+ instances
210
+ end
211
+
212
+ # * +state+ is an optional instance state. If specified, must be
213
+ # one of: running (default), pending, terminated, any
214
+ # * +inst_ids+ is an Array of instance IDs or Rudy::AWS::EC2::Instance objects.
215
+ # Returns a Hash of Rudy::AWS::EC2::Instance objects. The key is the instance ID.
216
+ # * +each_inst+ a block to execute for every instance in the list.
217
+ def list_as_hash(state=nil, inst_ids=[], &each_inst)
218
+ state &&= state.to_sym
219
+ state = nil if state == :any
220
+ raise "Unknown state: #{state}" if state && !Instances.known_state?(state)
221
+ state = :'shutting-down' if state == :shutting_down # EC2 uses a dash
222
+
223
+ # If we got Instance objects, we want just the IDs.
224
+ # This method always returns an Array.
225
+ inst_ids = objects_to_instance_ids(inst_ids)
226
+
227
+ response = execute_request({}) {
228
+ @ec2.describe_instances(:instance_id => inst_ids)
229
+ }
230
+
231
+ # requestId: c16878ac-28e4-4859-9878-ef93af45789c
232
+ # reservationSet:
233
+ # item:
234
+ # - reservationId: r-e493148d
235
+ # groupSet:
236
+ # item:
237
+ # - groupId: default
238
+ # instancesSet:
239
+ # item:
240
+ return nil unless response['reservationSet'].is_a?(Hash) # No instances
241
+
242
+ resids = []
243
+ instances = {}
244
+ response['reservationSet']['item'].each do |res|
245
+ resids << res['reservationId']
246
+ groups = res['groupSet']['item'].collect { |g| g['groupId'] }
247
+ # And each reservation can have 1 or more instances
248
+ next unless res['instancesSet'].is_a?(Hash)
249
+ res['instancesSet']['item'].each do |props|
250
+ inst = Instances.from_hash(props)
251
+ next if state && inst.state != state.to_s
252
+ inst.groups = groups
253
+ #puts "STATE: #{inst.state} #{state}"
254
+ instances[inst.awsid] = inst
255
+ end
256
+ end
257
+
258
+ instances.each_value { |inst| each_inst.call(inst) } if each_inst
259
+
260
+ instances = nil if instances.empty? # Don't return an empty hash
261
+ instances
262
+ end
263
+
264
+ # System console output.
265
+ #
266
+ # * +inst_id+ instance ID (String) or Instance object.
267
+ #
268
+ # NOTE: Amazon sends the console outputs as a Base64 encoded string.
269
+ # This method DOES NOT decode in order to remain compliant with the
270
+ # data formats returned by Amazon.
271
+ #
272
+ # You can decode it like this:
273
+ #
274
+ # require 'base64'
275
+ # Base64.decode64(output)
276
+ #
277
+ def console(inst_id, &each_inst)
278
+ inst_ids = objects_to_instance_ids([inst_id])
279
+ response = execute_request({}) {
280
+ @ec2.get_console_output(:instance_id => inst_ids.first)
281
+ }
282
+ response['output']
283
+ end
284
+
285
+ def attached_volume?(id, device)
286
+ list = volumes(id)
287
+ list.each do |v|
288
+ return true if v.device == device
289
+ end
290
+ false
291
+ end
292
+
293
+ def volumes(id)
294
+ rvol = Rudy::AWS::EC2::Volumes.new
295
+ rvol.ec2 = @ec2
296
+ rvol.list || []
297
+ list.select { |v| v.attached? && v.instid === id }
298
+ end
299
+
300
+ def device_volume(id, device)
301
+ volumes(id).select { |v| v.device === device }
302
+ end
303
+
304
+ # +inst_id+ is an instance ID
305
+ # Returns an Instance object
306
+ def get(inst_id)
307
+ inst_id = inst_id.awsid if inst_id.is_a?(Rudy::AWS::EC2::Instance)
308
+ inst = list(:any, inst_id)
309
+ inst &&= inst.first
310
+ inst
311
+ end
312
+
313
+ def any?(state=:any, inst_ids=[])
314
+ !list(state, inst_ids).nil?
315
+ end
316
+
317
+ def exists?(inst_ids)
318
+ any?(:any, inst_ids)
319
+ end
320
+
321
+ def any_group?(group=nil, state=:any)
322
+ ret = list_group(group, state)
323
+ !ret.nil?
324
+ end
325
+
326
+ def running?(inst_ids)
327
+ compare_instance_lists(list(:running, inst_ids), inst_ids)
328
+ end
329
+ def pending?(inst_ids)
330
+ compare_instance_lists(list(:pending, inst_ids), inst_ids)
331
+ end
332
+ def terminated?(inst_ids)
333
+ compare_instance_lists(list(:terminated, inst_ids), inst_ids)
334
+ end
335
+ def shutting_down?(inst_ids)
336
+ compare_instance_lists(list(:shutting_down, inst_ids), inst_ids)
337
+ end
338
+
339
+ def unavailable?(inst_ids)
340
+ instances = list(:any, inst_ids) || []
341
+ instances.reject! { |inst|
342
+ (inst.state == "shutting-down" ||
343
+ inst.state == "pending" ||
344
+ inst.state == "terminated")
345
+ }
346
+ compare_instance_lists(instances, inst_ids)
347
+ end
348
+
349
+ #
350
+ # +h+ is a hash of instance properties in the format returned
351
+ # by EC2::Base#describe_instances:
352
+ #
353
+ # kernelId: aki-9b00e5f2
354
+ # amiLaunchIndex: "0"
355
+ # keyName: solutious-default
356
+ # launchTime: "2009-03-14T12:48:15.000Z"
357
+ # instanceType: m1.small
358
+ # imageId: ami-0734d36e
359
+ # privateDnsName:
360
+ # reason:
361
+ # placement:
362
+ # availabilityZone: us-east-1b
363
+ # dnsName:
364
+ # instanceId: i-cdaa34a4
365
+ # instanceState:
366
+ # name: pending
367
+ # code: "0"
368
+ #
369
+ # Returns an Instance object.
370
+ def self.from_hash(h)
371
+ inst = Rudy::AWS::EC2::Instance.new
372
+ inst.aki = h['kernelId']
373
+ inst.ami = h['imageId']
374
+ inst.launch_time = h['launchTime']
375
+ inst.keyname = h['keyName']
376
+ inst.launch_index = h['amiLaunchIndex']
377
+ inst.size = h['instanceType']
378
+ inst.dns_private = h['privateDnsName']
379
+ inst.dns_public = h['dnsName']
380
+ inst.reason = h['reason']
381
+ inst.zone = h['placement']['availabilityZone']
382
+ inst.awsid = h['instanceId']
383
+ inst.state = h['instanceState']['name']
384
+ inst
385
+ end
386
+
387
+ # Is +state+ a known EC2 machine instance state? See: KNOWN_STATES
388
+ def self.known_state?(state)
389
+ return false unless state
390
+ state &&= state.to_sym
391
+ state = :shutting_down if state == :'shutting-down'
392
+ KNOWN_STATES.member?(state)
393
+ end
394
+
395
+ private
396
+
397
+
398
+ # Find out whether two lists of instance IDs (or Rudy::AWS::EC2::Instance objects)
399
+ # contain the same instances regardless of order.
400
+ #
401
+ # *+listA+ An Array of instance IDs (Strings) or Rudy::AWS::EC2::Instance objects
402
+ # *+listB+ Another Array of instance IDs (Strings) or Rudy::AWS::EC2::Instance objects
403
+ # Returns true if:
404
+ # * both listA and listB are Arrays
405
+ # * listA and listB contain the same number of items
406
+ # * all items in listA are in listB
407
+ # * all items in listB are in listA
408
+ def compare_instance_lists(listA, listB)
409
+ listA = objects_to_instance_ids(listA)
410
+ listB = objects_to_instance_ids(listB)
411
+ return false if listA.empty? || listB.empty?
412
+ return false unless listA.size == listB.size
413
+ (listA - listB).empty? && (listB - listA).empty?
414
+ end
415
+
416
+ # * +inst_ids+ an Array of instance IDs (Strings) or Instance objects.
417
+ # Note: This method removes nil values and always returns an Array.
418
+ # Returns an Array of instances IDs.
419
+ def objects_to_instance_ids(inst_ids)
420
+ inst_ids = [inst_ids].flatten # Make sure it's an Array
421
+ inst_ids = inst_ids.collect do |inst|
422
+ next if inst.nil? || inst.to_s.empty?
423
+ if !inst.is_a?(Rudy::AWS::EC2::Instance) && !Rudy::Utils.is_id?(:instance, inst)
424
+ raise %Q("#{inst}" is not an instance ID or object)
425
+ end
426
+ inst.is_a?(Rudy::AWS::EC2::Instance) ? inst.awsid : inst
427
+ end
428
+ inst_ids
429
+ end
430
+
431
+ end
432
+ end
433
+ end
434
+
@@ -0,0 +1,104 @@
1
+
2
+ module Rudy::AWS
3
+ module EC2
4
+
5
+ class KeyPair < Storable
6
+
7
+ field :name
8
+ field :fingerprint
9
+ field :private_key
10
+
11
+ def liner_note
12
+ "%-20s %s" % [self.name.bright, self.fingerprint]
13
+ end
14
+
15
+ def to_s(titles=false)
16
+ str = titles ? "%-20s %s#{$/}" % ['name', 'fingerprint'] : ""
17
+ str << liner_note
18
+ end
19
+
20
+ def public_key
21
+ return unless @private_key
22
+ k = Rye::Key.new(@private_key)
23
+ k.public_key.to_ssh2
24
+ end
25
+
26
+ end
27
+
28
+ class KeyPairs
29
+ include Rudy::AWS::ObjectBase
30
+ include Rudy::AWS::EC2::Base
31
+
32
+ def create(name)
33
+ raise "No name provided" unless name
34
+ ret = @ec2.create_keypair(:key_name => name)
35
+ self.class.from_hash(ret)
36
+ end
37
+
38
+ def destroy(name)
39
+ name = name.name if name.is_a?(Rudy::AWS::EC2::KeyPair)
40
+ raise "No name provided" unless name.is_a?(String)
41
+ ret = @ec2.delete_keypair(:key_name => name)
42
+ (ret && ret['return'] == 'true') # BUG? Always returns true
43
+ end
44
+
45
+ def list(*names)
46
+ keypairs = list_as_hash(names)
47
+ keypairs &&= keypairs.values
48
+ keypairs
49
+ end
50
+
51
+ def list_as_hash(*names)
52
+ names = names.flatten
53
+ klist = @ec2.describe_keypairs(:key_name => names)
54
+ return unless klist['keySet'].is_a?(Hash)
55
+ keypairs = {}
56
+ klist['keySet']['item'].each do |oldkp|
57
+ kp = self.class.from_hash(oldkp)
58
+ keypairs[kp.name] = kp
59
+ end
60
+ keypairs
61
+ end
62
+
63
+ def self.from_hash(h)
64
+ # keyName: test-c5g4v3pe
65
+ # keyFingerprint: 65:d0:ce:e7:6a:b0:88:4a:9c:c7:2d:b8:33:0c:fd:3b:c8:0f:0a:3c
66
+ # keyMaterial: |-
67
+ # -----BEGIN RSA PRIVATE KEY-----
68
+ #
69
+ keypair = Rudy::AWS::EC2::KeyPair.new
70
+ keypair.fingerprint = h['keyFingerprint']
71
+ keypair.name = h['keyName']
72
+ keypair.private_key = h['keyMaterial']
73
+ keypair
74
+ end
75
+
76
+ def any?
77
+ keypairs = list || []
78
+ !keypairs.empty?
79
+ end
80
+
81
+ def get(name)
82
+ keypairs = list(name) || []
83
+ return if keypairs.empty?
84
+ keypairs.first
85
+ end
86
+
87
+ def exists?(name)
88
+ return false unless name
89
+ kp = get(name) rescue nil
90
+ !kp.nil?
91
+ end
92
+
93
+ end
94
+
95
+ class Keypairs
96
+ def initialize(*args)
97
+ raise "Oops! The correct class uses a capital 'P': Rudy::AWS::EC2::KeyPairs"
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+
104
+