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
data/lib/rudy/cli.rb ADDED
@@ -0,0 +1,142 @@
1
+
2
+ require 'drydock'
3
+
4
+ module Rudy
5
+ class UnknownInstance < RuntimeError #:nodoc
6
+ end
7
+ end
8
+
9
+ module Rudy
10
+
11
+ #
12
+ module CLI
13
+ class NoCred < RuntimeError #:nodoc
14
+ end
15
+
16
+ class Base < Drydock::Command
17
+
18
+ attr_reader :config
19
+
20
+ protected
21
+ def init
22
+
23
+ # This is duplicated in Huxtable :[
24
+ String.disable_color if @global.nocolor
25
+ Rudy.enable_quiet if @global.quiet
26
+
27
+ @config = Rudy::Config.new
28
+ @config.look_and_load(@global.config)
29
+
30
+ unless !@config.empty? && @config.accounts? && @config.accounts.aws && @config.accounts.aws.accesskey
31
+ STDERR.puts "No AWS credentials. Check your configs!"
32
+ STDERR.puts "Try: #{$0} init"
33
+ exit 1
34
+ end
35
+
36
+ # EVERYTHING BELOW THIS LINE IS ATROCIOUS
37
+
38
+ # These are here so we can print the machine group shit in the header.
39
+ # The dupilcation annoys me (see Rudy::Huxtable#init_globals) and I'd
40
+ # like to find a cleaner solution.
41
+ if @config.defaults
42
+ @global.region ||= @config.defaults.region
43
+ @global.zone ||= @config.defaults.zone
44
+ @global.environment ||= @config.defaults.environment
45
+ @global.role ||= @config.defaults.role
46
+ @global.position ||= @config.defaults.position
47
+ @global.user ||= @config.defaults.user
48
+ end
49
+
50
+ @global.region ||= DEFAULT_REGION
51
+ @global.zone ||= DEFAULT_ZONE
52
+ @global.environment ||= DEFAULT_ENVIRONMENT
53
+ @global.role ||= DEFAULT_ROLE
54
+ @global.position ||= DEFAULT_POSITION
55
+ @global.user ||= DEFAULT_USER
56
+
57
+
58
+ if @global.verbose > 1
59
+ puts "CONFIGS: ", @config.paths, $/
60
+
61
+ puts "GLOBALS:"
62
+ @global.marshal_dump.each_pair do |n,v|
63
+ puts "#{n}: #{v}"
64
+ end
65
+ ["machines", "routines"].each do |type|
66
+ puts "#{$/*2}#{type.upcase}:"
67
+ val = @config.send(type).find_deferred(@global.environment, @global.role)
68
+ puts val.to_hash.to_yaml if val
69
+ end
70
+ puts
71
+ end
72
+
73
+ if @global.environment =~ /^prod/ && Drydock.debug?
74
+ puts Rudy.banner("PRODUCTION ACCESS IS DISABLED IN DEBUG MODE")
75
+ exit 1
76
+ end
77
+
78
+
79
+ # TODO: enforce home directory permissions
80
+ #if File.exists?(RUDY_CONFIG_DIR)
81
+ # puts "Checking #{check_environment} permissions..."
82
+ #end
83
+
84
+ end
85
+
86
+
87
+ # Print a default header to the screen for every command.
88
+ #
89
+ def print_header
90
+ puts Rudy::CLI.generate_header(@global, @config)
91
+ unless @global.quiet
92
+ puts # a new line
93
+
94
+ if @global.environment == "prod"
95
+ msg = "YOU ARE PLAYING WITH PRODUCTION"
96
+ puts Rudy.banner(msg, :huge, :red), $/
97
+ end
98
+
99
+ puts Rudy.banner("THIS IS EC2"), $/ if Rudy.in_situ?
100
+ end
101
+ end
102
+
103
+
104
+ end
105
+
106
+
107
+
108
+ def self.generate_header(global, config)
109
+ header = StringIO.new
110
+ title = "RUDY v#{Rudy::VERSION}" unless global.quiet
111
+ now_utc = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
112
+ criteria = []
113
+ [:zone, :environment, :role, :position].each do |n|
114
+ val = global.send(n)
115
+ next unless val
116
+ criteria << "#{n.to_s.slice(0,1).att :normal}:#{val.to_s.bright}"
117
+ end
118
+ if config.accounts && config.accounts.aws
119
+ header.puts '%s -- %s -- %s UTC' % [title, config.accounts.aws.name, now_utc] unless global.quiet
120
+ header.puts '[%s]' % criteria.join(" ") unless global.quiet
121
+ end
122
+
123
+ header.rewind
124
+ header.read
125
+ end
126
+
127
+
128
+ end
129
+ end
130
+
131
+ # Require EC2, S3, Simple DB class
132
+ begin
133
+ # TODO: Use autoload
134
+ Dir.glob(File.join(RUDY_LIB, 'rudy', 'cli', "*.rb")).each do |path|
135
+ require path
136
+ end
137
+ rescue LoadError => ex
138
+ puts "Error: #{ex.message}"
139
+ exit 1
140
+ end
141
+
142
+
@@ -0,0 +1,85 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Addresses < Rudy::CLI::Base
6
+
7
+
8
+ def associate_addresses_valid?
9
+ raise "You have not supplied an IP addresses" unless @argv.ipaddress
10
+ raise "You did not supply an instance ID" unless @argv.instanceid
11
+
12
+ @rmach = Rudy::Instances.new(:config => @config, :global => @global)
13
+ @radd = Rudy::Addresses.new(:config => @config, :global => @global)
14
+
15
+ raise "Instance #{@argv.instid} does not exist!" unless @rmach.exists?(@argv.instid)
16
+
17
+ raise "#{@argv.ipaddress} is not allocated to you" unless @radd.exists?(@argv.ipaddress)
18
+ raise "#{@argv.ipaddress} is already associated!" if @radd.associated?(@argv.ipaddress)
19
+
20
+ true
21
+ end
22
+
23
+ def associate_addresses
24
+ puts "Associating #{@argv.address} to #{@inst[:aws_groups]}: #{@inst[:dns_name]}"
25
+
26
+ address = @radd.get(@argv.ipaddress)
27
+ puts address.to_s
28
+
29
+ puts "Done!"
30
+ puts
31
+ end
32
+
33
+ def addresses
34
+ puts "Addresses".bright, $/
35
+
36
+ radd = Rudy::Addresses.new(:config => @config, :global => @global)
37
+ addresses = radd.list || []
38
+
39
+ addresses.each do |address|
40
+ puts address.to_s
41
+ end
42
+
43
+ puts "No Addresses" if addresses.empty?
44
+ end
45
+
46
+
47
+ def addresses_create
48
+ puts "Create Address".bright, $/
49
+ radd = Rudy::Addresses.new(:config => @config, :global => @global)
50
+ address = radd.create
51
+ puts address.to_s
52
+ end
53
+
54
+ def addresses_destroy_valid?
55
+ raise "You have not supplied an IP addresses" unless @argv.ipaddress
56
+
57
+ @radd = Rudy::Addresses.new(:config => @config, :global => @global)
58
+ @rmach = Rudy::Instances.new(:config => @config, :global => @global)
59
+
60
+ raise "#{@argv.ipaddress} is not allocated to you" unless @radd.exists?(@argv.ipaddress)
61
+ raise "#{@argv.ipaddress} is associated!" if @radd.associated?(@argv.ipaddress)
62
+
63
+ true
64
+ end
65
+ def addresses_destroy
66
+ puts "Destroy Address".bright, $/
67
+
68
+ address = @radd.get(@argv.ipaddress)
69
+ raise "Could not fetch #{address.ipaddress}" unless address
70
+
71
+ puts "Destroying address: #{@argv.ipaddress}"
72
+ puts "NOTE: this IP address will become available to other EC2 customers.".color(:blue)
73
+ exit unless Annoy.are_you_sure?(:low)
74
+
75
+ ret = @radd.destroy(@argv.ipaddress)
76
+ raise "Destroy failed" unless ret
77
+
78
+ puts
79
+ puts "Done"
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,175 @@
1
+
2
+
3
+
4
+
5
+ module Rudy
6
+ module CLI
7
+ class Backups < Rudy::CLI::Base
8
+
9
+
10
+ def backup
11
+ criteria = [@global.zone]
12
+ criteria += [@global.environment, @global.role] unless @option.all
13
+
14
+ Rudy::MetaData::Backup.list(@sdb, *criteria).each do |backup|
15
+ puts "%s (%s)" % [backup.name, backup.awsid]
16
+ end
17
+ end
18
+
19
+ # Check for backups pointing to snapshots that don't exist.
20
+ def sync_backup
21
+ unless argv.empty?
22
+ puts "The disk you specified will be ignored."
23
+ argv.clear
24
+ end
25
+
26
+ criteria = [@global.zone]
27
+ criteria += [@global.environment, @global.role] unless @option.all
28
+
29
+ puts "Looking for backup metadata with delinquent snapshots..."
30
+ to_be_deleted = {} # snap-id => backup
31
+ Rudy::MetaData::Backup.list(@sdb, *criteria).each do |backup|
32
+ to_be_deleted[backup.awsid] = backup unless @ec2.snapshots.exists?(backup.awsid)
33
+ end
34
+
35
+ if to_be_deleted.empty?
36
+ puts "All backups are in-sync with snapshots. Nothing to do."
37
+ return
38
+ end
39
+
40
+ puts
41
+ puts "These backup metadata will be deleted:"
42
+ to_be_deleted.each do |snap_id, backup|
43
+ puts "%s: %s" % [snap_id, backup.name]
44
+ end
45
+
46
+ puts
47
+ exit unless Annoy.are_you_sure?
48
+
49
+ puts
50
+ puts "Deleting..."
51
+ to_be_deleted.each do |snap_id, backup|
52
+ print " -> #{backup.name}... "
53
+ @sdb.destroy(RUDY_DOMAIN, backup.name)
54
+ puts "done"
55
+ end
56
+
57
+ puts "Done!"
58
+ end
59
+
60
+ def destroy_backup_valid?
61
+ raise "No backup specified" if argv.empty?
62
+ exit unless Annoy.are_you_sure?(:high)
63
+ true
64
+ end
65
+
66
+ def destroy_backup
67
+ name = @argv.first
68
+ puts "Destroying #{name}"
69
+ begin
70
+ backup = Rudy::MetaData::Backup.get(@sdb, name)
71
+ rescue => ex
72
+ puts "Error deleteing backup: #{ex.message}"
73
+ end
74
+
75
+ return unless backup
76
+
77
+ begin
78
+ puts " -> deleting snapshot..."
79
+ @ec2.snapshots.destroy(backup.awsid)
80
+ rescue => ex
81
+ puts "Error deleting snapshot: #{ex.message}."
82
+ puts "Continuing..."
83
+ ensure
84
+ puts " -> deleting metadata..."
85
+ @sdb.destroy(RUDY_DOMAIN, name)
86
+ end
87
+ puts "Done."
88
+ end
89
+
90
+ def create_backup
91
+ diskname = @argv.first
92
+
93
+ machine = find_current_machine
94
+
95
+ disks = Rudy::MetaData::Disk.list(@sdb, machine[:aws_availability_zone], @global.environment, @global.role, @global.position)
96
+ raise "The machine #{machine_name} does not have any disk metadata" if disks.empty?
97
+
98
+ puts "Machine: #{machine_name}"
99
+
100
+ if @option.snapshot
101
+ raise "You must supply a diskname when using an existing snapshot" unless diskname
102
+ raise "The snapshot #{@option.snapshot} does not exist" unless @ec2.snapshots.exists?(@option.snapshot)
103
+ disk = Rudy::MetaData::Disk.get(@sdb, diskname)
104
+
105
+ raise "The disk #{diskname} does not exist" unless disk
106
+ backup = Rudy::MetaData::Backup.new
107
+ backup.awsid = @option.snapshot
108
+ backup.time_stamp
109
+
110
+ # Populate machine infos
111
+ [:zone, :environment, :role, :position].each do |n|
112
+ backup.send("#{n}=", @global.send(n)) if @global.send(n)
113
+ end
114
+
115
+ # Populate disk infos
116
+ [:path, :size].each do |n|
117
+ backup.send("#{n}=", disk.send(n)) if disk.send(n)
118
+ end
119
+
120
+
121
+ Rudy::MetaData::Backup.save(@sdb, backup)
122
+
123
+ puts backup.name
124
+
125
+ else
126
+ volumes = @ec2.instances.volumes(machine[:aws_instance_id])
127
+ raise "The machine #{machine_name} does not have any volumes attached." if volumes.empty?
128
+
129
+ puts "#{disks.size} Disk(s) defined with #{volumes.size} Volume(s) running"
130
+
131
+ volumes.each do |volume|
132
+ print "Volume #{volume[:aws_id]}... "
133
+ disk = Rudy::MetaData::Disk.find_from_volume(@sdb, volume[:aws_id])
134
+ backup = Rudy::MetaData::Backup.new
135
+
136
+ # TODO: Look for the disk based on the machine
137
+ raise "No disk associated to volume #{volume[:aws_id]}" unless disk
138
+
139
+ backup.volume = volume[:aws_id]
140
+
141
+ # Populate machine infos
142
+ [:zone, :environment, :role, :position].each do |n|
143
+ backup.send("#{n}=", @global.send(n)) if @global.send(n)
144
+ end
145
+
146
+ # Populate disk infos
147
+ [:path, :size].each do |n|
148
+ backup.send("#{n}=", disk.send(n)) if disk.send(n)
149
+ end
150
+
151
+ backup.time_stamp
152
+
153
+ raise "There was a problem creating the backup metadata" unless backup.valid?
154
+
155
+ snap = @ec2.snapshots.create(volume[:aws_id])
156
+
157
+ if !snap || !snap.is_a?(Hash)
158
+ puts "There was an unknown problem creating #{backup.name}. Continuing with the next volume..."
159
+ next
160
+ end
161
+
162
+ backup.awsid = snap[:aws_id]
163
+
164
+ Rudy::MetaData::Backup.save(@sdb, backup)
165
+
166
+ puts backup.name
167
+
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+ end
174
+ end
175
+ end
@@ -1,10 +1,10 @@
1
1
 
2
2
 
3
3
  module Rudy
4
- module Command
5
- class Config < Rudy::Command::Base
4
+ module CLI
5
+ class Config < Rudy::CLI::Base
6
6
 
7
- # We force the Command::Base#print_header to be quiet
7
+ # We force the CLI::Base#print_header to be quiet
8
8
  def print_header
9
9
  @global.quiet = true
10
10
  super
@@ -32,11 +32,9 @@ module Rudy
32
32
  #
33
33
  def config
34
34
  return if @config.nil?
35
- puts "Config: #{@config.paths}" if @global.verbose > 0
36
35
 
37
- which = @option.defaults ? @global.user : machine_name
38
- puts "Machine: #{which}" if @global.verbose > 0
39
- puts "User: #{@global.user}" if @global.verbose > 0
36
+
37
+ @option.group ||= [@global.environment, @global.role].join(RUDY_DELIM)
40
38
 
41
39
  return if @config.empty?
42
40
 
@@ -47,15 +45,22 @@ module Rudy
47
45
 
48
46
 
49
47
  else
50
-
51
48
  if @option.all
52
- y @config.machines.to_hash
53
- y @config.routines.to_hash
54
- elsif @option.defaults
49
+ puts "# ACCOUNTS: not displayed"
50
+ puts "# MACHINES: "
51
+ if @config.machines?
52
+ y @config.machines.to_hash
53
+ end
54
+ if @config.routines?
55
+ puts "# ROUTINES: "
56
+ y @config.routines.to_hash
57
+ end
58
+ elsif @option.defaults?
55
59
  y @config.defaults.to_hash
56
60
  else
57
- env, rol, usr, att = @global.environment, @global.role, @global.user, @argv.name
58
- val = @config.machines.find_deferred(env, rol, usr, att) || ''
61
+ zon, env, rol = @global.zone, @global.environment, @global.role
62
+ usr, att = @global.user, @argv.name
63
+ val = @config.machines.find_deferred(zon, env, rol, usr, att) || ''
59
64
  puts (val.is_a?(String)) ? val : val.to_hash.to_yaml
60
65
  end
61
66