rudy 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,275 @@
1
+
2
+ module Rudy
3
+ class UnknownInstance < RuntimeError; end
4
+ end
5
+
6
+ module Rudy
7
+ module Command
8
+ class NoCred < RuntimeError; end;
9
+
10
+ class Base < Drydock::Command
11
+
12
+ attr_accessor :access_key
13
+ attr_accessor :secret_key
14
+ attr_accessor :ec2_cert
15
+ attr_accessor :ec2_private_key
16
+ attr_accessor :keypairs
17
+
18
+ attr_reader :user
19
+ attr_reader :environment
20
+ attr_reader :role
21
+ attr_reader :position
22
+
23
+ attr_reader :zone
24
+ attr_reader :region
25
+
26
+ attr_reader :scm
27
+
28
+ attr_reader :rscripts
29
+
30
+ attr_reader :machine_images
31
+
32
+ def init
33
+ @access_key = ENV['AWS_ACCESS_KEY'] unless @access_key
34
+ @secret_key = ENV['AWS_SECRET_KEY'] unless @secret_key
35
+
36
+ @ec2_cert = ENV['EC2_CERT'] unless @ec2_cert
37
+ @ec2_private_key = ENV['EC2_PRIVATE_KEY'] unless @ec2_private_key
38
+
39
+ if ENV['RUDY_SVN_BASE']
40
+ @scm = Rudy::SCM::SVN.new(ENV['RUDY_SVN_BASE'])
41
+ end
42
+
43
+ @user ||= 'rudy'
44
+ @environment ||= 'stage'
45
+ @role ||= 'app'
46
+ @position ||= '01'
47
+
48
+ @zone ||= DEFAULT_ZONE
49
+ @region ||= DEFAULT_REGION
50
+
51
+ @keypairs = {}
52
+ ENV.keys.select { |key| key.match /EC2_KEYPAIR/i }.each do |key|
53
+ ec2, keypair, env, role, user = key.split '_' # EC2_KEYPAIR_STAGE_APP_RUDY
54
+ raise "#{key} is malformed." unless env && role && user
55
+ new_key = "#{env}-#{role}-#{user}".downcase
56
+ @keypairs[new_key] = ENV[key]
57
+ end
58
+
59
+ @rscripts = {}
60
+ ENV.keys.select { |key| key.match /RUDY_RSCRIPT/i }.each do |key|
61
+ rudy, rscript, env, role, user = key.split '_' # RUDY_RSCRIPT_STAGE_APP_ROOT
62
+ raise "#{key} is malformed." unless env && role && user
63
+ new_key = "#{env}-#{role}-#{user}".downcase
64
+ @rscripts[new_key] = ENV[key]
65
+ end
66
+
67
+ @machine_images = {}
68
+ ENV.keys.select { |key| key.match /EC2_AMI_/i }.each do |key|
69
+ ec2, ami, env, role = key.split '_' # RUDY_RSCRIPT_STAGE_APP_ROOT
70
+ raise "#{key} is malformed." unless env && role
71
+ new_key = "#{env}-#{role}".downcase
72
+ @machine_images[new_key] = ENV[key]
73
+ end
74
+
75
+ if @verbose > 1 && Drydock.debug?
76
+ instance_variables.each do |var|
77
+ puts "#{var}: #{instance_variable_get(var)}"
78
+ end
79
+ end
80
+
81
+ @sdb = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
82
+ @ec2 = Rudy::AWS::EC2.new(@access_key, @secret_key)
83
+ #@s3 = Rudy::AWS::SimpleDB.new(@access_key, @secret_key)
84
+ end
85
+
86
+ def has_pem_keys?
87
+ (@ec2_cert && File.exists?(@ec2_cert) &&
88
+ @ec2_private_key && File.exists?(@ec2_private_key))
89
+ end
90
+
91
+ def has_keys?
92
+ (@access_key && @secret_key)
93
+ end
94
+
95
+ def has_keypair?(name)
96
+ (has_keypairs? && @keypairs.has_key?(name))
97
+ end
98
+
99
+ def has_keypairs?
100
+ (!@keypairs.empty?)
101
+ end
102
+
103
+ def machine_group
104
+ [@environment, @role].join(RUDY_DELIM)
105
+ end
106
+
107
+ def machine_image
108
+ @machine_images[machine_group]
109
+ end
110
+
111
+ def machine_name
112
+ [machine_group, @position].join(RUDY_DELIM)
113
+ end
114
+
115
+ def keypairname
116
+ [machine_group, user].join(RUDY_DELIM)
117
+ end
118
+
119
+ def keypairpath
120
+ return unless has_keypair?(keypairname)
121
+ @keypairs[keypairname]
122
+ end
123
+
124
+ def instance_id?(id=nil)
125
+ (id && id[0,2] == "i-")
126
+ end
127
+
128
+ def image_id?(id=nil)
129
+ (id && id[0,4] == "ami-")
130
+ end
131
+
132
+ def volume_id?(id=nil)
133
+ (id && id[0,4] == "vol-")
134
+ end
135
+
136
+ def snapshot_id?(id=nil)
137
+ (id && id[0,5] == "snap-")
138
+ end
139
+
140
+
141
+ def do_dirty_disk_volume_deeds(disk, machine)
142
+ puts "-"*30
143
+ puts "Disk: #{disk.name} (path: #{disk.path}, device: #{disk.device})"
144
+ puts
145
+
146
+ if @ec2.instances.attached_volume?(machine[:aws_instance_id], disk.device)
147
+ raise "#{disk.device} is already in use on #{machine[:aws_instance_id]}! (Try umounting it)"
148
+ end
149
+
150
+ new_volume = false
151
+ if !disk.awsid || (disk.awsid && !@ec2.volumes.exists?(disk.awsid))
152
+ disk = Rudy::MetaData::Disk.update_volume(@sdb, @ec2, disk, machine)
153
+ new_volume = true
154
+ end
155
+
156
+ raise "Unknown error creating volume! #{disk.awsid}" unless disk && disk.awsid
157
+
158
+ puts "Attaching #{disk.awsid} to #{machine[:aws_instance_id]}"
159
+ @ec2.volumes.attach(machine[:aws_instance_id], disk.awsid, disk.device)
160
+ sleep 2
161
+
162
+ @user = "root"
163
+
164
+ if new_volume
165
+ puts "Creating the filesystem (mkfs.ext3 -F #{disk.device})"
166
+ capture(:stdout) do
167
+ ssh machine[:dns_name], keypairpath, user, "mkfs.ext3 -F #{disk.device}"
168
+ end
169
+ sleep 2
170
+ end
171
+
172
+ puts "Mounting #{disk.device} to #{disk.path}"
173
+ capture(:stdout) do
174
+ ssh machine[:dns_name], keypairpath, user, "mkdir -p #{disk.path} && mount -t ext3 #{disk.device} #{disk.path}"
175
+ end
176
+ sleep 1
177
+
178
+ end
179
+
180
+
181
+
182
+ # Print a default header to the screen for every command.
183
+ # +cmd+ is the name of the command current running.
184
+ def print_header(cmd=nil)
185
+ print "RUDY v#{Rudy::VERSION}"
186
+ print " -- #{@alias}" if @alias
187
+ puts
188
+
189
+ criteria = []
190
+ [:zone, :environment, :role, :position].each do |n|
191
+ val = instance_variable_get("@#{n}")
192
+ criteria << "[#{n} = #{val}]"
193
+ end
194
+ puts criteria.join(" and ")
195
+
196
+ if (@environment == "prod")
197
+ puts %q(
198
+ =======================================================
199
+ =======================================================
200
+ !!!!!!!!! YOU ARE PLAYING WITH PRODUCTION !!!!!!!!!
201
+ =======================================================
202
+ =======================================================
203
+ )
204
+ end
205
+
206
+ end
207
+
208
+ def print_footer
209
+
210
+ end
211
+
212
+
213
+
214
+ def group_metadata(env=@environment, role=@role)
215
+ query = "['environment' = '#{env}'] intersection ['role' = '#{role}']"
216
+ @sdb.query_with_attributes(RUDY_DOMAIN, query)
217
+ end
218
+
219
+ private
220
+ # Print info about a running instance
221
+ # +inst+ is a hash
222
+ def print_instance(inst)
223
+ puts '-'*60
224
+ puts "Instance: #{inst[:aws_instance_id]} (AMI: #{inst[:aws_image_id]})"
225
+ [:aws_state, :dns_name, :private_dns_name, :aws_availability_zone, :aws_launch_time, :ssh_key_name].each do |key|
226
+ printf(" %22s: %s#{$/}", key, inst[key]) if inst[key]
227
+ end
228
+ printf(" %22s: %s#{$/}", 'aws_groups', inst[:aws_groups].join(', '))
229
+ puts
230
+ end
231
+
232
+ def print_image(img)
233
+ puts '-'*60
234
+ puts "Image: #{img[:aws_location]}"
235
+ img.each_pair do |key, value|
236
+ printf(" %22s: %s#{$/}", key, value) if value
237
+ end
238
+ puts
239
+ end
240
+
241
+ def print_disk(disk)
242
+ puts '-'*60
243
+ puts "Disk: #{disk.name}"
244
+ puts disk.to_s
245
+ puts
246
+ end
247
+
248
+ # Print info about a a security group
249
+ # +group+ is an OpenStruct
250
+ def print_group(group)
251
+ puts '-'*60
252
+ puts "%12s: %s" % ['GROUP', group[:aws_group_name]]
253
+ puts
254
+
255
+ group_ip = {}
256
+ group[:aws_perms].each do |perm|
257
+ (group_ip[ perm[:cidr_ips] ] ||= []) << "#{perm[:protocol]}/#{perm[:from_port]}-#{perm[:to_port]}"
258
+ end
259
+
260
+ puts "%22s %s" % ["source address/mask", "protocol/ports (from, to)"]
261
+
262
+
263
+ group_ip.each_pair do |ip, perms|
264
+ puts "%22s %s" % [ip, perms.shift]
265
+ perms.each do |perm|
266
+ puts "%22s %s" % ['', perm]
267
+ end
268
+ puts
269
+ end
270
+ end
271
+
272
+
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,10 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Commit < Rudy::Command::Base
6
+
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,61 @@
1
+
2
+
3
+ module Rudy
4
+ module Command
5
+ class Disks < Rudy::Command::Base
6
+
7
+
8
+ def create_disk
9
+ disk = Rudy::MetaData::Disk.new
10
+ [:environment, :role, :position, :path, :device, :size].each do |n|
11
+ val = instance_variable_get("@#{n}")
12
+ disk.send("#{n}=", val) if val
13
+ end
14
+
15
+ raise "Not enough info was provided to define a disk (#{disk.name})" unless disk.valid?
16
+ raise "The device #{disk.device} is already in use on that machine" if Rudy::MetaData::Disk.is_defined?(@sdb, disk)
17
+ puts "Creating disk metadata for #{disk.name}"
18
+
19
+ Rudy::MetaData::Disk.save(@sdb, disk)
20
+
21
+ print_disks
22
+ end
23
+
24
+ def print_disks
25
+ criteria = [@zone]
26
+ criteria += [@environment, @role] unless @all
27
+
28
+ Rudy::MetaData::Disk.list(@sdb, *criteria).each do |disk|
29
+ print_disk disk
30
+ end
31
+
32
+ end
33
+
34
+ def attach_disk(name)
35
+ puts "Looking for #{name}"
36
+ disk = Rudy::MetaData::Disk.get(@sdb, name)
37
+ instances = @ec2.instances.list(machine_group)
38
+ raise "There are no instances running in #{machine_group}" if !instances || instances.empty?
39
+ instance_id = instances.keys.first
40
+ machine = instances.values.first
41
+
42
+ do_dirty_disk_volume_deeds(disk, machine)
43
+
44
+
45
+ puts
46
+ ssh machine[:dns_name], keypairpath, user, "df -h" # Display current mounts
47
+ puts
48
+
49
+ puts "Done!"
50
+ end
51
+
52
+ def destroy_disk(name)
53
+ puts "Destroying #{name}"
54
+ @sdb.destroy(RUDY_DOMAIN, name)
55
+ puts "Done."
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,95 @@
1
+
2
+
3
+
4
+
5
+
6
+ # :role:
7
+ # :env:
8
+ # :access_key:
9
+ # :secret_key:
10
+ # :dbmachine:
11
+
12
+
13
+ module Rudy
14
+ module Command
15
+ class Environment < Rudy::Command::Base
16
+
17
+ def connect
18
+ raise "No SSH key provided for #{keypairname}!" unless has_keypair?(keypairname)
19
+ raise "SSH key provided but cannot be found! (#{keypairpath})" unless File.exists?(keypairpath)
20
+ machine_list = @ec2.instances.list(machine_group)
21
+ machine = machine_list.values.first # NOTE: Only one machine per group, for now...
22
+
23
+ raise "There's no machine running in #{machine_group}" unless machine
24
+ raise "The primary machine in #{machine_group} is not in a running state" unless machine[:aws_state] == 'running'
25
+ cmd = "ssh -i #{keypairpath} #{user}@#{machine[:dns_name]}"
26
+ puts cmd
27
+ system(cmd) unless @print
28
+ end
29
+
30
+ # Start EC2 instances to run the stage.
31
+ # Returns a hash in the same format as +instances+
32
+ def build_stage
33
+
34
+ rig = running_instance_groups(@app_group)
35
+ raise "You already have a stage: #{rig.join(', ')}" unless rig.empty?
36
+
37
+ root_keypair_name = File.basename(@app_root_keypair)
38
+
39
+ # This is read by the Rudy start up script /etc/init.d/rudy-ec2-startup
40
+ user_data = {
41
+ :dbmaster => "localhost",
42
+ :role => "FE",
43
+ :env => "stage"
44
+ }
45
+
46
+ ret = @ec2.run_instances(@app_ami, 1, 1, [@app_group], root_keypair_name, user_data.to_yaml, 'public')
47
+ puts "The instance has started. Please wait while it boots..."
48
+ puts "Check '#{$0} state' for aws_state 'running'."
49
+ puts "Then run '#{$0} stage --start"
50
+ associate_address
51
+ ret
52
+ end
53
+
54
+
55
+ # Start (rake bootstrap, etc...) the rails app on the stage
56
+ def start_stage
57
+
58
+ rig = running_instance_groups(@app_group)
59
+ raise "There is no stage start!" if rig.empty?
60
+ cmds =[]
61
+ cmds.each do |cmd|
62
+ puts cmd
63
+ `#{cmd}`
64
+ end
65
+
66
+ end
67
+
68
+ # Shutdown all instances in the stage security group
69
+ def destroy_stage
70
+
71
+ rig = running_instance_groups(@app_group)
72
+ raise "There is no stage to destroy!" if rig.empty?
73
+ stopped = []
74
+ rig.each do |inst|
75
+ stopped << inst[:aws_instance_id]
76
+ end
77
+ destroy_instances(stopped)
78
+ end
79
+
80
+ #def svn_tag_exists?(rtag, prefix)
81
+ # return false if rtag.empty?
82
+ # `svn info tag/`
83
+ #end
84
+
85
+ def tag_release
86
+
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+
93
+ __END__
94
+
95
+
@@ -0,0 +1,59 @@
1
+
2
+
3
+
4
+ module Rudy
5
+ module Command
6
+ class Groups < Rudy::Command::Base
7
+
8
+ def print_groups(name=nil)
9
+ name = machine_group if name.nil? && !@all
10
+ @ec2.groups.list(name).each do |grp|
11
+ print_group grp
12
+ end
13
+ end
14
+
15
+ def create_group(name=nil)
16
+ name = machine_group if name.nil?
17
+ raise "The group #{name} already exists" if @ec2.groups.exists?(name)
18
+
19
+ puts "Creating group #{name}"
20
+ @ec2.groups.create(name)
21
+
22
+ modify_group name
23
+ end
24
+
25
+ def modify_group(name=nil)
26
+ name = machine_group if name.nil?
27
+ raise "The group #{name} does not exist" unless @ec2.groups.exists?(name)
28
+
29
+ @addresses = [Rudy::Utils::external_ip_address] if @addresses.nil?
30
+ @ports = [22,80,443] if @ports.nil?
31
+ @protocols = ["tcp"] if @protocols.nil?
32
+
33
+ # Make sure the IP addresses have ranges
34
+ @addresses.collect! { |ip| (ip.match /\/\d+/) ? ip : "#{ip}/32" }
35
+
36
+ @protocols.each do |protocol|
37
+ puts "Adding ports #{@ports.join(',')} (#{protocol}) for #{@addresses.join(', ')}"
38
+ @addresses.each do |address|
39
+ @ports.each do |port|
40
+ @ec2.groups.modify(name, port, port, protocol, address)
41
+ end
42
+ end
43
+ end
44
+
45
+ print_groups name
46
+ end
47
+
48
+ def destroy_group(name=nil)
49
+ name = machine_group if name.nil?
50
+ raise "The group #{name} does not exist" unless @ec2.groups.exists?(name)
51
+
52
+ puts "Destroying group #{name}"
53
+ @ec2.groups.destroy(name)
54
+
55
+ end
56
+ end
57
+ end
58
+ end
59
+