rudy 0.2

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,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
+