solutious-rudy 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+