solutious-rudy 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/CHANGES.txt +8 -9
  2. data/README.rdoc +48 -7
  3. data/Rakefile +102 -7
  4. data/Rudyfile +28 -0
  5. data/bin/ird +162 -0
  6. data/bin/rudy +287 -93
  7. data/lib/annoy.rb +227 -0
  8. data/lib/aws_sdb/service.rb +1 -1
  9. data/lib/console.rb +20 -4
  10. data/lib/escape.rb +305 -0
  11. data/lib/rudy.rb +265 -125
  12. data/lib/rudy/aws.rb +61 -26
  13. data/lib/rudy/aws/ec2.rb +20 -296
  14. data/lib/rudy/aws/ec2/address.rb +121 -0
  15. data/lib/rudy/aws/ec2/group.rb +241 -0
  16. data/lib/rudy/aws/ec2/image.rb +46 -0
  17. data/lib/rudy/aws/ec2/instance.rb +407 -0
  18. data/lib/rudy/aws/ec2/keypair.rb +92 -0
  19. data/lib/rudy/aws/ec2/snapshot.rb +87 -0
  20. data/lib/rudy/aws/ec2/volume.rb +234 -0
  21. data/lib/rudy/aws/simpledb.rb +33 -15
  22. data/lib/rudy/cli.rb +142 -0
  23. data/lib/rudy/cli/addresses.rb +85 -0
  24. data/lib/rudy/cli/backups.rb +175 -0
  25. data/lib/rudy/{command → cli}/config.rb +18 -13
  26. data/lib/rudy/cli/deploy.rb +12 -0
  27. data/lib/rudy/cli/disks.rb +125 -0
  28. data/lib/rudy/cli/domains.rb +17 -0
  29. data/lib/rudy/cli/groups.rb +77 -0
  30. data/lib/rudy/{command → cli}/images.rb +18 -6
  31. data/lib/rudy/cli/instances.rb +142 -0
  32. data/lib/rudy/cli/keypairs.rb +47 -0
  33. data/lib/rudy/cli/manager.rb +51 -0
  34. data/lib/rudy/{command → cli}/release.rb +10 -10
  35. data/lib/rudy/cli/routines.rb +80 -0
  36. data/lib/rudy/cli/volumes.rb +121 -0
  37. data/lib/rudy/command/addresses.rb +62 -39
  38. data/lib/rudy/command/backups.rb +60 -170
  39. data/lib/rudy/command/disks-old.rb +322 -0
  40. data/lib/rudy/command/disks.rb +5 -209
  41. data/lib/rudy/command/domains.rb +34 -0
  42. data/lib/rudy/command/groups.rb +105 -48
  43. data/lib/rudy/command/instances.rb +263 -70
  44. data/lib/rudy/command/keypairs.rb +149 -0
  45. data/lib/rudy/command/manager.rb +65 -0
  46. data/lib/rudy/command/volumes.rb +110 -49
  47. data/lib/rudy/config.rb +90 -70
  48. data/lib/rudy/config/objects.rb +67 -0
  49. data/lib/rudy/huxtable.rb +253 -0
  50. data/lib/rudy/metadata/backup.rb +23 -48
  51. data/lib/rudy/metadata/disk.rb +79 -68
  52. data/lib/rudy/metadata/machine.rb +34 -0
  53. data/lib/rudy/routines.rb +54 -0
  54. data/lib/rudy/routines/disk_handler.rb +190 -0
  55. data/lib/rudy/routines/release.rb +15 -0
  56. data/lib/rudy/routines/script_runner.rb +65 -0
  57. data/lib/rudy/routines/shutdown.rb +42 -0
  58. data/lib/rudy/routines/startup.rb +48 -0
  59. data/lib/rudy/utils.rb +57 -2
  60. data/lib/storable.rb +11 -5
  61. data/lib/sysinfo.rb +274 -0
  62. data/rudy.gemspec +84 -20
  63. data/support/randomize-root-password +45 -0
  64. data/support/rudy-ec2-startup +5 -5
  65. data/support/update-ec2-ami-tools +20 -0
  66. data/test/05_config/00_setup_test.rb +24 -0
  67. data/test/05_config/30_machines_test.rb +69 -0
  68. data/test/20_sdb/00_setup_test.rb +31 -0
  69. data/test/20_sdb/10_domains_test.rb +113 -0
  70. data/test/25_ec2/00_setup_test.rb +34 -0
  71. data/test/25_ec2/10_keypairs_test.rb +33 -0
  72. data/test/25_ec2/20_groups_test.rb +139 -0
  73. data/test/25_ec2/30_addresses_test.rb +35 -0
  74. data/test/25_ec2/40_volumes_test.rb +46 -0
  75. data/test/25_ec2/50_snapshots_test.rb +69 -0
  76. data/test/26_ec2_instances/00_setup_test.rb +33 -0
  77. data/test/26_ec2_instances/10_instances_test.rb +81 -0
  78. data/test/26_ec2_instances/50_images_test.rb +13 -0
  79. data/test/30_sdb_metadata/00_setup_test.rb +28 -0
  80. data/test/30_sdb_metadata/10_disks_test.rb +99 -0
  81. data/test/30_sdb_metadata/20_backups_test.rb +102 -0
  82. data/test/50_commands/00_setup_test.rb +11 -0
  83. data/test/50_commands/10_keypairs_test.rb +79 -0
  84. data/test/50_commands/20_groups_test.rb +77 -0
  85. data/test/50_commands/40_volumes_test.rb +55 -0
  86. data/test/50_commands/50_instances_test.rb +110 -0
  87. data/test/coverage.txt +51 -0
  88. data/test/helper.rb +35 -0
  89. data/tryouts/disks.rb +55 -0
  90. data/tryouts/nested_methods.rb +36 -0
  91. data/tryouts/session_tryout.rb +48 -0
  92. metadata +94 -25
  93. data/bin/rudy-ec2 +0 -108
  94. data/lib/rudy/command/base.rb +0 -839
  95. data/lib/rudy/command/deploy.rb +0 -12
  96. data/lib/rudy/command/environment.rb +0 -74
  97. data/lib/rudy/command/machines.rb +0 -170
  98. data/lib/rudy/command/metadata.rb +0 -41
  99. data/lib/rudy/metadata.rb +0 -26
@@ -0,0 +1,92 @@
1
+ require 'rye'
2
+
3
+ module Rudy::AWS
4
+ class EC2
5
+
6
+ class KeyPair < Storable
7
+ attr_accessor :private_key # not a storable field
8
+
9
+ field :name
10
+ field :fingerprint
11
+
12
+ def to_s
13
+ "%-20s %s" % [self.name, self.fingerprint]
14
+ end
15
+
16
+ def public_key
17
+ return unless @private_key
18
+ k = Rye::Key.new(@private_key)
19
+ k.public_key.to_ssh2
20
+ end
21
+
22
+ end
23
+
24
+ class KeyPairs
25
+ include Rudy::AWS::ObjectBase
26
+
27
+ def create(name)
28
+ raise "No name provided" unless name
29
+ ret = @aws.create_keypair(:key_name => name)
30
+ self.class.from_hash(ret)
31
+ end
32
+
33
+ def destroy(name)
34
+ name = name.name if name.is_a?(Rudy::AWS::EC2::KeyPair)
35
+ raise "No name provided" unless name.is_a?(String)
36
+ ret = @aws.delete_keypair(:key_name => name)
37
+ (ret && ret['return'] == 'true') # BUG? Always returns true
38
+ end
39
+
40
+ def list(*names)
41
+ keypairs = list_as_hash(names)
42
+ keypairs &&= keypairs.values
43
+ keypairs
44
+ end
45
+
46
+ def list_as_hash(*names)
47
+ names = names.flatten
48
+ klist = @aws.describe_keypairs(:key_name => names)
49
+ return unless klist['keySet'].is_a?(Hash)
50
+ keypairs = {}
51
+ klist['keySet']['item'].each do |oldkp|
52
+ kp = self.class.from_hash(oldkp)
53
+ keypairs[kp.name] = kp
54
+ end
55
+ keypairs
56
+ end
57
+
58
+ def self.from_hash(h)
59
+ # keyName: test-c5g4v3pe
60
+ # keyFingerprint: 65:d0:ce:e7:6a:b0:88:4a:9c:c7:2d:b8:33:0c:fd:3b:c8:0f:0a:3c
61
+ # keyMaterial: |-
62
+ # -----BEGIN RSA PRIVATE KEY-----
63
+ #
64
+ keypair = Rudy::AWS::EC2::KeyPair.new
65
+ keypair.fingerprint = h['keyFingerprint']
66
+ keypair.name = h['keyName']
67
+ keypair.private_key = h['keyMaterial']
68
+ keypair
69
+ end
70
+
71
+ def any?
72
+ keypairs = list || []
73
+ !keypairs.empty?
74
+ end
75
+
76
+ def get(name)
77
+ keypairs = list(name) || []
78
+ return if keypairs.empty?
79
+ keypairs.first
80
+ end
81
+
82
+ def exists?(name)
83
+ begin
84
+ kp = get(name)
85
+ kp.is_a?(Rudy::AWS::EC2::KeyPair)
86
+ rescue => ex
87
+ false
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,87 @@
1
+
2
+
3
+ module Rudy::AWS
4
+ class EC2
5
+ class Snapshot < Storable
6
+ field :awsid
7
+ field :progress
8
+ field :created
9
+ field :volid
10
+ field :status
11
+
12
+ def to_s
13
+ "%s:%s (%s) %s" [self.awsid, self.volid, self.created, self.status]
14
+ end
15
+
16
+ def completed?
17
+ self.status && self.status == 'completed'
18
+ end
19
+
20
+ end
21
+
22
+ class Snapshots
23
+ include Rudy::AWS::ObjectBase
24
+
25
+
26
+ def list(*snap_id)
27
+ snapshots = list_as_hash(snap_id)
28
+ snapshots &&= snapshots.values
29
+ snapshots
30
+ end
31
+ def list_as_hash(*snap_id)
32
+ snap_id = snap_id.flatten
33
+ slist = @aws.describe_snapshots(:snapshot_id => snap_id)
34
+ return unless slist['snapshotSet'].is_a?(Hash)
35
+ snapshots = {}
36
+ slist['snapshotSet']['item'].each do |snap|
37
+ kp = self.class.from_hash(snap)
38
+ snapshots[kp.awsid] = kp
39
+ end
40
+ snapshots
41
+ end
42
+
43
+ def create(vol_id)
44
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
45
+ snap = @aws.create_snapshot(:volume_id => vol_id)
46
+ self.class.from_hash(snap)
47
+ end
48
+
49
+ def destroy(snap_id)
50
+ ret = @aws.delete_snapshot(:snapshot_id => snap_id)
51
+ (ret && ret['return'] == 'true')
52
+ end
53
+
54
+
55
+ def Snapshots.from_hash(h)
56
+ #snapshotSet:
57
+ # item:
58
+ # - snapshotId: snap-5493653d
59
+ # volumeId: vol-0836d761
60
+ # status: completed
61
+ # startTime: "2009-03-29T20:15:10.000Z"
62
+ # progress: 100%
63
+ vol = Rudy::AWS::EC2::Snapshot.new
64
+ vol.awsid = h['snapshotId']
65
+ vol.volid = h['volumeId']
66
+ vol.created = h['startTime']
67
+ vol.progress = h['progress']
68
+ vol.status = h['status']
69
+ vol
70
+ end
71
+
72
+ def any?
73
+ !(list_as_hash || {}).empty?
74
+ end
75
+
76
+ def get(snap_id)
77
+ list(snap_id).first || nil
78
+ end
79
+
80
+ def exists?(id)
81
+ !get(snap_id).nil?
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,234 @@
1
+
2
+
3
+ module Rudy::AWS
4
+ class EC2
5
+ class Volume < Storable
6
+ field :awsid
7
+ field :status
8
+ field :size
9
+ field :snapid
10
+ field :zone
11
+ field :create_time
12
+ field :attach_time
13
+ field :instid
14
+ field :device
15
+
16
+ def to_s
17
+ lines = ["Volume: #{self.awsid.bright}"]
18
+ field_names.each do |n|
19
+ lines << sprintf(" %12s: %s", n, self.send(n)) if self.send(n)
20
+ end
21
+ lines.join($/)
22
+ end
23
+
24
+ # Alias for status
25
+ def state
26
+ status
27
+ end
28
+
29
+ def available?
30
+ (status && status == "available")
31
+ end
32
+
33
+ def creating?
34
+ (status && status == "creating")
35
+ end
36
+
37
+ def deleting?
38
+ (status && status == "deleting")
39
+ end
40
+
41
+ def attached?
42
+ (status && (status == "attached"))
43
+ end
44
+
45
+ def in_use?
46
+ (status && (status == "in-use"))
47
+ end
48
+
49
+ end
50
+
51
+
52
+ class EC2::Volumes
53
+ include Rudy::AWS::ObjectBase
54
+
55
+ unless defined?(KNOWN_STATES)
56
+ KNOWN_STATES = [:available, :creating, :deleting, :attached, :detaching].freeze
57
+ end
58
+
59
+ def attach(inst_id, vol_id, device)
60
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
61
+ inst_id = inst_id.is_a?(Rudy::AWS::EC2::Instance) ? inst_id.awsid : inst_id
62
+
63
+ ret = execute_request(false) {
64
+ @aws.attach_volume(:volume_id => vol_id, :instance_id => inst_id, :device => device)
65
+ }
66
+ (ret['status'] == 'attaching')
67
+ end
68
+
69
+ def detach(vol_id)
70
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
71
+
72
+ ret = execute_request({}) {
73
+ @aws.detach_volume(:volume_id => vol_id)
74
+ }
75
+ (ret['status'] == 'detaching')
76
+ end
77
+
78
+
79
+ def list(state=nil, vol_id=[])
80
+ list_as_hash(state, vol_id).values
81
+ end
82
+
83
+ def list_as_hash(state=nil, vol_id=[])
84
+ state &&= state.to_sym
85
+ state = nil if state == :any
86
+ raise "Unknown state: #{state}" if state && !Volumes.known_state?(state)
87
+
88
+ opts = {
89
+ :volume_id => vol_id ? [vol_id].flatten : []
90
+ }
91
+ begin
92
+
93
+ vlist = execute_request({}) {
94
+ @aws.describe_volumes(opts)
95
+ }
96
+
97
+ # NOTE: The InternalError is returned for non-existent volume IDs.
98
+ # It's probably a bug so we're ignoring it -- Dave.
99
+ rescue ::EC2::InternalError => ex
100
+ vlist = {}
101
+ end
102
+ volumes = {}
103
+ return volumes unless vlist['volumeSet'].is_a?(Hash)
104
+ vlist['volumeSet']['item'].each do |vol|
105
+ v = Volumes.from_hash(vol)
106
+ next if state && v.state != state.to_s
107
+ volumes[v.awsid] = v
108
+ end
109
+ volumes
110
+ end
111
+
112
+
113
+ # * +size+ the number of GB
114
+ def create(size, zone, snapid=nil)
115
+ opts = {
116
+ :availability_zone => zone.to_s,
117
+ :size => (size || 1).to_s
118
+ }
119
+
120
+ opts[:snapshot_id] = snapid if snapid
121
+
122
+ # "status"=>"creating",
123
+ # "size"=>"1",
124
+ # "snapshotId"=>nil,
125
+ # "requestId"=>"d42ff744-48b5-4f47-a3f0-7aba57a13eb9",
126
+ # "availabilityZone"=>"us-east-1b",
127
+ # "createTime"=>"2009-03-17T20:10:48.000Z",
128
+ # "volumeId"=>"vol-48826421"
129
+ vol = execute_request({}) {
130
+ @aws.create_volume(opts)
131
+ }
132
+
133
+ reqid = vol['requestId']
134
+ Volumes.from_hash(vol) || nil
135
+ end
136
+
137
+
138
+ def destroy(vol_id)
139
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
140
+ ret = execute_request({}) {
141
+ @aws.delete_volume(:volume_id => vol_id)
142
+ }
143
+ (ret['return'] == 'true')
144
+ end
145
+
146
+ def self.from_hash(h)
147
+ # ---
148
+ # volumeSet:
149
+ # item:
150
+ # - status: available
151
+ # size: "1"
152
+ # snapshotId:
153
+ # availabilityZone: us-east-1b
154
+ # attachmentSet:
155
+ # createTime: "2009-03-17T20:10:48.000Z"
156
+ # volumeId: vol-48826421
157
+ # attachmentSet:
158
+ # item:
159
+ # - attachTime: "2009-03-17T21:49:54.000Z"
160
+ # status: attached
161
+ # device: /dev/sdh
162
+ # instanceId: i-956af3fc
163
+ # volumeId: vol-48826421
164
+ #
165
+ # requestId: 8fc30e5b-a9c3-4fe0-a979-0f71e639a7c7
166
+ vol = Rudy::AWS::EC2::Volume.new
167
+ vol.status = h['status']
168
+ vol.size = h['size']
169
+ vol.snapid = h['snapshotId']
170
+ vol.zone = h['availabilityZone']
171
+ vol.awsid = h['volumeId']
172
+ vol.create_time = h['createTime']
173
+ if h['attachmentSet'].is_a?(Hash)
174
+ item = h['attachmentSet']['item'].first
175
+ vol.status = item['status'] # Overwrite "available status". Possibly a bad idea.
176
+ vol.device = item['device']
177
+ vol.attach_time = item['attachTime']
178
+ vol.instid = item['instanceId']
179
+ end
180
+ vol
181
+ end
182
+
183
+
184
+ def any?(state=nil,vol_id=[])
185
+ !(list(state, vol_id) || []).empty?
186
+ end
187
+
188
+ def exists?(vol_id)
189
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
190
+ !get(vol_id).nil?
191
+ end
192
+
193
+ def get(vol_id)
194
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
195
+ list(:any, vol_id).first || nil
196
+ end
197
+
198
+ def deleting?(vol_id)
199
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
200
+ return false unless vol_id
201
+ vol = get(vol_id)
202
+ (vol && vol.status == "deleting")
203
+ end
204
+
205
+ def available?(vol_id)
206
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
207
+ return false unless vol_id
208
+ vol = get(vol_id)
209
+ (vol && vol.status == "available")
210
+ end
211
+
212
+ def attached?(vol_id)
213
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
214
+ return false unless vol_id
215
+ vol = get(vol_id)
216
+ (vol && (vol.status == "attached"))
217
+ end
218
+
219
+ def in_use?(vol_id)
220
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
221
+ return false unless vol_id
222
+ vol = get(vol_id)
223
+ (vol && (vol.status == "in-use"))
224
+ end
225
+
226
+ # Is +state+ a known EC2 volume state? See: KNOWN_STATES
227
+ def self.known_state?(state)
228
+ return false unless state
229
+ state &&= state.to_sym
230
+ KNOWN_STATES.member?(state)
231
+ end
232
+ end
233
+ end
234
+ end
@@ -2,52 +2,70 @@
2
2
 
3
3
 
4
4
  module Rudy::AWS
5
-
6
-
7
5
  class SimpleDB
8
6
  class Domains
9
7
  include Rudy::AWS::ObjectBase
10
8
 
11
9
  def create(name)
12
10
  @aws.create_domain(name)
11
+ true
13
12
  end
14
13
 
15
14
  def destroy(name)
16
- @aws.delete_domain(name)
15
+ @aws.delete_domain(name) # Always returns nil, wtf?
16
+ true
17
17
  end
18
18
 
19
19
  def list
20
- @aws.list_domains
20
+ domains = (@aws.list_domains || [[]]) # Nested array, again wtf?
21
+ domains.first.flatten
21
22
  end
22
23
  end
23
24
 
24
- def destroy(domain, item, attributes={})
25
- @aws.delete_attributes(domain, item, attributes)
25
+ def destroy(domain, item)
26
+ @aws.delete_attributes(domain, item)
27
+ true
26
28
  end
27
29
 
28
30
  def store(domain, item, attributes={}, replace=false)
31
+ replace &&= true # Accept any value as true
29
32
  @aws.put_attributes(domain, item, attributes, replace)
30
33
  end
31
34
 
32
35
  def query(domain, query=nil, max=nil)
33
- @aws.query(domain, query, max)
36
+ items = @aws.query(domain, query, max)
37
+ return nil if !items || items.empty? || items == [[], ""] # NOTE: wtf, aws-sdb?
38
+ # [["produce", "produce1", "produce2"], ""]
39
+ clean_items = items.first
40
+ clean_items
34
41
  end
35
42
 
36
43
  def query_with_attributes(domain, query, max=nil)
37
- items = {}
38
- query(domain, query)[:items].each do |item|
39
- items[item] = get_attributes(domain, item)[:attributes]
44
+ items = @aws.query_with_attributes(domain, query, max)
45
+ return nil if !items || items.empty? || items == [[], ""] # NOTE: wtf, aws-sdb?
46
+ clean_items = {}
47
+ # aws-sdb returns the following (another nested array -- wtf X 9):
48
+ # [[{"device"=>["/dev/sdh"], "Name"=>"disk-us-east-1b-stella-app-01-stella", "zone"=>["us-east-1b"],
49
+ # "size"=>["1"], "region"=>["us-east-1"], "role"=>["app"], "rtype"=>["disk"], "awsid"=>[""],
50
+ # "environment"=>["stella"], "position"=>["01"], "path"=>["/stella"]}], ""]
51
+ items.first.each do |item|
52
+ clean_items[item.delete('Name')] = item
40
53
  end
41
- items
54
+ clean_items
42
55
  end
43
56
 
44
57
  def select(query)
45
- list = @aws2.select(query) || []
46
- list[0]
58
+ items = @aws.select(query) || []
59
+ return nil if !items || items.empty? || items == [[], ""] # NOTE: wtf, aws-sdb?
60
+ clean_items = {}
61
+ items.first.each do |item|
62
+ clean_items[item.delete('Name')] = item
63
+ end
64
+ clean_items
47
65
  end
48
66
 
49
- def get_attributes(domain, item, attribute=nil)
50
- @aws.get_attributes(domain, item, attribute)
67
+ def get(domain, item)
68
+ @aws.get_attributes(domain, item)
51
69
  end
52
70
  end
53
71
  end