sabat-rudy 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/CHANGES.txt +188 -0
  2. data/LICENSE.txt +19 -0
  3. data/README.rdoc +118 -0
  4. data/Rakefile +165 -0
  5. data/Rudyfile +184 -0
  6. data/bin/ird +153 -0
  7. data/bin/rudy +232 -0
  8. data/bin/rudy-ec2 +241 -0
  9. data/bin/rudy-s3 +79 -0
  10. data/bin/rudy-sdb +69 -0
  11. data/examples/README.md +10 -0
  12. data/examples/debian-sinatra-passenger/commands.rb +19 -0
  13. data/examples/debian-sinatra-passenger/machines.rb +32 -0
  14. data/examples/debian-sinatra-passenger/routines.rb +30 -0
  15. data/examples/debian-sinatra-thin/commands.rb +17 -0
  16. data/examples/debian-sinatra-thin/machines.rb +35 -0
  17. data/examples/debian-sinatra-thin/routines.rb +72 -0
  18. data/lib/rudy.rb +170 -0
  19. data/lib/rudy/aws.rb +75 -0
  20. data/lib/rudy/aws/ec2.rb +59 -0
  21. data/lib/rudy/aws/ec2/address.rb +157 -0
  22. data/lib/rudy/aws/ec2/group.rb +301 -0
  23. data/lib/rudy/aws/ec2/image.rb +168 -0
  24. data/lib/rudy/aws/ec2/instance.rb +438 -0
  25. data/lib/rudy/aws/ec2/keypair.rb +104 -0
  26. data/lib/rudy/aws/ec2/snapshot.rb +109 -0
  27. data/lib/rudy/aws/ec2/volume.rb +230 -0
  28. data/lib/rudy/aws/ec2/zone.rb +77 -0
  29. data/lib/rudy/aws/s3.rb +60 -0
  30. data/lib/rudy/aws/sdb.rb +312 -0
  31. data/lib/rudy/aws/sdb/error.rb +47 -0
  32. data/lib/rudy/cli.rb +186 -0
  33. data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
  34. data/lib/rudy/cli/aws/ec2/candy.rb +191 -0
  35. data/lib/rudy/cli/aws/ec2/groups.rb +118 -0
  36. data/lib/rudy/cli/aws/ec2/images.rb +185 -0
  37. data/lib/rudy/cli/aws/ec2/instances.rb +222 -0
  38. data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
  39. data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
  40. data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
  41. data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
  42. data/lib/rudy/cli/aws/s3/buckets.rb +49 -0
  43. data/lib/rudy/cli/aws/s3/store.rb +22 -0
  44. data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
  45. data/lib/rudy/cli/candy.rb +19 -0
  46. data/lib/rudy/cli/config.rb +81 -0
  47. data/lib/rudy/cli/disks.rb +58 -0
  48. data/lib/rudy/cli/machines.rb +114 -0
  49. data/lib/rudy/cli/routines.rb +108 -0
  50. data/lib/rudy/config.rb +116 -0
  51. data/lib/rudy/config/objects.rb +148 -0
  52. data/lib/rudy/global.rb +130 -0
  53. data/lib/rudy/guidelines.rb +18 -0
  54. data/lib/rudy/huxtable.rb +373 -0
  55. data/lib/rudy/machines.rb +281 -0
  56. data/lib/rudy/metadata.rb +51 -0
  57. data/lib/rudy/metadata/backup.rb +113 -0
  58. data/lib/rudy/metadata/backups.rb +65 -0
  59. data/lib/rudy/metadata/disk.rb +177 -0
  60. data/lib/rudy/metadata/disks.rb +67 -0
  61. data/lib/rudy/metadata/objectbase.rb +104 -0
  62. data/lib/rudy/mixins.rb +2 -0
  63. data/lib/rudy/mixins/hash.rb +25 -0
  64. data/lib/rudy/routines.rb +318 -0
  65. data/lib/rudy/routines/helper.rb +55 -0
  66. data/lib/rudy/routines/helpers/dependshelper.rb +34 -0
  67. data/lib/rudy/routines/helpers/diskhelper.rb +331 -0
  68. data/lib/rudy/routines/helpers/scmhelper.rb +39 -0
  69. data/lib/rudy/routines/helpers/scripthelper.rb +198 -0
  70. data/lib/rudy/routines/helpers/userhelper.rb +37 -0
  71. data/lib/rudy/routines/passthrough.rb +38 -0
  72. data/lib/rudy/routines/reboot.rb +75 -0
  73. data/lib/rudy/routines/release.rb +50 -0
  74. data/lib/rudy/routines/shutdown.rb +33 -0
  75. data/lib/rudy/routines/startup.rb +36 -0
  76. data/lib/rudy/scm.rb +75 -0
  77. data/lib/rudy/scm/git.rb +217 -0
  78. data/lib/rudy/scm/svn.rb +110 -0
  79. data/lib/rudy/utils.rb +365 -0
  80. data/rudy.gemspec +151 -0
  81. data/support/mailtest +40 -0
  82. data/support/randomize-root-password +45 -0
  83. data/support/rudy-ec2-startup +200 -0
  84. data/support/update-ec2-ami-tools +20 -0
  85. data/test/01_mixins/10_hash_test.rb +25 -0
  86. data/test/10_config/00_setup_test.rb +20 -0
  87. data/test/10_config/30_machines_test.rb +69 -0
  88. data/test/15_scm/00_setup_test.rb +20 -0
  89. data/test/15_scm/20_git_test.rb +61 -0
  90. data/test/20_sdb/00_setup_test.rb +16 -0
  91. data/test/20_sdb/10_domains_test.rb +115 -0
  92. data/test/25_ec2/00_setup_test.rb +29 -0
  93. data/test/25_ec2/10_keypairs_test.rb +41 -0
  94. data/test/25_ec2/20_groups_test.rb +131 -0
  95. data/test/25_ec2/30_addresses_test.rb +38 -0
  96. data/test/25_ec2/40_volumes_test.rb +49 -0
  97. data/test/25_ec2/50_snapshots_test.rb +74 -0
  98. data/test/26_ec2_instances/00_setup_test.rb +28 -0
  99. data/test/26_ec2_instances/10_instances_test.rb +83 -0
  100. data/test/26_ec2_instances/50_images_test.rb +13 -0
  101. data/test/30_sdb_metadata/00_setup_test.rb +21 -0
  102. data/test/30_sdb_metadata/10_disks_test.rb +109 -0
  103. data/test/30_sdb_metadata/20_backups_test.rb +102 -0
  104. data/test/coverage.txt +51 -0
  105. data/test/helper.rb +36 -0
  106. metadata +276 -0
@@ -0,0 +1,55 @@
1
+
2
+
3
+ module Rudy
4
+ module Routines
5
+ module HelperBase
6
+ include Rudy::Huxtable
7
+
8
+ def execute_rbox_command(ret=nil, &command)
9
+ begin
10
+ ret = command.call if command
11
+ return unless ret.is_a?(Rye::Rap)
12
+ puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
13
+ print_response(ret)
14
+ rescue IOError => ex
15
+ STDERR.puts " Connection Error (#{ex.message})".color(:red)
16
+ exit 12 unless keep_going?
17
+ rescue Rye::CommandError => ex
18
+ print_response(ex)
19
+ exit 12 unless keep_going?
20
+ rescue Rye::CommandNotFound => ex
21
+ STDERR.puts " CommandNotFound: #{ex.message}".color(:red)
22
+ STDERR.puts ex.backtrace if Rudy.debug?
23
+ exit 12 unless keep_going?
24
+ end
25
+
26
+ ret
27
+ end
28
+
29
+ def keep_going?
30
+ Annoy.pose_question(" Keep going?\a ", /yes|y|ya|sure|you bet!/i, STDERR)
31
+ end
32
+
33
+ # Returns a formatted string for printing command info
34
+ def command_separator(cmd, user)
35
+ cmd ||= ""
36
+ cmd, user = cmd.to_s, user.to_s
37
+ prompt = user == "root" ? "#" : "$"
38
+ ("%s%s%s %s" % [$/, user, prompt, cmd.bright])
39
+ end
40
+
41
+ private
42
+ def print_response(rap)
43
+ colour = rap.exit_code != 0 ? :red : :normal
44
+ [:stderr].each do |sumpin|
45
+ next if rap.send(sumpin).empty?
46
+ STDERR.puts
47
+ STDERR.puts((" #{sumpin.to_s.upcase} " << '-'*38).color(colour).bright)
48
+ STDERR.puts " " << rap.send(sumpin).join("#{$/} ").color(colour)
49
+ end
50
+ STDERR.puts " Exit code: #{rap.exit_code}".color(colour) if rap.exit_code != 0
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module Rudy; module Routines;
3
+ module DependsHelper
4
+ include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
5
+ extend self
6
+
7
+ def has_depends?(timing, routine)
8
+ (!routine.is_a?(Caesars::Hash) || routine[timing].is_a?(Caesars::Hash))
9
+ end
10
+
11
+ # Returns an Array of the dependent routines for the given +timing+ (before/after)
12
+ def get(timing, routine)
13
+ return if !(routine.is_a?(Caesars::Hash) || routine[timing].is_a?(Caesars::Hash))
14
+
15
+ # This will produce an Array containing the routines to run. The
16
+ # elements are the valid routine names.
17
+ # NOTE: The "timing" elements are removed from the routines hash.
18
+ dependencies = []
19
+ routine[timing].each_pair do |n,v|
20
+ next unless v.nil? # this skips all "script" blocks
21
+ raise "#{timing}: #{n} is not a known routine" unless valid_routine?(n)
22
+ routine[timing].delete(n)
23
+ dependencies << n
24
+ end
25
+
26
+ # We need to return only the keys b/c the values are nil
27
+ dependencies = nil if dependencies.empty?
28
+ dependencies
29
+ end
30
+
31
+
32
+ end
33
+
34
+ end; end
@@ -0,0 +1,331 @@
1
+
2
+
3
+ module Rudy; module Routines;
4
+ module DiskHelper
5
+ include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
6
+ extend self
7
+
8
+ def disks?(routine)
9
+ (routine.is_a?(Caesars::Hash) && routine.disks &&
10
+ routine.disks.is_a?(Caesars::Hash) && !routine.disks.empty?) ? true : false
11
+ end
12
+
13
+ def paths(routine)
14
+ return nil unless disks?(routine)
15
+ routine.disks.values.collect { |d| d.keys }.flatten
16
+ end
17
+
18
+ def execute(routine, machine, rbox)
19
+ return unless routine
20
+ raise "Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
21
+ raise "Not a Rye::Box" unless rbox.is_a?(Rye::Box)
22
+
23
+ @machine = machine
24
+ @rbox = rbox
25
+
26
+ # We need to add mkfs since it's not enabled by default.
27
+ # We add it only to this instance we're using.
28
+ # We give it a funny name so we can delete it.
29
+ def @rbox.rudy_mkfs(*args); cmd('mkfs', args); end
30
+
31
+ unless disks?(routine)
32
+ STDERR.puts "[nothing to do]"
33
+ return
34
+ end
35
+
36
+ modified = []
37
+ routine.disks.each_pair do |action, disks|
38
+ unless DiskHelper.respond_to?(action)
39
+ STDERR.puts %Q(DiskHelper: unknown action "#{action}")
40
+ next
41
+ end
42
+ send(action, disks) # create, copy, destroy, ...
43
+ modified << disks
44
+ end
45
+
46
+ # TODO: remove rudy_mkfs method
47
+
48
+ end
49
+
50
+ def snapshot(disks)
51
+ rdisk = Rudy::Disks.new
52
+ rback = Rudy::Backups.new
53
+
54
+ disks.each_pair do |path, props|
55
+ adisk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
56
+ disk = rdisk.get(adisk.name)
57
+ if disk == nil
58
+ puts "Not found: #{adisk.name}".color(:red)
59
+ return
60
+ end
61
+ back = disk.backup
62
+ puts "Created backup: #{back.name}"
63
+ end
64
+ end
65
+
66
+ def restore(disks)
67
+ rdisk = Rudy::Disks.new
68
+ rback = Rudy::Backups.new
69
+
70
+ disks.each_pair do |path, props|
71
+ disk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
72
+
73
+ olddisk = rdisk.get(disk.name)
74
+ back = nil
75
+ if olddisk && olddisk.exists?
76
+ olddisk.update
77
+ puts "Disk found: #{olddisk.name}. Skipping...".color(:red)
78
+ return
79
+ else
80
+ disk.fstype = props[:fstype] || 'ext3'
81
+ back = (rback.list(nil, nil, props) || []).first
82
+ raise "No backup found" unless back
83
+ puts "Found backup #{back.name} "
84
+ end
85
+
86
+
87
+
88
+ unless disk.exists? # Checks the EBS volume
89
+ msg = "Creating volume from snapshot (#{back.awsid})... "
90
+ disk.create(back.size, @@global.zone, back.awsid)
91
+ Rudy::Utils.waiter(2, 60, STDOUT, msg) {
92
+ disk.available?
93
+ }
94
+ end
95
+
96
+ msg = "Attaching #{disk.awsid} to #{@machine.awsid}... "
97
+ disk.attach(@machine.awsid)
98
+ Rudy::Utils.waiter(2, 10, STDOUT, msg) {
99
+ disk.attached?
100
+ }
101
+
102
+ sleep 2
103
+
104
+ begin
105
+ @rbox.mkdir(:p, disk.path)
106
+
107
+ print "Mounting at #{disk.path}... "
108
+
109
+ ret = @rbox.mount(:t, disk.fstype, disk.device, disk.path)
110
+ print_response ret
111
+ if ret.exit_code > 0
112
+ STDERR.puts "Error creating disk".color(:red)
113
+ return
114
+ else
115
+ puts "done"
116
+ end
117
+ disk.mounted = true
118
+ disk.save
119
+
120
+ rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
121
+ STDERR.puts "Error creating disk".color(:red)
122
+ STDERR.puts ex.message.color(:red)
123
+ rescue Rye::CommandNotFound => ex
124
+ puts " CommandNotFound: #{ex.message}".color(:red)
125
+
126
+ rescue
127
+ STDERR.puts "Error creating disk" .color(:red)
128
+ Rudy::Utils.bug
129
+ end
130
+
131
+ end
132
+ end
133
+
134
+
135
+ def create(disks)
136
+ rdisk = Rudy::Disks.new
137
+
138
+ disks.each_pair do |path, props|
139
+ disk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
140
+ olddisk = rdisk.get(disk.name)
141
+ if olddisk && olddisk.exists?
142
+ olddisk.update
143
+ puts "Disk found: #{olddisk.name}"
144
+ if olddisk.attached?
145
+ puts "In use. Skipping...".color(:red)
146
+ return
147
+ else
148
+ disk = olddisk
149
+ end
150
+ else
151
+ puts "Creating #{disk.name} "
152
+ disk.fstype = props[:fstype] || 'ext3'
153
+ end
154
+
155
+ unless disk.exists? # Checks the EBS volume
156
+ msg = "Creating volume... "
157
+ disk.create
158
+ Rudy::Utils.waiter(2, 60, STDOUT, msg) {
159
+ disk.available?
160
+ }
161
+ end
162
+
163
+ msg = "Attaching #{disk.awsid} to #{@machine.awsid}... "
164
+ disk.attach(@machine.awsid)
165
+ Rudy::Utils.waiter(2, 10, STDOUT, msg) {
166
+ disk.attached?
167
+ }
168
+
169
+ # The device needs some time.
170
+ # Otherwise mkfs returns:
171
+ # "No such file or directory while trying to determine filesystem size"
172
+ sleep 2
173
+
174
+ # TODO: Cleanup. See ScriptHelper
175
+ begin
176
+ if disk.raw == true
177
+ print "Creating #{disk.fstype} filesystem for #{disk.device}... "
178
+ @rbox.rudy_mkfs(:t, disk.fstype, :F, disk.device)
179
+ disk.raw = false
180
+ disk.save
181
+ puts "done"
182
+ end
183
+
184
+ @rbox.mkdir(:p, disk.path)
185
+
186
+ print "Mounting at #{disk.path}... "
187
+
188
+ ret = @rbox.mount(:t, disk.fstype, disk.device, disk.path)
189
+ print_response ret
190
+ if ret.exit_code > 0
191
+ STDERR.puts "Error creating disk".color(:red)
192
+ return
193
+ else
194
+ puts "done"
195
+ end
196
+ disk.mounted = true
197
+ disk.save
198
+
199
+ rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
200
+ STDERR.puts "Error creating disk".color(:red)
201
+ STDERR.puts ex.message.color(:red)
202
+ rescue Rye::CommandNotFound => ex
203
+ puts " CommandNotFound: #{ex.message}".color(:red)
204
+
205
+ rescue
206
+ STDERR.puts "Error creating disk" .color(:red)
207
+ Rudy::Utils.bug
208
+ end
209
+
210
+ end
211
+ end
212
+
213
+ def mount(disks)
214
+ rdisk = Rudy::Disks.new
215
+ disks.each_pair do |path, props|
216
+ adisk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
217
+ disk = rdisk.get(adisk.name)
218
+ if disk == nil
219
+ puts "Not found: #{adisk.name}".color(:red)
220
+ return
221
+ end
222
+
223
+ msg = "Attaching #{disk.awsid} to #{@machine.awsid}... "
224
+ disk.attach(@machine.awsid)
225
+ Rudy::Utils.waiter(2, 10, STDOUT, msg) {
226
+ disk.attached?
227
+ }
228
+
229
+ sleep 2
230
+
231
+ begin
232
+ @rbox.mkdir(:p, disk.path)
233
+
234
+ print "Mounting at #{disk.path}... "
235
+
236
+ ret = @rbox.mount(:t, disk.fstype, disk.device, disk.path)
237
+ print_response ret
238
+ if ret.exit_code > 0
239
+ STDERR.puts "Error creating disk".color(:red)
240
+ return
241
+ else
242
+ puts "done"
243
+ end
244
+ disk.mounted = true
245
+ disk.save
246
+
247
+ rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
248
+ STDERR.puts "Error creating disk".color(:red)
249
+ STDERR.puts ex.message.color(:red)
250
+ rescue Rye::CommandNotFound => ex
251
+ puts " CommandNotFound: #{ex.message}".color(:red)
252
+
253
+ rescue
254
+ STDERR.puts "Error creating disk" .color(:red)
255
+ Rudy::Utils.bug
256
+ end
257
+
258
+ end
259
+ end
260
+
261
+
262
+ def umount(disks)
263
+ rdisk = Rudy::Disks.new
264
+ disks.each_pair do |path, props|
265
+ adisk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
266
+ disk = rdisk.get(adisk.name)
267
+ if disk == nil
268
+ puts "Not found: #{adisk.name}".color(:red)
269
+ return
270
+ end
271
+
272
+ if disk.mounted?
273
+ print "Unmounting #{disk.path}..."
274
+ execute_rbox_command { @rbox.umount(disk.path) }
275
+ puts " done"
276
+ sleep 0.5
277
+ end
278
+
279
+ sleep 2
280
+
281
+ if disk.attached?
282
+ msg = "Detaching #{disk.awsid}..."
283
+ disk.detach
284
+ Rudy::Utils.waiter(2, 60, STDOUT, msg) {
285
+ disk.available?
286
+ }
287
+ sleep 0.5
288
+ end
289
+
290
+
291
+ end
292
+ end
293
+ alias_method :unmount, :umount
294
+
295
+ def destroy(disks)
296
+ rdisk = Rudy::Disks.new
297
+
298
+ disks.each_pair do |path, props|
299
+ adisk = Rudy::MetaData::Disk.new(path, props[:size], props[:device], @machine.position)
300
+ disk = rdisk.get(adisk.name)
301
+ if disk == nil
302
+ puts "Not found: #{adisk.name}".color(:red)
303
+ return
304
+ end
305
+
306
+ puts "Destroying #{disk.name}"
307
+
308
+ if disk.mounted?
309
+ print "Unmounting #{disk.path}..."
310
+ execute_rbox_command { @rbox.umount(disk.path) }
311
+ puts " done"
312
+ sleep 0.5
313
+ end
314
+
315
+ if disk.attached?
316
+ msg = "Detaching #{disk.awsid}..."
317
+ disk.detach
318
+ Rudy::Utils.waiter(2, 60, STDOUT, msg) {
319
+ disk.available?
320
+ }
321
+ sleep 0.5
322
+ end
323
+
324
+ puts "Destroying volume and metadata... "
325
+ disk.destroy
326
+
327
+ end
328
+ end
329
+
330
+ end
331
+ end;end
@@ -0,0 +1,39 @@
1
+
2
+ module Rudy; module Routines;
3
+ module SCMHelper
4
+ include Rudy::Routines::HelperBase
5
+ extend self
6
+
7
+ # Does the routine config contain SCM routines?
8
+ # Raises Rudy::Error if there is malformed configuration.
9
+ def scm?(routine)
10
+ scmnames = SUPPORTED_SCM_NAMES & routine.keys # Find intersections.
11
+ return false if scmnames.empty? # Nothing to do.
12
+ scmnames.each do |scm|
13
+ routine[scm].values.each do |t| # Each SCM should have a
14
+ raise "Bad #{scm} config" if !t.kind_of?(Hash) # Hash config. Otherwise
15
+ end # it's misconfigured.
16
+ end
17
+ true
18
+ end
19
+
20
+ def create_scm_objects(routine)
21
+ return nil unless routine
22
+ scmnames = SUPPORTED_SCM_NAMES & routine.keys
23
+ vlist = []
24
+ # Look for scm config in the routine by checking all known scm types.
25
+ # For each one we'll create an instance of the appropriate SCM class.
26
+ scmnames.each do |scm|
27
+ routine[scm].each_pair do |user,params|
28
+ klass = eval "Rudy::SCM::#{scm.to_s.upcase}"
29
+ params[:user] = user
30
+ scm = klass.new(params)
31
+ scm.raise_early_exceptions # Raises exceptions for obvious problems.
32
+ vlist << scm
33
+ end
34
+ end
35
+ vlist
36
+ end
37
+
38
+ end
39
+ end; end