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,49 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module S3;
5
+
6
+ class Buckets < Rudy::CLI::CommandBase
7
+
8
+
9
+ def buckets
10
+ raise "No bucket name supplied" if !@argv.name && @option.list
11
+ s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region)
12
+ unless @option.list
13
+ (s3.list_buckets || []).each do |b|
14
+ puts b.name
15
+ end
16
+ else
17
+ puts "All objects in #{@argv.name}:"
18
+ (s3.list_bucket_objects(@argv.name) || []).each do |o|
19
+ puts o
20
+ end
21
+ end
22
+ end
23
+
24
+ def create_buckets_valid?
25
+ raise "No bucket name supplied" unless @argv.name
26
+ true
27
+ end
28
+ def create_buckets
29
+ s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region)
30
+ s3.create_bucket(@argv.name, @option.location)
31
+ buckets
32
+ end
33
+
34
+ def destroy_buckets_valid?
35
+ raise "No bucket name supplied" unless @argv.name
36
+ true
37
+ end
38
+ def destroy_buckets
39
+ execute_check(:medium)
40
+ s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region)
41
+ s3.destroy_bucket(@argv.name)
42
+ buckets
43
+ end
44
+
45
+
46
+ end
47
+
48
+ end; end
49
+ end; end
@@ -0,0 +1,22 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module S3;
5
+
6
+ class Store < Rudy::CLI::CommandBase
7
+
8
+ def store_valid?
9
+ raise "No path specified" unless @argv.path
10
+ raise "No bucket specified" unless @option.bucket
11
+ true
12
+ end
13
+ def store
14
+ s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region)
15
+ puts "Success: %s" % s3.store(@argv.path, @option.bucket)
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end;end
22
+ end;end
@@ -0,0 +1,41 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module SDB;
5
+
6
+ class Domains < Rudy::CLI::CommandBase
7
+
8
+
9
+ def domains
10
+ sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
11
+ domains = sdb.list_domains
12
+ puts domains
13
+ puts "No domains" if domains.nil? || domains.empty?
14
+ end
15
+
16
+ def domains_create_valid?
17
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
18
+ raise "No name specified" unless @argv.name
19
+ true
20
+ end
21
+ def domains_create
22
+ @sdb.create_domain @argv.name
23
+ execute_check(:low)
24
+ domains
25
+ end
26
+
27
+ def domains_destroy_valid?
28
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
29
+ raise "No name specified" unless @argv.name
30
+ true
31
+ end
32
+ def domains_destroy
33
+ execute_check(:medium)
34
+ @sdb.destroy_domain @argv.name
35
+ domains
36
+ end
37
+
38
+ end
39
+
40
+ end; end
41
+ end; end
@@ -0,0 +1,19 @@
1
+
2
+ module Rudy
3
+ module CLI
4
+ class Candy < Rudy::CLI::CommandBase
5
+
6
+ def open
7
+ rmach = Rudy::Machines.new
8
+ machines = rmach.list
9
+ puts 1
10
+ if machines
11
+ `open http://#{machines.first.dns_public}`
12
+ else
13
+ puts "No machines"
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,81 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Config < Rudy::CLI::CommandBase
6
+
7
+ # We force the CLI::Base#print_header to be quiet
8
+ def print_header
9
+ #@@global.quiet = true
10
+ #super
11
+ end
12
+
13
+
14
+ # Display configuration from the local user data file (~/.rudy/config).
15
+ # This config contains user data which is sent to each EC2 when
16
+ # it's created.
17
+ #
18
+ # The primary purpose of this command is to give other apps a way
19
+ # to check various configuration values. (This is mostly useful for
20
+ # debugging and checking configuration on an instance itself).
21
+ #
22
+ # It will return the most specific configuration available. If the
23
+ # attribute isn'e found it will check each parent for the same attribute.
24
+ # i.e. if [prod][app][ami] is not available, it will check [prod][ami]
25
+ # and then [ami].
26
+ #
27
+ # # Display the value for a specific machine.
28
+ # $ rudy -e prod -r db config param-name
29
+ #
30
+ # # Display all configuration
31
+ # $ rudy config --all
32
+ #
33
+ def config
34
+ # if Rudy.in_situ? # TODO: do something intelligent when running on EC2
35
+
36
+ if @@config.nil? || @@config.empty?
37
+ return if @@global.quiet
38
+ raise Rudy::NoConfig
39
+ end
40
+
41
+ outform = @@global.format == :json ? :to_json : :to_yaml
42
+
43
+ types = @option.marshal_dump.keys & @@config.keys # Intersections only
44
+ types = @@config.keys if @option.all
45
+ types = [:machines] if types.empty?
46
+
47
+ if @option.project
48
+ rf = File.join(RUDY_HOME, 'Rudyfile')
49
+ raise "Cannot find: #{rf}" unless File.exists?(rf)
50
+ puts File.read(rf)
51
+
52
+ elsif @option.script
53
+ conf = fetch_script_config
54
+ puts conf.to_hash.send(outform) if conf
55
+
56
+ else
57
+ puts "# ACCOUNTS: [not displayed]" if types.delete(:accounts)
58
+ types.each do |conftype|
59
+ puts "# #{conftype.to_s.upcase}"
60
+ next unless @@config[conftype] # Nothing to output
61
+ puts @@config[conftype].to_hash.send(outform)
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ def print_global
68
+ # NOTE: This method cannot be called just "global" b/c that conflicts
69
+ # with the global accessor for Drydock::Command classes.
70
+ if @@global.nil?
71
+ raise Rudy::NoGlobal
72
+ end
73
+ gtmp = @@global.clone
74
+ gtmp.format = "yaml" if gtmp.format == :s || gtmp.format == :string
75
+ gtmp.accesskey = gtmp.secretkey = "[not displayed]"
76
+ puts gtmp.dump(gtmp.format)
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,58 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Disks < Rudy::CLI::CommandBase
6
+
7
+
8
+ def disks
9
+ rdisk = Rudy::Disks.new
10
+ rback = Rudy::Backups.new
11
+ more, less = [], []
12
+ less = [:environment, :role] if @option.all
13
+ # We first get the disk metadata
14
+ disk_list = rdisk.list(more, less) || []
15
+ # If there are no disks currently, there could be backups
16
+ # so we grab those to create a list of disks.
17
+ if @option.backups
18
+ backups = rback.list(more, less) || []
19
+ backups.each_with_index do |b, index|
20
+ disk_list << b.disk
21
+ end
22
+ end
23
+ # We go through the list of disks but we'll skip ones we've seen
24
+ seen = []
25
+ disk_list.each do |d|
26
+ next if seen.member?(d.name)
27
+ seen << d.name
28
+ puts @@global.verbose > 0 ? d.inspect : d.dump(@@global.format)
29
+ if @option.backups
30
+ d.list_backups.each_with_index do |b, index|
31
+ puts ' %s' % b.name
32
+ break if @option.all.nil? && index >= 2 # display only 3, unless all
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def disks_wash
39
+ rdisk = Rudy::Disks.new
40
+ dirt = (rdisk.list || [])#.select { |d| d.available? }
41
+ if dirt.empty?
42
+ puts "Nothing to wash in #{rdisk.current_machine_group}"
43
+ return
44
+ end
45
+
46
+ puts "The following disk metadata will be deleted:"
47
+ puts dirt.collect {|d| d.name }
48
+ execute_check(:medium)
49
+
50
+ dirt.each do |d|
51
+ d.destroy(:force)
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,114 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Machines < Rudy::CLI::CommandBase
6
+
7
+
8
+ def machines
9
+ # Rudy::Machines.list takes two optional args for adding or
10
+ # removing metadata attributes to modify the select query.
11
+ # When all is specified we want to find machines in every
12
+ # environment and role to we remove these attributes from
13
+ # the select.
14
+ more, less = nil, nil
15
+ less = [:environment, :role] if @option.all
16
+
17
+ rmach = Rudy::Machines.new
18
+ mlist = rmach.list(more, less) || []
19
+ if mlist.empty?
20
+ if @option.all
21
+ puts "No machines running"
22
+ else
23
+ puts "No machines running in #{current_machine_group}"
24
+ puts "Try: rudy machines --all"
25
+ end
26
+ end
27
+ mlist.each do |m|
28
+ puts @@global.verbose > 0 ? m.inspect : m.dump(@@global.format)
29
+ end
30
+ end
31
+
32
+ def machines_wash
33
+ rmach = Rudy::Machines.new
34
+ dirt = (rmach.list || []).select { |m| !m.running? }
35
+ if dirt.empty?
36
+ puts "Nothing to wash in #{rmach.current_machine_group}"
37
+ return
38
+ end
39
+
40
+ puts "The following machine metadata will be deleted:".bright
41
+ puts dirt.collect {|m| m.name.bright }
42
+ execute_check(:medium)
43
+
44
+ dirt.each do |m|
45
+ m.destroy
46
+ end
47
+
48
+ end
49
+
50
+
51
+ def ssh
52
+ # TODO: Give this methos a good look over
53
+ pkey = user_keypairpath(@@global.user)
54
+ unless pkey
55
+ puts "No private key configured for #{@@global.user} in #{current_machine_group}"
56
+ end
57
+
58
+ # Options to be sent to Net::SSH
59
+ ssh_opts = { :user => @@global.user || Rudy.sysinfo.user, :debug => nil }
60
+ if pkey
61
+ raise "Cannot find file #{pkey}" unless File.exists?(pkey)
62
+ raise InsecureKeyPermissions, @pkey unless File.stat(pkey).mode == 33152
63
+ ssh_opts[:keys] = pkey
64
+ end
65
+
66
+
67
+ # The user specified a command to run. We won't create an interactive
68
+ # session so we need to prepare the command and its arguments
69
+ if @argv.first
70
+ command, command_args = @argv.shift, @argv || []
71
+ puts "#{command} #{command_args.join(' ')}" if @@global.verbose > 1
72
+
73
+ # otherwise, we'll open an ssh session or print command
74
+ else
75
+ command, command_args = :interactive_ssh, @option.print.nil?
76
+ end
77
+
78
+
79
+ checked = false
80
+ rudy = Rudy::Machines.new
81
+ lt = rudy.list
82
+ unless lt
83
+ puts "No machines running in #{rudy.current_machine_group}"
84
+ exit
85
+ end
86
+ lt.each do |machine|
87
+ machine.update # make sure we have the latest DNS info
88
+
89
+ # Print header
90
+ if @@global.quiet
91
+ print "You are #{ssh_opts[:user].to_s.bright}. " if !checked # only the 1st
92
+ else
93
+ puts machine_separator(machine.name, machine.awsid)
94
+ puts "Connecting #{ssh_opts[:user].to_s.bright}@#{machine.dns_public} "
95
+ puts
96
+ end
97
+
98
+ # Make sure we want to run this command on all instances
99
+ if !checked && command != :interactive_ssh
100
+ execute_check(:low) if ssh_opts[:user] == "root"
101
+ checked = true
102
+ end
103
+
104
+ # Open the connection and run the command
105
+ rbox = Rye::Box.new(machine.dns_public, ssh_opts)
106
+ ret = rbox.send(command, command_args)
107
+ puts ret unless command == :interactive_ssh
108
+ end
109
+ end
110
+
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,108 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ class Routines < Rudy::CLI::CommandBase
5
+
6
+ def startup_valid?
7
+ @rr = Rudy::Routines::Startup.new
8
+ @rr.raise_early_exceptions
9
+ true
10
+ end
11
+ def startup
12
+ machines = @rr.execute || []
13
+ puts $/, "The following machines are now available:" unless machines.empty?
14
+ machines.each do |machine|
15
+ puts machine.to_s
16
+ end
17
+ end
18
+
19
+ def reboot_valid?
20
+ @rr = Rudy::Routines::Reboot.new
21
+ @rr.raise_early_exceptions
22
+ true
23
+ end
24
+ def reboot
25
+ machines = @rr.execute
26
+ puts $/, "The following machines have been restarted:"
27
+ machines.each do |machine|
28
+ puts machine.to_s
29
+ end
30
+ end
31
+
32
+ def release_valid?
33
+ @rr = Rudy::Routines::Release.new(@alias) # Important: could be rerelease
34
+ @rr.raise_early_exceptions
35
+ true
36
+ end
37
+ def release
38
+ machines = @rr.execute
39
+
40
+ unless machines.empty?
41
+ puts $/, "The following machines were processed:"
42
+ machines.each do |machine|
43
+ puts machine.to_s
44
+ end
45
+ end
46
+ end
47
+
48
+ def passthrough_valid?
49
+ @rr = Rudy::Routines::Passthrough.new(@alias)
50
+ @rr.raise_early_exceptions
51
+ true
52
+ end
53
+
54
+ # All unknown commands are sent here (using Drydock's trawler).
55
+ # By default, the generic passthrough routine is executed which
56
+ # does nothing other than execute the routine config block that
57
+ # matches +@alias+ (the name used on the command-line). Calling
58
+ #
59
+ # $ rudy unknown
60
+ #
61
+ # would end up here because it's an unknown command. Passthrough
62
+ # then looks for a routine config in the current environment and
63
+ # role called "unknown". If found, it's executed otherwise it'll
64
+ # raise an exception.
65
+ #
66
+ def passthrough
67
+ machines = @rr.execute
68
+
69
+ unless machines.empty?
70
+ puts $/, "The following machines were processed:"
71
+ machines.each do |machine|
72
+ puts @@global.verbose > 0 ? machine.inspect : machine.dump(@@global.format)
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ def shutdown_valid?
79
+ @rr = Rudy::Routines::Shutdown.new
80
+ @rr.raise_early_exceptions
81
+ true
82
+ end
83
+ def shutdown
84
+ routine = fetch_routine_config(:shutdown)
85
+
86
+ puts "All machines in #{current_machine_group} will be shutdown".bright
87
+ if routine && routine.disks
88
+ if routine.disks.destroy
89
+ puts "The following filesystems will be destroyed:".bright
90
+ puts routine.disks.destroy.keys.join($/).bright
91
+ end
92
+ end
93
+
94
+ execute_check :medium
95
+
96
+ machines = @rr.execute
97
+ puts $/, "The following instances have been destroyed:"
98
+ machines.each do |machine|
99
+ puts '%s %s ' % [machine.name.bright, machine.awsid]
100
+ end
101
+
102
+
103
+ end
104
+
105
+
106
+ end
107
+ end; end
108
+