solutious-rudy 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,77 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Config < Rudy::Command::Base
6
+
7
+ # We force the Command::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
+ return if @config.nil?
35
+ puts "Config: #{@config.paths}" if @global.verbose > 0
36
+
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
40
+
41
+ return if @config.empty?
42
+
43
+ # We need to check whether we're running on a human's computer
44
+ # or within EC2 (we call that running "in-situ"). The userdata
45
+ # available when running in-situ is in a different format.
46
+ if Rudy.in_situ?
47
+
48
+
49
+ else
50
+
51
+ if @option.all
52
+ y @config.machines.to_hash
53
+ y @config.routines.to_hash
54
+ elsif @option.defaults
55
+ y @config.defaults.to_hash
56
+ else
57
+ env, rol, usr, att = @global.environment, @global.role, @global.user, @argv.name
58
+ val = @config.machines.find_deferred(env, rol, usr, att) || ''
59
+ puts (val.is_a?(String)) ? val : val.to_hash.to_yaml
60
+ end
61
+
62
+ #name = @argv.first
63
+ #if name && @config.userdata.has_key?(which)
64
+ # value = @config.userdata[which][name.to_s]
65
+ # puts value if value
66
+ #elsif @option.all
67
+ # puts @config.to_yaml
68
+ #else
69
+ # value = @config.userdata[which]
70
+ # puts value.to_yaml if value
71
+ #end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,12 @@
1
+
2
+
3
+
4
+
5
+ module Rudy
6
+ module Command
7
+ class Deploy < Rudy::Command::Base
8
+
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,213 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Disks < Rudy::Command::Base
6
+
7
+ def disk
8
+ criteria = [@global.zone]
9
+ criteria += [@global.environment, @global.role] unless @option.all
10
+ Rudy::MetaData::Disk.list(@sdb, *criteria).each do |disk|
11
+ backups = Rudy::MetaData::Backup.for_disk(@sdb, disk, 2)
12
+ print_disk(disk, backups)
13
+ end
14
+ end
15
+
16
+ def create_disk_valid?
17
+ raise "No filesystem path specified" unless @option.path
18
+ raise "No size specified" unless @option.size
19
+ @instances = @ec2.instances.list(machine_group)
20
+ raise "There are no instances running in #{machine_group}" if !@instances || @instances.empty?
21
+ true
22
+ end
23
+
24
+ def create_disk
25
+ puts "Creating #{@option.path} for #{machine_group}"
26
+ switch_user("root")
27
+ exit unless are_you_sure?(2)
28
+ machine = @instances.values.first # NOTE: DANGER! Should handle position.
29
+
30
+ disk = Rudy::MetaData::Disk.new
31
+ [:region, :zone, :environment, :role, :position].each do |n|
32
+ disk.send("#{n}=", @global.send(n)) if @global.send(n)
33
+ end
34
+ [:path, :device, :size].each do |n|
35
+ disk.send("#{n}=", @option.send(n)) if @option.send(n)
36
+ end
37
+
38
+ raise "Not enough info was provided to define a disk (#{disk.name})" unless disk.valid?
39
+ raise "The device #{disk.device} is already in use on that machine" if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
40
+ # TODO: Check disk path
41
+ puts "Creating disk metadata for #{disk.name}"
42
+
43
+
44
+
45
+ puts "Creating volume... (#{disk.size}GB in #{@global.zone})"
46
+ volume = @ec2.volumes.create(@global.zone, disk.size)
47
+ sleep 3
48
+
49
+ disk.awsid = volume[:aws_id]
50
+ disk.raw_volume = true # This value is not saved.
51
+ Rudy::MetaData::Disk.save(@sdb, disk)
52
+
53
+ execute_attach_disk(disk, machine)
54
+
55
+ print_disk(disk)
56
+ end
57
+
58
+
59
+ def destroy_disk_valid?
60
+ raise "No disk specified" if argv.empty?
61
+
62
+ if @argv.diskname =~ /^disk-/
63
+ @disk = Rudy::MetaData::Disk.get(@sdb, @argv.diskname)
64
+ else
65
+ disk = Rudy::MetaData::Disk.new
66
+ [:zone, :environment, :role, :position].each do |n|
67
+ disk.send("#{n}=", @global.send(n)) if @global.send(n)
68
+ end
69
+ disk.path = @argv.diskname
70
+ @disk = Rudy::MetaData::Disk.get(@sdb, disk.name)
71
+ end
72
+
73
+ raise "No such disk: #{@argv.diskname}" unless @disk
74
+ raise "The disk is in another machine environment" unless @global.environment.to_s == @disk.environment.to_s
75
+ raise "The disk is in another machine role" unless @global.role.to_s == @disk.role.to_s
76
+ @instances = @ec2.instances.list(machine_group)
77
+ true
78
+ end
79
+
80
+ def destroy_disk
81
+ puts "Destroying #{@disk.name} and #{@disk.awsid}"
82
+ switch_user("root")
83
+ exit unless are_you_sure?(5)
84
+
85
+ machine = @instances.values.first # NOTE: DANGER! Should handle position.
86
+
87
+ execute_unattach_disk(@disk, machine)
88
+ execute_destroy_disk(@disk, machine)
89
+
90
+ puts "Done."
91
+ end
92
+
93
+ def attach_disk_valid?
94
+ destroy_disk_valid?
95
+ raise "There are no instances running in #{machine_group}" if !@instances || @instances.empty?
96
+ true
97
+ end
98
+
99
+ def attach_disk
100
+ puts "Attaching #{name}"
101
+ switch_user("root")
102
+ are_you_sure?(4)
103
+
104
+ machine = @instances.values.first # AK! Assumes single machine
105
+
106
+ execute_attach_disk(@disk, machine)
107
+
108
+ puts
109
+ ssh_command machine[:dns_name], keypairpath, @global.user, "df -h" # Display current mounts
110
+ puts
111
+
112
+ puts "Done!"
113
+ end
114
+
115
+
116
+
117
+
118
+ def unattach_disk_valid?
119
+ destroy_disk_valid?
120
+ true
121
+ end
122
+
123
+ def unattach_disk
124
+ puts "Unattaching #{@disk.name} from #{machine_group}"
125
+ switch_user("root")
126
+ are_you_sure?(4)
127
+
128
+ machine = @instances.values.first
129
+
130
+ execute_unattach_disk(@disk, machine)
131
+
132
+ puts "Done!"
133
+ end
134
+
135
+
136
+
137
+ def execute_unattach_disk(disk, machine)
138
+ begin
139
+
140
+ if machine
141
+ puts "Unmounting #{disk.path}...".att(:bright)
142
+ ssh_command machine[:dns_name], keypairpath, global.user, "umount #{disk.path}"
143
+ sleep 1
144
+ end
145
+
146
+ if @ec2.volumes.attached?(disk.awsid)
147
+ puts "Unattaching #{disk.awsid}".att(:bright)
148
+ @ec2.volumes.detach(disk.awsid)
149
+ sleep 5
150
+ end
151
+
152
+ rescue => ex
153
+ puts "Error while unattaching volume #{disk.awsid}: #{ex.message}"
154
+ puts ex.backtrace if Drydock.debug?
155
+ end
156
+ end
157
+
158
+ def execute_destroy_disk(disk, machine)
159
+ begin
160
+
161
+ if disk
162
+
163
+ if disk.awsid && @ec2.volumes.available?(disk.awsid)
164
+ puts "Destroying #{disk.path} (#{disk.awsid})".att(:bright)
165
+ @ec2.volumes.destroy(disk.awsid)
166
+ end
167
+
168
+ end
169
+
170
+ rescue => ex
171
+ puts "Error while destroying volume #{disk.awsid}: #{ex.message}"
172
+ puts ex.backtrace if Drydock.debug?
173
+ ensure
174
+ puts "Deleteing metadata for #{disk.name}".att(:bright)
175
+ Rudy::MetaData::Disk.destroy(@sdb, disk)
176
+ end
177
+ end
178
+
179
+ def execute_attach_disk(disk, machine)
180
+ begin
181
+ unless @ec2.instances.attached_volume?(machine[:aws_instance_id], disk.device)
182
+ puts "Attaching #{disk.awsid} to #{machine[:aws_instance_id]}".att(:bright)
183
+ @ec2.volumes.attach(machine[:aws_instance_id], disk.awsid, disk.device)
184
+ sleep 3
185
+ end
186
+
187
+ if disk.raw_volume
188
+ puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})".att(:bright)
189
+ ssh_command machine[:dns_name], keypairpath, @global.user, "mkfs.ext3 -F #{disk.device}"
190
+ sleep 1
191
+ end
192
+
193
+ puts "Mounting #{disk.path} to #{disk.device}".att(:bright)
194
+ ssh_command machine[:dns_name], keypairpath, @global.user, "mkdir -p #{disk.path} && mount -t ext3 #{disk.device} #{disk.path}"
195
+
196
+ sleep 1
197
+ rescue => ex
198
+ puts "There was an error attaching #{disk.name}: #{ex.message}"
199
+ puts ex.backtrace if Drydock.debug?
200
+ end
201
+ end
202
+
203
+ end
204
+ end
205
+ end
206
+
207
+
208
+ __END__
209
+
210
+
211
+
212
+
213
+
@@ -0,0 +1,74 @@
1
+
2
+
3
+
4
+ module Rudy
5
+ module Command
6
+ class Environment < Rudy::Command::Base
7
+
8
+ #---
9
+ # TODO: http://net-ssh.rubyforge.org/ssh/v1/chapter-4.html
10
+ #+++
11
+
12
+
13
+ def connect
14
+ check_keys
15
+ machine = find_current_machine
16
+ if @argv.cmd
17
+ cmd = @argv.cmd.is_a?(Array) ? @argv.cmd.join(' ') : @argv.cmd
18
+ else
19
+ cmd = false
20
+ end
21
+
22
+ ret = ssh_command(machine[:dns_name], keypairpath, @global.user, cmd, @option.print)
23
+ puts ret if ret # ssh command returns false with "ssh_exchange_identification: Connection closed by remote host"
24
+
25
+ end
26
+
27
+ def copy_valid?
28
+ check_keys
29
+ raise "No path specified (rudy copy FROM-PATH [FROM-PATH ...] TO-PATH)" unless argv.size >= 2
30
+ true
31
+ end
32
+
33
+ # +paths+ an array of paths to copy. The last element is the "to" path.
34
+ def copy
35
+ machine = find_current_machine
36
+
37
+ paths = @argv
38
+ dest_path = paths.pop
39
+
40
+ if @option.print
41
+ scp_command machine[:dns_name], keypairpath, @global.user, paths, dest_path, @option.remote, false, @option.print
42
+ return
43
+ end
44
+
45
+ @option.remote = true if @alias == 'download'
46
+ @option.remote = false if @alias == 'upload'
47
+
48
+ if @alias == 'scp' || @alias == 'copy'
49
+ @alias = 'download' if @option.remote
50
+ @alias = 'upload' unless @option.remote
51
+ end
52
+
53
+ scp do |scp|
54
+ transfers = paths.collect { |path|
55
+ scp.send(@alias, path, dest_path) do |ch, name, sent, total|
56
+ #TODO: Nice printing in place
57
+ #puts "#{name}: #{sent}/#{total}"
58
+ end
59
+
60
+ }
61
+ transfers.each { |trans| trans.wait }
62
+ end
63
+ end
64
+
65
+
66
+
67
+
68
+ end
69
+ end
70
+ end
71
+
72
+ __END__
73
+
74
+
@@ -0,0 +1,61 @@
1
+
2
+
3
+
4
+ module Rudy
5
+ module Command
6
+ class Groups < Rudy::Command::Base
7
+
8
+ def groups(name=@argv.first)
9
+ name = machine_group if name.nil? && !@option.all
10
+ @ec2.groups.list(name).each do |grp|
11
+ print_group grp
12
+ end
13
+ end
14
+
15
+ def create_groups(name=@argv.first)
16
+ name ||= machine_group
17
+ puts "Creating group #{name}"
18
+ raise "The group #{name} already exists" if @ec2.groups.exists?(name)
19
+
20
+ @ec2.groups.create(name)
21
+
22
+ modify_groups name
23
+ end
24
+
25
+ def modify_groups(name=@argv.first)
26
+ name ||= machine_group
27
+ raise "The group #{name} does not exist" unless @ec2.groups.exists?(name)
28
+
29
+ @option.addresses ||= [Rudy::Utils::external_ip_address]
30
+ @option.ports ||= [22,80,443]
31
+ @option.protocols ||= ["tcp"]
32
+
33
+ # Make sure the IP addresses have ranges
34
+ @option.addresses.collect! { |ip| (ip.match /\/\d+/) ? ip : "#{ip}/32" }
35
+
36
+ @option.protocols.each do |protocol|
37
+ puts "Adding ports #{@option.ports.join(',')} (#{protocol}) for #{@option.addresses.join(', ')}"
38
+ @option.addresses.each do |address|
39
+ @option.ports.each do |port|
40
+ @ec2.groups.modify(name, port, port, protocol, address)
41
+ end
42
+ end
43
+ end
44
+
45
+ groups name
46
+ end
47
+
48
+ def destroy_groups(name=@argv.first)
49
+ name ||= machine_group
50
+ puts "Destroying group #{name}"
51
+ name = machine_group if name.nil?
52
+ raise "The group #{name} does not exist" unless @ec2.groups.exists?(name)
53
+ exit unless are_you_sure?
54
+
55
+ @ec2.groups.destroy(name)
56
+
57
+ end
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,99 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Images < Rudy::Command::Base
6
+
7
+
8
+ def images
9
+ @ec2.images.list.each do |img|
10
+ print_image img
11
+ end
12
+ end
13
+
14
+ def create_images_valid?
15
+ puts "Make sure the machine is clean. I don't want archive no crud!"
16
+ switch_user("root")
17
+
18
+ raise "No EC2 .pem keys provided" unless has_pem_keys?
19
+ raise "No SSH key provided for #{@global.user}!" unless has_keypair?
20
+ raise "No SSH key provided for root!" unless has_keypair?(:root)
21
+ true
22
+ end
23
+
24
+
25
+ def prepare_images
26
+ # TODO: Avail hooks for clean an instance
27
+ # Clean off Rudy specific crap.
28
+ end
29
+
30
+
31
+ def create_images
32
+ puts "Creating image from #{machine_group}"
33
+
34
+ # ~/.rudy, /etc/motd, history -c, /etc/hosts, /var/log/rudy*
35
+
36
+ are_you_sure?(2)
37
+
38
+
39
+ machine_list = @ec2.instances.list(machine_group)
40
+ machine = machine_list.values.first # NOTE: Only one machine per group, for now...
41
+
42
+ raise "There's no machine running in #{machine_group}" unless machine
43
+ raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
44
+
45
+ puts "The new image will be based on #{machine_group}_01"
46
+
47
+ @option.account ||= @config.awsinfo.account
48
+
49
+ unless @option.account
50
+ puts "Enter your 12 digit Amazon account number:"
51
+ @global.account = gets.chomp
52
+ end
53
+
54
+ unless @option.image_name
55
+ puts "Enter the image name:"
56
+ @option.image_name = gets.chomp
57
+ end
58
+
59
+ unless @option.bucket_name
60
+ puts "Enter the S3 bucket that will store the image:"
61
+ @option.bucket_name = gets.chomp
62
+ end
63
+
64
+ unless @option.print
65
+ puts "Copying .pem keys to /mnt (they will not be included in the AMI)"
66
+ scp_command machine[:dns_name], keypairpath, @global.user, @global.cert, "/mnt/"
67
+ scp_command machine[:dns_name], keypairpath, @global.user, @global.privatekey, "/mnt/"
68
+ end
69
+
70
+ ssh do |session|
71
+ session.exec!("touch /root/firstrun")
72
+ end
73
+
74
+ puts "Starting bundling process...".att(:bright)
75
+ puts ssh_command(machine[:dns_name], keypairpath, @global.user, "ec2-bundle-vol -r i386 -p #{@option.image_name} -k /mnt/pk-*pem -c /mnt/cert*pem -u #{@option.account}", @option.print)
76
+ puts ssh_command(machine[:dns_name], keypairpath, @global.user, "ec2-upload-bundle -b #{@option.bucket_name} -m /tmp/#{@option.image_name}.manifest.xml -a #{@global.accesskey} -s #{@global.secretkey}", @option.print)
77
+
78
+ @ec2.images.register("#{@option.bucket_name}/#{@option.image_name}.manifest.xml") unless @option.print
79
+ end
80
+
81
+ def deregister
82
+ ami = @argv.first
83
+ raise "You must supply an AMI ID (ami-XXXXXXX)" unless ami
84
+ puts "Deregistering AMI: #{ami}"
85
+
86
+ are_you_sure?
87
+
88
+ if @ec2.images.deregister(ami)
89
+ puts "Done!"
90
+ else
91
+ puts "There was an unknown problem!"
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+ end
99
+