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,105 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module EC2;
5
+
6
+ class Addresses < Rudy::CLI::CommandBase
7
+
8
+ def addresses_create
9
+ radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
10
+ address = radd.create
11
+ puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
12
+ end
13
+
14
+ def addresses_destroy_valid?
15
+ raise Drydock::ArgError.new("IP address", @alias) unless @argv.ipaddress
16
+ @radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
17
+ raise "#{@argv.ipaddress} is not allocated to you" unless @radd.exists?(@argv.ipaddress)
18
+ raise "#{@argv.ipaddress} is associated!" if @radd.associated?(@argv.ipaddress)
19
+ true
20
+ end
21
+ def addresses_destroy
22
+ address = @radd.get(@argv.ipaddress)
23
+ raise "Could not fetch #{address.ipaddress}" unless address
24
+
25
+ puts "Destroying address: #{@argv.ipaddress}"
26
+ puts "NOTE: this IP address will become available to other EC2 customers.".bright
27
+ execute_check(:medium)
28
+ execute_action { @radd.destroy(@argv.ipaddress) }
29
+ self.addresses
30
+ end
31
+
32
+ def associate_addresses_valid?
33
+ raise Drydock::ArgError.new('IP address', @alias) if !@argv.ipaddress && !@option.newaddress
34
+ raise Drydock::OptError.new('instance ID', @alias) if !@option.instance
35
+ true
36
+ end
37
+ def associate_addresses
38
+ radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
39
+ rinst = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
40
+
41
+ raise "Instance #{@argv.instid} does not exist!" unless rinst.exists?(@option.instance)
42
+
43
+ if @option.newaddress
44
+ print "Creating address... "
45
+ tmp = radd.create
46
+ puts "#{tmp.ipaddress}"
47
+ address = tmp.ipaddress
48
+ else
49
+ address = @argv.ipaddress
50
+ end
51
+
52
+ raise "#{address} is not allocated to you" unless radd.exists?(address)
53
+ raise "#{address} is already associated!" if radd.associated?(address)
54
+
55
+ instance = rinst.get(@option.instance)
56
+
57
+ # If an instance was recently disassoiciated, the dns_public may
58
+ # not be updated yet
59
+ instance_name = instance.dns_public
60
+ instance_name = instance.awsid if !instance_name || instance_name.empty?
61
+
62
+ puts "Associating #{address} to #{instance_name} (#{instance.groups.join(', ')})"
63
+ execute_check(:low)
64
+ execute_action { radd.associate(address, instance.awsid) }
65
+ address = radd.get(address)
66
+ puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
67
+ end
68
+
69
+ def disassociate_addresses_valid?
70
+ raise "You have not supplied an IP addresses" unless @argv.ipaddress
71
+ true
72
+ end
73
+ def disassociate_addresses
74
+ radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
75
+ rinst = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
76
+ raise "#{@argv.ipaddress} is not allocated to you" unless radd.exists?(@argv.ipaddress)
77
+ raise "#{@argv.ipaddress} is not associated!" unless radd.associated?(@argv.ipaddress)
78
+
79
+ address = radd.get(@argv.ipaddress)
80
+ instance = rinst.get(address.instid)
81
+
82
+ puts "Disassociating #{address.ipaddress} from #{instance.awsid} (#{instance.groups.join(', ')})"
83
+ execute_check(:medium)
84
+ execute_action { radd.disassociate(@argv.ipaddress) }
85
+ address = radd.get(@argv.ipaddress)
86
+ puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
87
+ end
88
+
89
+ def addresses
90
+ radd = Rudy::AWS::EC2::Addresses.new(@@global.accesskey, @@global.secretkey, @@global.region)
91
+ addresses = radd.list || []
92
+
93
+ addresses.each do |address|
94
+ puts @@global.verbose > 0 ? address.inspect : address.dump(@@global.format)
95
+ end
96
+
97
+ puts "No Addresses" if addresses.empty?
98
+ end
99
+
100
+
101
+ end
102
+
103
+ end; end
104
+ end; end
105
+
@@ -0,0 +1,191 @@
1
+
2
+ module Rudy; module CLI;
3
+ module AWS; module EC2;
4
+
5
+ class Candy < Rudy::CLI::CommandBase
6
+
7
+ def status_valid?
8
+ avail = Rudy::Utils.service_available?('status.aws.amazon.com', 80, 5)
9
+ raise ServiceUnavailable, 'status.aws.amazon.com' unless @@global.offline || avail
10
+ true
11
+ end
12
+ def status
13
+ url = 'http://status.aws.amazon.com/rss/EC2.rss'
14
+
15
+ if (@@global.region || '').to_s.strip.match(/\Aeu/)
16
+ url = 'http://status.aws.amazon.com/rss/EC2EU.rss'
17
+ end
18
+
19
+ # TODO: Move to Rudy::AWS
20
+ ec2 = Rudy::Utils::RSSReader.run(url) || {}
21
+
22
+ # TODO: Create Storable object
23
+ if @@global.format == 'yaml'
24
+ puts ec2.to_yaml
25
+ elsif @@global.format == 'json'
26
+ require 'json'
27
+ puts ec2.to_json
28
+ else
29
+ puts "#{ec2[:title]}"
30
+ puts "Updated: #{ec2[:pubdate]}"
31
+ (ec2[:items] || []).each do |i|
32
+ puts
33
+ puts '%s' % i[:title]
34
+ puts ' %s: %s' % [i[:pubdate], i[:description]]
35
+ end
36
+ if ec2.empty? || ec2[:items].empty?
37
+ puts "No announcements"
38
+ return
39
+ end
40
+ end
41
+
42
+
43
+
44
+ end
45
+
46
+ def ssh_valid?
47
+ if @@global.pkey
48
+ raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
49
+ raise "Insecure permissions for #{@@global.pkey}" unless (File.stat(@@global.pkey).mode & 600) == 0
50
+ end
51
+ if @option.group
52
+ rgroup = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
53
+ raise "Cannot supply group and instance ID" if @option.instid
54
+ raise "Group #{@option.group} does not exist" unless rgroup.exists?(@option.group)
55
+ end
56
+ if @option.instid && !Rudy::Utils.is_id?(:instance, @option.instid)
57
+ raise "#{@option.instid} is not an instance ID"
58
+ end
59
+ true
60
+ end
61
+ def ssh
62
+ opts = {}
63
+ opts[:group] = @option.group if @option.group
64
+ opts[:group] = :any if @option.all
65
+ opts[:id] = @option.instid if @option.instid
66
+
67
+ # Options to be sent to Rye::Box
68
+ ssh_opts = { :user => @global.user || Rudy.sysinfo.user, :debug => nil }
69
+ if @@global.pkey
70
+ raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
71
+ raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
72
+ ssh_opts[:keys] = @@global.pkey
73
+ end
74
+
75
+
76
+ # The user specified a command to run. We won't create an interactive
77
+ # session so we need to prepare the command and its arguments
78
+ if @argv.first
79
+ command, command_args = @argv.shift, @argv || []
80
+ puts "#{command} #{command_args.join(' ')}" if @@global.verbose > 1
81
+
82
+ # otherwise, we'll open an ssh session or print command
83
+ else
84
+ command, command_args = :interactive_ssh, @option.print.nil?
85
+ end
86
+
87
+ checked = false
88
+ rudy = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
89
+ lt = rudy.list_group(opts[:group], :running, opts[:id]) do |inst|
90
+
91
+ # Print header
92
+ if @@global.quiet
93
+ print "You are #{ssh_opts[:user].bright}. " if !checked # only the 1st
94
+ else
95
+ print "Connecting #{ssh_opts[:user].bright}@#{inst.dns_public} "
96
+ puts "(#{inst.awsid}, groups: #{inst.groups.join(', ')})"
97
+ end
98
+
99
+ # Make sure we want to run this command on all instances
100
+ if !checked && command != :interactive_ssh
101
+ execute_check(:medium) if ssh_opts[:user] == "root"
102
+ checked = true
103
+ end
104
+
105
+ # Open the connection and run the command
106
+ rbox = Rye::Box.new(inst.dns_public, ssh_opts)
107
+ ret = rbox.send(command, command_args)
108
+ puts ret unless command == :interactive_ssh
109
+ end
110
+ end
111
+
112
+ def copy_valid?
113
+ raise "You must supply a source and a target. See rudy-ec2 #{@alias} -h" unless @argv.size >= 2
114
+ raise "You cannot download and upload at the same time" if @option.download && @alias == 'upload'
115
+ raise "You cannot download and upload at the same time" if @option.upload && @alias == 'download'
116
+ true
117
+ end
118
+ def copy
119
+
120
+ opts = {}
121
+ opts[:group] = @option.group if @option.group
122
+ opts[:group] = :any if @option.all
123
+
124
+ opts[:id] = @argv.shift if Rudy::Utils.is_id?(:instance, @argv.first)
125
+ opts[:id] &&= [opts[:id]].flatten
126
+
127
+ # Options to be sent to Net::SSH
128
+ ssh_opts = { :user => @global.user || Rudy.sysinfo.user, :debug => nil }
129
+ if @@global.pkey
130
+ raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
131
+ raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
132
+ ssh_opts[:keys] = @@global.pkey
133
+ end
134
+
135
+ opts[:paths] = @argv
136
+ opts[:dest] = opts[:paths].pop
137
+
138
+ opts[:task] = :download if %w(dl download).member?(@alias) || @option.download
139
+ opts[:task] = :upload if %w(ul upload).member?(@alias)
140
+ opts[:task] ||= :upload
141
+ opts[:user] = @global.user || Rudy.sysinfo.user
142
+
143
+
144
+ # Options to be sent to Rye::Box
145
+ info = @@global.quiet ? nil : STDERR
146
+ ssh_opts = { :user => @global.user || Rudy.sysinfo.user, :info => info }
147
+ if @@global.pkey
148
+ raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
149
+ raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
150
+ ssh_opts[:keys] = @@global.pkey
151
+ end
152
+
153
+
154
+ checked = false
155
+ rudy = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
156
+ lt = rudy.list_group(opts[:group], :running, opts[:id]) do |inst|
157
+
158
+ if @option.print
159
+ Rudy::Utils.scp_command inst.dns_public, @@global.pkey, opts[:user], opts[:paths], opts[:dest], (opts[:task] == :download), false, @option.print
160
+ next
161
+ end
162
+
163
+ # Print header
164
+ if @@global.quiet
165
+ print "You are #{ssh_opts[:user].bright}. " if !checked # only the 1st
166
+ else
167
+ print "Connecting #{ssh_opts[:user].bright}@#{inst.dns_public} "
168
+ puts "(#{inst.awsid}, groups: #{inst.groups.join(', ')})"
169
+ end
170
+
171
+ # Make sure we want to run this command on all instances
172
+ if !checked
173
+ #execute_check(:medium) if opts[:user] == "root"
174
+ checked = true
175
+ end
176
+
177
+ # Open the connection and run the command
178
+ rbox = Rye::Box.new(inst.dns_public, ssh_opts)
179
+ rbox.send(opts[:task], opts[:paths], opts[:dest])
180
+ end
181
+
182
+ end
183
+
184
+
185
+
186
+
187
+ end
188
+
189
+
190
+ end; end
191
+ end; end
@@ -0,0 +1,118 @@
1
+
2
+ module Rudy; module CLI;
3
+ module AWS; module EC2;
4
+
5
+ class Groups < Rudy::CLI::CommandBase
6
+
7
+
8
+
9
+ def create_groups_valid?
10
+ @rgroups = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
11
+ raise Drydock::ArgError.new('group name', @alias) unless @argv.name
12
+ raise "Group #{@argv.name} alread exists" if @rgroups.exists?(@argv.name)
13
+ true
14
+ end
15
+ def create_groups
16
+ opts = check_options
17
+ execute_action {
18
+ @rgroups.create(@argv.name, @option.description, opts[:addresses], opts[:ports], opts[:protocols])
19
+ }
20
+ @rgroups.list(@argv.name) do |group|
21
+ puts @@global.verbose > 0 ? group.inspect : group.dump(@@global.format)
22
+ end
23
+ end
24
+
25
+
26
+ def destroy_groups_valid?
27
+ @rgroups = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
28
+ raise Drydock::ArgError.new('group name', @alias) unless @argv.name
29
+ raise "Group #{@argv.name} does not exist" unless @rgroups.exists?(@argv.name)
30
+ true
31
+ end
32
+
33
+ def destroy_groups
34
+ puts "Destroying group: #{@argv.name}"
35
+ execute_check(:medium)
36
+ execute_action { @rgroups.destroy(@argv.name) }
37
+ @argv.clear # so groups will print all other groups
38
+ groups
39
+ end
40
+
41
+ def revoke_groups_valid?; modify_group_valid?; end
42
+ def revoke_groups; modify_group(:revoke); end
43
+
44
+ def authorize_groups_valid?; modify_group_valid?; end
45
+ def authorize_groups; modify_group(:authorize); end
46
+
47
+ def groups
48
+ opts = {}
49
+ name = @option.all ? nil : @argv.name
50
+ rgroups = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
51
+ rgroups.list(name).each do |group|
52
+ puts @@global.verbose > 0 ? group.inspect : group.dump(@@global.format)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def modify_group_valid?
59
+ if @option.owner == 'self'
60
+ raise "AWS_ACCOUNT_NUMBER not set" unless @@global.accountnum
61
+ @option.owner = @@global.accountnum
62
+ end
63
+
64
+ if (@option.addresses || @option.ports) && (@option.group || @option.owner)
65
+ raise Drydock::OptError.new('', @alias, "Cannot mix group and network authorization")
66
+ end
67
+ if @option.owner && !@option.group
68
+ raise Drydock::OptError.new('', @alias, "Must provide -g with -o")
69
+ end
70
+
71
+ raise Drydock::ArgError.new('group name', @alias) unless @argv.name
72
+ @groups = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
73
+ end
74
+
75
+ def modify_group(action)
76
+ opts = check_options
77
+ if (@option.group || @option.owner)
78
+ g = [opts[:owner], opts[:group]].join(':')
79
+ puts "#{action.to_s.capitalize} access to #{@argv.name.bright} from #{g.bright}"
80
+ else
81
+ print "#{action.to_s.capitalize} access to #{@argv.name.bright}"
82
+ puts " from #{opts[:addresses].join(', ').bright}"
83
+ print "on #{opts[:protocols].join(', ').bright} "
84
+ puts "ports: #{opts[:ports].map { |p| "#{p.join(' to ').bright}" }.join(', ')}"
85
+ end
86
+ rgroups = Rudy::AWS::EC2::Groups.new(@@global.accesskey, @@global.secretkey, @@global.region)
87
+ execute_check(:medium)
88
+ execute_action {
89
+ if (@option.group || @option.owner)
90
+ rgroups.send("#{action.to_s}_group", @argv.name, opts[:group], opts[:owner])
91
+ else
92
+ rgroups.send(action, @argv.name, opts[:addresses], opts[:ports], opts[:protocols])
93
+ end
94
+ }
95
+ groups # prints on the modified group b/c of @argv.name
96
+ end
97
+
98
+ def check_options
99
+ opts = {}
100
+ [:addresses, :protocols, :owner, :group, :ports].each do |opt|
101
+ opts[opt] = @option.send(opt) if @option.respond_to?(opt)
102
+ end
103
+ unless @option.group || @option.owner
104
+ opts[:ports].collect! { |port| port.split(/[:-]/) } if opts[:ports]
105
+ opts[:ports] ||= [[22,22],[80,80],[443,443]]
106
+ opts[:addresses] ||= [Rudy::Utils::external_ip_address]
107
+ opts[:protocols] ||= [:tcp]
108
+ else
109
+ opts[:owner] ||= @@global.accountnum
110
+ end
111
+ opts
112
+ end
113
+
114
+ end
115
+
116
+
117
+ end; end
118
+ end; end
@@ -0,0 +1,185 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module EC2;
5
+
6
+ class Images < Rudy::CLI::CommandBase
7
+
8
+ #def print_header
9
+ # puts @global.print_header, @@global.print_header
10
+ #end
11
+
12
+
13
+ def images_valid?
14
+ if @option.owner == 'self'
15
+ raise "AWS_ACCOUNT_NUMBER not set" unless @@global.accountnum
16
+ @option.owner = @@global.accountnum
17
+ end
18
+
19
+ true
20
+ end
21
+ def images
22
+
23
+ rimages = Rudy::AWS::EC2::Images.new(@@global.accesskey, @@global.secretkey, @@global.region)
24
+ unless @option.all
25
+ @option.owner ||= 'amazon'
26
+ puts "Images owned by #{@option.owner.bright}" unless @argv.awsid
27
+ end
28
+
29
+ images = rimages.list(@option.owner, @argv) || []
30
+ images.each do |img|
31
+ puts @@global.verbose > 0 ? img.inspect : img.dump(@@global.format)
32
+ end
33
+ puts "No images" if images.empty?
34
+ end
35
+
36
+ ##def prepare_images_valid?
37
+ ## true
38
+ ##end
39
+ ##def prepare_images
40
+ ## opts = {}
41
+ ## opts[:id] = @option.instid if @option.instid
42
+ ##
43
+ ## puts "This will do the following:"
44
+ ## puts "- Clear bash history"
45
+ ## # NOTE: We can't delete the host keys here. Otherwise we can't create the image.
46
+ ## #puts "- Delete host SSH keys (this is permanent!)"
47
+ ## puts ""
48
+ ##
49
+ ## ## TODO:
50
+ ## ## ~/.rudy, /etc/motd, history -c, /etc/hosts, /var/log/rudy*
51
+ ##
52
+ ## execute_check(:medium)
53
+ ##
54
+ ##
55
+ ## # Options to be sent to Net::SSH
56
+ ## ssh_opts = { :user => @global.user || Rudy.sysinfo.user, :debug => STDERR }
57
+ ## if @@global.pkey
58
+ ## raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
59
+ ## raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
60
+ ## ssh_opts[:keys] = @@global.pkey
61
+ ## end
62
+ ##
63
+ ##
64
+ ## rudy = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
65
+ ## lt = rudy.list_group(nil, :running, opts[:id]) do |inst|
66
+ ##
67
+ ## puts "Preparing #{inst.dns_public}..."
68
+ ##
69
+ ## # Open the connection and run the command
70
+ ## rbox = Rye::Box.new(inst.dns_public, ssh_opts)
71
+ ##
72
+ ## # We need to explicitly add the rm command for rbox so we
73
+ ## # can delete the SSH host keys. This is will force the instance
74
+ ## # to re-create it's SSH keys on first boot.
75
+ ## def rbox.rm(*args); cmd('rm', args); end
76
+ ## p ret = rbox.history(:c)
77
+ ## p ret.exit_code
78
+ ## p ret.stderr
79
+ ## p ret.stdout
80
+ ##
81
+ ## end
82
+ ##
83
+ ## puts "done"
84
+ ##end
85
+
86
+ def create_images_valid?
87
+ raise "No account number" unless @@global.accountnum
88
+ raise "No Amazon cert-***.pem" unless @@global.cert
89
+ raise "No Amazon pk-***.pem" unless @@global.privatekey
90
+ raise "You must supply a root keypair path" unless @@global.pkey
91
+
92
+ @rinst = Rudy::AWS::EC2::Instances.new(@@global.accesskey, @@global.secretkey, @@global.region)
93
+ @rimages = Rudy::AWS::EC2::Images.new(@@global.accesskey, @@global.secretkey, @@global.region)
94
+ @s3 = Rudy::AWS::S3.new(@@global.accesskey, @@global.secretkey, @@global.region)
95
+ raise "No instances" unless @rinst.any?
96
+ raise "You must supply an S3 bucket name. See: 'rudy-s3 buckets'" unless @option.bucket
97
+ raise "You must supply an image name" unless @option.name
98
+ raise "The bucket '#{@option.bucket}' does not exist" unless @s3.bucket_exists?(@option.bucket)
99
+ true
100
+ end
101
+
102
+ def create_images
103
+ opts = {}
104
+ opts[:id] = @option.instid if @option.instid
105
+
106
+ @@global.user = 'root'
107
+
108
+ puts "You may want to run rudy-ec2 #{@alias} --prepare first".bright
109
+ puts "NOTE 1: This process is currently Linux-only"
110
+ puts "NOTE 2: If you plan to create a public machine image, there are "
111
+ puts "additional steps to take to remove any sensitive information"
112
+ puts "before creating the image. See:"
113
+ puts "http://docs.amazonwebservices.com/AWSEC2/latest/DeveloperGuide/AESDG-chapter-sharingamis.html"
114
+ exit unless Annoy.pose_question(" Continue?\a ", /yes|y|ya|sure|you bet!/i, STDERR)
115
+
116
+ # Options to be sent to Net::SSH
117
+ ssh_opts = { :user => @@global.user || Rudy.sysinfo.user, :debug => nil }
118
+ if @@global.pkey
119
+ raise "Cannot find file #{@@global.pkey}" unless File.exists?(@@global.pkey)
120
+ raise InsecureKeyPermissions, @@global.pkey unless File.stat(@@global.pkey).mode == 33152
121
+ ssh_opts[:keys] = @@global.pkey
122
+ end
123
+
124
+ lt = @rinst.list_group(nil, :running, opts[:id]) do |inst|
125
+
126
+ puts inst.to_s
127
+
128
+ # Open the connection and run the command
129
+ rbox = Rye::Box.new(inst.dns_public, ssh_opts)
130
+ def rbox.bundle_vol(*args); cmd('ec2-bundle-vol', args); end
131
+ def rbox.upload_vol(*args); cmd('ec2-upload-bundle', args); end
132
+
133
+ rbox.upload(@@global.cert, @@global.privatekey, "/mnt")
134
+ rbox.touch("/root/firstrun")
135
+
136
+ ## TODO:
137
+ ## We have to delete the host keys just before we run the bundle command.
138
+ ## The problem is that if we lose the connection we won't be able to connect
139
+ ## to the instance again. A better solution is to add the keys to the ignore
140
+ ## list for the bundle command.
141
+ ##ret = rbox.rm('/etc/ssh/ssh_host_*_key*')
142
+
143
+ puts "Starting bundling process..."
144
+
145
+ pkeyfile = File.basename(@@global.privatekey)
146
+ certfile = File.basename(@@global.cert)
147
+
148
+ rbox.bundle_vol(:r, "i386", :p, @option.name, :k, "/mnt/#{pkeyfile}", :c, "/mnt/#{certfile}", :u, @@global.accountnum)
149
+ rbox.upload_vol(:b, @option.bucket, :m, "/tmp/#{@option.name}.manifest.xml", :a, @@global.accesskey, :s, @@global.secretkey)
150
+
151
+ @rimages.register("#{@option.bucket}/#{@option.name}.manifest.xml")
152
+
153
+ break
154
+ end
155
+
156
+ end
157
+
158
+ def destroy_images_valid?
159
+ unless @argv.ami && Rudy::Utils.is_id?(:image, @argv.ami)
160
+ raise "Must supply an AMI ID (ami-XXXXXXX)"
161
+ end
162
+ @rimages = Rudy::AWS::EC2::Images.new(@@global.accesskey, @@global.secretkey, @@global.region)
163
+ end
164
+ def destroy_images
165
+ puts @rimages.deregister(@argv.ami) ? "Done" : "Unknown error"
166
+ end
167
+
168
+ def register_images_valid?
169
+ unless @argv.first
170
+ raise "Must supply a valid manifest path (bucket/ami-name.manifest.xml)"
171
+ end
172
+ @rimages = Rudy::AWS::EC2::Images.new(@@global.accesskey, @@global.secretkey, @@global.region)
173
+ end
174
+ def register_images
175
+ puts @rimages.register(@argv.first)
176
+ end
177
+
178
+
179
+ end
180
+
181
+
182
+ end; end
183
+ end; end
184
+
185
+