bschwartz-capsize 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -0
- data/License.txt +67 -0
- data/Manifest.txt +24 -0
- data/README.textile +83 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +76 -0
- data/config/requirements.rb +18 -0
- data/examples/capsize.yml.template +67 -0
- data/examples/deploy.rb +29 -0
- data/lib/capsize/capsize.rb +101 -0
- data/lib/capsize/configuration.rb +44 -0
- data/lib/capsize/ec2.rb +654 -0
- data/lib/capsize/ec2_plugin.rb +618 -0
- data/lib/capsize/meta_tasks.rb +156 -0
- data/lib/capsize/sqs.rb +57 -0
- data/lib/capsize/sqs_plugin.rb +128 -0
- data/lib/capsize/version.rb +9 -0
- data/lib/capsize.rb +26 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +27 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/test_capsize.rb +12 -0
- data/test/test_helper.rb +10 -0
- metadata +130 -0
@@ -0,0 +1,618 @@
|
|
1
|
+
module Capsize
|
2
|
+
module CapsizeEC2
|
3
|
+
include Capsize
|
4
|
+
|
5
|
+
|
6
|
+
# HELPER METHODS
|
7
|
+
#########################################
|
8
|
+
|
9
|
+
|
10
|
+
def hostname_from_instance_id(instance_id = nil)
|
11
|
+
raise Exception, "Instance ID required" if instance_id.nil? || instance_id.empty?
|
12
|
+
|
13
|
+
amazon = connect()
|
14
|
+
|
15
|
+
response = amazon.describe_instances(:instance_id => instance_id)
|
16
|
+
return dns_name = response.reservationSet.item[0].instancesSet.item[0].dnsName
|
17
|
+
end
|
18
|
+
|
19
|
+
def hostnames_from_instance_ids(ids = [])
|
20
|
+
ids.collect { |id| hostname_from_instance_id(id) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def hostnames_from_group(group_name = nil)
|
24
|
+
hostnames = []
|
25
|
+
return hostnames if group_name.nil?
|
26
|
+
instances = describe_instances
|
27
|
+
return hostnames if instances.nil?
|
28
|
+
return hostnames if instances.reservationSet.nil?
|
29
|
+
instances.reservationSet.item.each do |reservation|
|
30
|
+
hostname = nil
|
31
|
+
in_group = false
|
32
|
+
running = false
|
33
|
+
unless reservation.groupSet.nil?
|
34
|
+
reservation.groupSet.item.each do |group|
|
35
|
+
in_group = group.groupId == group_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
unless reservation.instancesSet.nil?
|
40
|
+
reservation.instancesSet.item.each do |instance|
|
41
|
+
hostname = instance.dnsName
|
42
|
+
running = (!instance.instanceState.nil? && (instance.instanceState.name == "running"))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
hostnames << hostname if in_group and running
|
46
|
+
end
|
47
|
+
return hostnames
|
48
|
+
end
|
49
|
+
|
50
|
+
def role_from_security_group(role, security_group, *args)
|
51
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
52
|
+
options = {:user => 'root', :ssh_options => { :keys => [capsize_ec2.get_key_file] }}.merge(options)
|
53
|
+
role(role, options) do
|
54
|
+
hostnames_from_group(security_group)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# build the key file path from key_dir and key_file
|
59
|
+
def get_key_file(options = {})
|
60
|
+
options = {:key_dir => nil, :key_name => nil}.merge(options)
|
61
|
+
key_dir = options[:key_dir] || get(:key_dir) || get(:capsize_secure_config_dir)
|
62
|
+
key_name = options[:key_name] || get(:key_name)
|
63
|
+
return key_file = [key_dir, "id_rsa-" + key_name].join('/')
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# CONSOLE METHODS
|
68
|
+
#########################################
|
69
|
+
|
70
|
+
|
71
|
+
def get_console_output(options = {})
|
72
|
+
amazon = connect()
|
73
|
+
options = {:instance_id => ""}.merge(options)
|
74
|
+
amazon.get_console_output(:instance_id => options[:instance_id])
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# KEYPAIR METHODS
|
79
|
+
#########################################
|
80
|
+
|
81
|
+
|
82
|
+
#describe your keypairs
|
83
|
+
def describe_keypairs(options = {})
|
84
|
+
amazon = connect()
|
85
|
+
options = {:key_name => []}.merge(options)
|
86
|
+
amazon.describe_keypairs(:key_name => options[:key_name])
|
87
|
+
end
|
88
|
+
|
89
|
+
#sets up a keypair named options[:key_name] and writes out the private key to options[:key_dir]
|
90
|
+
def create_keypair(options = {})
|
91
|
+
amazon = connect()
|
92
|
+
|
93
|
+
# default key_name is the same as our appname, unless specifically overriden in capsize.yml
|
94
|
+
# default key_dir is set in the :capsize_config_dir variable
|
95
|
+
options = {:key_name => nil, :key_dir => nil}.merge(options)
|
96
|
+
|
97
|
+
options[:key_name] = options[:key_name] || get(:key_name)
|
98
|
+
options[:key_dir] = options[:key_dir] || get(:key_dir) || get(:capsize_secure_config_dir)
|
99
|
+
|
100
|
+
#verify key_name and key_dir are set
|
101
|
+
raise Exception, "Keypair name required" if options[:key_name].nil? || options[:key_name].empty?
|
102
|
+
raise Exception, "Keypair directory required" if options[:key_dir].nil? || options[:key_dir].empty?
|
103
|
+
|
104
|
+
key_file = get_key_file(:key_name => options[:key_name], :key_dir => options[:key_dir])
|
105
|
+
|
106
|
+
# Verify keypair doesn't already exist on EC2 servers...
|
107
|
+
unless amazon.describe_keypairs(:key_name => options[:key_name]).keySet.nil?
|
108
|
+
raise Exception, "Sorry, a keypair with the name \"#{options[:key_name]}\" already exists on EC2."
|
109
|
+
end
|
110
|
+
|
111
|
+
# and doesn't exist locally either...
|
112
|
+
file_exists_message = <<-MESSAGE
|
113
|
+
\n
|
114
|
+
Warning! A keypair with the name \"#{key_file}\"
|
115
|
+
already exists on your local filesytem. You must remove it before trying to overwrite
|
116
|
+
again. Warning! Removing keypairs associated with active instances will prevent you
|
117
|
+
from accessing them via SSH or Capistrano!!\n\n
|
118
|
+
MESSAGE
|
119
|
+
raise Exception, file_exists_message if File.exists?(key_file)
|
120
|
+
|
121
|
+
#All is good, so we create the new keypair
|
122
|
+
private_key = amazon.create_keypair(:key_name => options[:key_name])
|
123
|
+
|
124
|
+
# write private key to file
|
125
|
+
File.open(key_file, 'w') do |file|
|
126
|
+
file.write(private_key.keyMaterial)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Cross platform CHMOD, make the file owner +rw, group and other -all
|
130
|
+
File.chmod 0600, key_file
|
131
|
+
return [key_name, key_file]
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# TODO : Is there a way to extract the 'puts' calls from here and make this have less 'view' code?
|
136
|
+
# Deletes a keypair from EC2 and from the local filesystem
|
137
|
+
def delete_keypair(options = {})
|
138
|
+
amazon = connect()
|
139
|
+
|
140
|
+
options = {:key_name => nil, :key_dir => nil}.merge(options)
|
141
|
+
|
142
|
+
options[:key_name] = options[:key_name] || get(:key_name)
|
143
|
+
options[:key_dir] = options[:key_dir] || get(:key_dir) || get(:capsize_secure_config_dir)
|
144
|
+
|
145
|
+
raise Exception, "Keypair name required" if options[:key_name].nil? || options[:key_name].empty?
|
146
|
+
raise Exception, "Keypair directory required" if options[:key_dir].nil? || options[:key_dir].empty?
|
147
|
+
raise Exception, "Keypair \"#{options[:key_name]}\" does not exist on EC2." if amazon.describe_keypairs(:key_name => options[:key_name]).keySet.nil?
|
148
|
+
|
149
|
+
# delete the keypair from the amazon EC2 servers
|
150
|
+
amazon.delete_keypair(:key_name => options[:key_name])
|
151
|
+
puts "Keypair \"#{options[:key_name]}\" deleted from EC2!"
|
152
|
+
|
153
|
+
begin
|
154
|
+
# determine the local key file name and delete it
|
155
|
+
key_file = get_key_file(:key_name => options[:key_name])
|
156
|
+
File.delete(key_file)
|
157
|
+
rescue
|
158
|
+
puts "Keypair \"#{key_file}\" not found on the local filesystem."
|
159
|
+
else
|
160
|
+
puts "Keypair \"#{key_file}\" deleted from local file system!"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# IMAGE METHODS
|
166
|
+
#########################################
|
167
|
+
|
168
|
+
|
169
|
+
#describe the amazon machine images available for launch
|
170
|
+
# Even though the amazon-ec2 library allows us to pass in an array of image_id's,
|
171
|
+
# owner_id's, or executable_by's we restrict Capsize usage to passing in a String
|
172
|
+
# with a single value.
|
173
|
+
def describe_images(options = {})
|
174
|
+
amazon = connect()
|
175
|
+
|
176
|
+
options = {:image_id => nil, :owner_id => nil, :executable_by => nil}.merge(options)
|
177
|
+
|
178
|
+
options[:image_id] = options[:image_id] || get(:image_id) || ""
|
179
|
+
options[:owner_id] = options[:owner_id] || get(:owner_id) || ""
|
180
|
+
options[:executable_by] = options[:executable_by] || get(:executable_by) || ""
|
181
|
+
|
182
|
+
amazon.describe_images(:image_id => options[:image_id], :owner_id => options[:owner_id], :executable_by => options[:executable_by])
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# INSTANCE METHODS
|
188
|
+
#########################################
|
189
|
+
|
190
|
+
|
191
|
+
#returns information about instances owned by the user
|
192
|
+
def describe_instances(options = {})
|
193
|
+
amazon = connect()
|
194
|
+
options = {:instance_id => []}.merge(options)
|
195
|
+
amazon.describe_instances(:instance_id => options[:instance_id])
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
# Run EC2 instance(s)
|
200
|
+
# TODO : Deal with starting multiple instances! Now only single instances are properly handled.
|
201
|
+
def run_instance(options = {})
|
202
|
+
amazon = connect()
|
203
|
+
|
204
|
+
options = { :image_id => get(:image_id),
|
205
|
+
:min_count => get(:min_count),
|
206
|
+
:max_count => get(:max_count),
|
207
|
+
:key_name => nil,
|
208
|
+
:group_name => nil,
|
209
|
+
:user_data => get(:user_data),
|
210
|
+
:addressing_type => get(:addressing_type),
|
211
|
+
:instance_type => get(:instance_type)
|
212
|
+
}.merge(options)
|
213
|
+
|
214
|
+
# What security group should we run as?
|
215
|
+
options[:group_id] = (options[:group_name] || get(:group_name) || "").split(',')
|
216
|
+
|
217
|
+
# We want to run the new instance using our public/private keypair if
|
218
|
+
# one is defined for this application or of the user has explicitly passed
|
219
|
+
# in a key_name as a parameter. Only allow use of application name keyname if
|
220
|
+
# the <application> name is defined on EC2 as a key_name, AND we have the local
|
221
|
+
# private key stored in the config dir.
|
222
|
+
|
223
|
+
# override application key_name if the user provided one in config or on the command line
|
224
|
+
options[:key_name] = options[:key_name] || get(:key_name)
|
225
|
+
|
226
|
+
# key_dir defaults to same as :capsize_config_dir variable
|
227
|
+
options[:key_dir] = options[:key_dir] || get(:key_dir) || get(:capsize_secure_config_dir)
|
228
|
+
|
229
|
+
# determine the local key file name and delete it
|
230
|
+
key_file = get_key_file(:key_name => options[:key_name], :key_dir => options[:key_dir])
|
231
|
+
|
232
|
+
# don't let them go further if there is no private key present.
|
233
|
+
raise Exception, "Private key is not present in #{key_file}.\nPlease generate one with 'cap ec2:keypairs:create' or specify a different KEY_NAME." unless File.exists?(key_file)
|
234
|
+
|
235
|
+
# Verify image_id, min_count, and max_count are present as these are required
|
236
|
+
raise Exception, "image_id (ami-) required" if options[:image_id].nil? || options[:image_id].empty?
|
237
|
+
raise Exception, "min_count is required" if options[:min_count].nil?
|
238
|
+
raise Exception, "max_count is required" if options[:max_count].nil?
|
239
|
+
|
240
|
+
# Start instance(s)!
|
241
|
+
response = amazon.run_instances(options)
|
242
|
+
|
243
|
+
instance_id = response.instancesSet.item[0].instanceId
|
244
|
+
puts "Instance #{instance_id} startup in progress"
|
245
|
+
|
246
|
+
#set scope outside of block
|
247
|
+
instance = nil
|
248
|
+
|
249
|
+
#loop checking for confirmation that instance is running
|
250
|
+
tries = 0
|
251
|
+
begin
|
252
|
+
instance = amazon.describe_instances(:instance_id => instance_id)
|
253
|
+
raise "Server Not Running" unless instance.reservationSet.item[0].instancesSet.item[0].instanceState.name == "running"
|
254
|
+
puts ""
|
255
|
+
puts "Instance #{instance_id} entered state 'running'"
|
256
|
+
rescue
|
257
|
+
$stdout.print '.'
|
258
|
+
sleep(10)
|
259
|
+
tries += 1
|
260
|
+
retry unless tries == 35
|
261
|
+
raise "Instance #{instance_id} never moved to state 'running'!"
|
262
|
+
end
|
263
|
+
|
264
|
+
#loop waiting to get the public key
|
265
|
+
tries = 0
|
266
|
+
begin
|
267
|
+
require 'timeout'
|
268
|
+
begin
|
269
|
+
Timeout::timeout(5) do
|
270
|
+
system("ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no -i #{get_key_file} root@#{hostname_from_instance_id(instance_id)} echo success") or raise "SSH Auth Failure"
|
271
|
+
end
|
272
|
+
rescue Timeout::Error
|
273
|
+
raise "SSH timed out..."
|
274
|
+
end
|
275
|
+
puts ""
|
276
|
+
puts "SSH is up! Grabbing the public key..."
|
277
|
+
if system "scp -o StrictHostKeyChecking=no -i #{get_key_file} root@#{hostname_from_instance_id(instance_id)}:/mnt/openssh_id.pub #{get_key_file}.pub"
|
278
|
+
puts "Public key saved at #{get_key_file}.pub"
|
279
|
+
else
|
280
|
+
puts "Error grabbing public key"
|
281
|
+
end
|
282
|
+
rescue Exception => e
|
283
|
+
$stdout.print '.'
|
284
|
+
sleep(10)
|
285
|
+
tries += 1
|
286
|
+
retry unless tries == 35
|
287
|
+
puts "We couldn't ever SSH in!"
|
288
|
+
end
|
289
|
+
|
290
|
+
#scripts
|
291
|
+
if File.exists?(fetch(:capsize_config_dir)+"/scripts")
|
292
|
+
begin
|
293
|
+
instance = amazon.describe_instances(:instance_id => instance_id)
|
294
|
+
instance.reservationSet.item.first.groupSet.item.map { |g| g.groupId }.sort.each do |group|
|
295
|
+
script_path = fetch(:capsize_config_dir)+"/scripts/#{group}"
|
296
|
+
if File.exists?(script_path)
|
297
|
+
begin
|
298
|
+
puts "Found script for security group #{group}, running"
|
299
|
+
system("scp -o StrictHostKeyChecking=no -i #{get_key_file} #{script_path} root@#{hostname_from_instance_id(instance_id)}:/tmp/") or raise "SCP ERROR"
|
300
|
+
system("ssh -o StrictHostKeyChecking=no -i #{get_key_file} root@#{hostname_from_instance_id(instance_id)} chmod o+x /tmp/#{group}") or raise "Error changing script permissions"
|
301
|
+
system("ssh -o StrictHostKeyChecking=no -i #{get_key_file} root@#{hostname_from_instance_id(instance_id)} /tmp/#{group}") or raise "Error running script"
|
302
|
+
rescue Exception => e
|
303
|
+
puts e
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
rescue Exception => e
|
308
|
+
puts e
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
return instance
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
#reboot a running instance
|
317
|
+
def reboot_instance(options = {})
|
318
|
+
amazon = connect()
|
319
|
+
options = {:instance_id => []}.merge(options)
|
320
|
+
raise Exception, ":instance_id required" if options[:instance_id].nil?
|
321
|
+
amazon.reboot_instances(:instance_id => options[:instance_id])
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
#terminates a running instance
|
326
|
+
def terminate_instance(options = {})
|
327
|
+
amazon = connect()
|
328
|
+
options = {:instance_id => []}.merge(options)
|
329
|
+
raise Exception, ":instance_id required" if options[:instance_id].nil?
|
330
|
+
amazon.terminate_instances(:instance_id => options[:instance_id])
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
# SECURITY GROUP METHODS
|
335
|
+
#########################################
|
336
|
+
|
337
|
+
|
338
|
+
def create_security_group(options = {})
|
339
|
+
amazon = connect()
|
340
|
+
|
341
|
+
# default group_name is the same as our appname, unless specifically overriden in capsize.yml
|
342
|
+
# default group_description is set in the :group_description variable
|
343
|
+
options = {:group_name => nil, :group_description => nil}.merge(options)
|
344
|
+
|
345
|
+
options[:group_name] = options[:group_name] || get(:group_name)
|
346
|
+
options[:group_description] = options[:group_description] || get(:group_description)
|
347
|
+
|
348
|
+
raise Exception, "Group name required" if options[:group_name].nil? || options[:group_name].empty?
|
349
|
+
raise Exception, "Group description required" if options[:group_description].nil? || options[:group_description].empty?
|
350
|
+
|
351
|
+
amazon.create_security_group(:group_name => options[:group_name], :group_description => options[:group_description])
|
352
|
+
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
#describe your security groups
|
357
|
+
def describe_security_groups(options = {})
|
358
|
+
amazon = connect()
|
359
|
+
options = {:group_name => nil}.merge(options)
|
360
|
+
options[:group_name] = options[:group_name] || get(:group_name) || ""
|
361
|
+
amazon.describe_security_groups(:group_name => options[:group_name])
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
def delete_security_group(options = {})
|
366
|
+
amazon = connect()
|
367
|
+
|
368
|
+
# default group_name is the same as our appname, unless specifically overriden in capsize.yml
|
369
|
+
options = {:group_name => nil}.merge(options)
|
370
|
+
|
371
|
+
options[:group_name] = options[:group_name] || get(:group_name)
|
372
|
+
|
373
|
+
raise Exception, "Group name required" if options[:group_name].nil? || options[:group_name].empty?
|
374
|
+
|
375
|
+
amazon.delete_security_group(:group_name => options[:group_name])
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
# Define firewall access rules for a specific security group. Instances will inherit
|
381
|
+
# the security group permissions based on the group they are assigned to.
|
382
|
+
def authorize_ingress(options = {})
|
383
|
+
amazon = connect()
|
384
|
+
|
385
|
+
options = { :group_name => nil,
|
386
|
+
:ip_protocol => get(:ip_protocol),
|
387
|
+
:from_port => get(:from_port),
|
388
|
+
:to_port => get(:to_port),
|
389
|
+
:cidr_ip => get(:cidr_ip),
|
390
|
+
:source_security_group_name => get(:source_security_group_name),
|
391
|
+
:source_security_group_owner_id => get(:source_security_group_owner_id) }.merge(options)
|
392
|
+
|
393
|
+
options[:group_name] = options[:group_name] || get(:group_name)
|
394
|
+
|
395
|
+
# Verify only that :group_name is passed. This is the only REQUIRED parameter.
|
396
|
+
# The others are optional and depend on what it is you are trying to
|
397
|
+
# do (CIDR based permissions vs. user/group pair permissions). We let the EC2
|
398
|
+
# service itself do the validations on the extra params and count on it to raise an exception
|
399
|
+
# if it doesn't like the options passed. We'll see an EC2::Exception class returned if so.
|
400
|
+
raise Exception, "You must specify a :group_name" if options[:group_name].nil? || options[:group_name].empty?
|
401
|
+
|
402
|
+
# set the :to_port to the same value as :from_port if :to_port was not explicitly defined.
|
403
|
+
unless options[:from_port].nil? || options[:from_port].empty?
|
404
|
+
set :to_port, options[:from_port] if options[:to_port].nil? || options[:to_port].empty?
|
405
|
+
options[:to_port] = to_port if options[:to_port].nil? || options[:to_port].empty?
|
406
|
+
end
|
407
|
+
|
408
|
+
#if source_security_group_name and source_security_group_owner_id are specified, unset the incompatible options
|
409
|
+
if !options[:source_security_group_name].nil? && !options[:source_security_group_owner_id].nil?
|
410
|
+
options.delete(:ip_protocol)
|
411
|
+
options.delete(:from_port)
|
412
|
+
options.delete(:to_port)
|
413
|
+
options.delete(:cidr_ip)
|
414
|
+
end
|
415
|
+
|
416
|
+
amazon.authorize_security_group_ingress(options)
|
417
|
+
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
# Revoke firewall access rules for a specific security group. Instances will inherit
|
422
|
+
# the security group permissions based on the group they are assigned to.
|
423
|
+
def revoke_ingress(options = {})
|
424
|
+
amazon = connect()
|
425
|
+
|
426
|
+
options = { :group_name => nil,
|
427
|
+
:ip_protocol => get(:ip_protocol),
|
428
|
+
:from_port => get(:from_port),
|
429
|
+
:to_port => get(:to_port),
|
430
|
+
:cidr_ip => get(:cidr_ip),
|
431
|
+
:source_security_group_name => get(:source_security_group_name),
|
432
|
+
:source_security_group_owner_id => get(:source_security_group_owner_id) }.merge(options)
|
433
|
+
|
434
|
+
options[:group_name] = options[:group_name] || get(:group_name)
|
435
|
+
|
436
|
+
# Verify only that :group_name is passed. This is the only REQUIRED parameter.
|
437
|
+
# The others are optional and depend on what it is you are trying to
|
438
|
+
# do (CIDR based permissions vs. user/group pair permissions). We let the EC2
|
439
|
+
# service itself do the validations on the extra params and count on it to raise an exception
|
440
|
+
# if it doesn't like the options passed. We'll see an EC2::Exception class returned if so.
|
441
|
+
raise Exception, "You must specify a :group_name" if options[:group_name].nil? || options[:group_name].empty?
|
442
|
+
|
443
|
+
# set the :to_port to the same value as :from_port if :to_port was not explicitly defined.
|
444
|
+
unless options[:from_port].nil? || options[:from_port].empty?
|
445
|
+
set :to_port, options[:from_port] if options[:to_port].nil? || options[:to_port].empty?
|
446
|
+
options[:to_port] = to_port if options[:to_port].nil? || options[:to_port].empty?
|
447
|
+
end
|
448
|
+
|
449
|
+
#if source_security_group_name and source_security_group_owner_id are specified, unset the incompatible options
|
450
|
+
if !options[:source_security_group_name].nil? && !options[:source_security_group_owner_id].nil?
|
451
|
+
options.delete(:ip_protocol)
|
452
|
+
options.delete(:from_port)
|
453
|
+
options.delete(:to_port)
|
454
|
+
options.delete(:cidr_ip)
|
455
|
+
end
|
456
|
+
|
457
|
+
amazon.revoke_security_group_ingress(options)
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
# ELASTIC IP ADDRESS METHODS
|
462
|
+
#########################################
|
463
|
+
|
464
|
+
# returns information about elastic IP addresses owned by the user
|
465
|
+
def describe_addresses(options = {})
|
466
|
+
amazon = connect()
|
467
|
+
options = {:public_ip => []}.merge(options)
|
468
|
+
amazon.describe_addresses(:public_ip => options[:public_ip])
|
469
|
+
end
|
470
|
+
|
471
|
+
# allocate an elastic IP address for use with this account
|
472
|
+
def allocate_address
|
473
|
+
amazon = connect()
|
474
|
+
amazon.allocate_address
|
475
|
+
end
|
476
|
+
|
477
|
+
# release an elastic IP address from this account
|
478
|
+
def release_address(options)
|
479
|
+
amazon = connect()
|
480
|
+
amazon.release_address(:public_ip => options[:public_ip])
|
481
|
+
end
|
482
|
+
|
483
|
+
# associate an elastic IP address to an instance
|
484
|
+
def associate_address(options)
|
485
|
+
amazon = connect()
|
486
|
+
amazon.associate_address(:public_ip => options[:public_ip], :instance_id => options[:instance_id])
|
487
|
+
end
|
488
|
+
|
489
|
+
# disassociate an elastic IP address from whatever instance it may be assigned to
|
490
|
+
def disassociate_address(options)
|
491
|
+
amazon = connect()
|
492
|
+
amazon.disassociate_address(:public_ip => options[:public_ip])
|
493
|
+
end
|
494
|
+
|
495
|
+
|
496
|
+
# CAPSIZE HELPER METHODS
|
497
|
+
#########################################
|
498
|
+
# call these from tasks.rb with 'capsize.method_name'
|
499
|
+
# returns an EC2::Base object
|
500
|
+
def connect()
|
501
|
+
|
502
|
+
# get the :use_ssl value from the config pool and set it if its available
|
503
|
+
# this will allow users to globally override whether or not their connection
|
504
|
+
# is made via SSL in their config files or deploy.rb. Of course default to using SSL.
|
505
|
+
case get(:use_ssl)
|
506
|
+
when true, nil
|
507
|
+
set :use_ssl, true
|
508
|
+
when false
|
509
|
+
set :use_ssl, false
|
510
|
+
else
|
511
|
+
raise Exception, "You have an invalid value in your config for :use_ssl. Must be 'true' or 'false'."
|
512
|
+
end
|
513
|
+
|
514
|
+
# Optimized so we don't read the config files six times just to connect.
|
515
|
+
# Read once, set it, and re-use what we get back...
|
516
|
+
set :aws_access_key_id, get(:aws_access_key_id)
|
517
|
+
set :aws_secret_access_key, get(:aws_secret_access_key)
|
518
|
+
|
519
|
+
raise Exception, "You must have an :aws_access_key_id defined in your config." if fetch(:aws_access_key_id).nil? || fetch(:aws_access_key_id).empty?
|
520
|
+
raise Exception, "You must have an :aws_secret_access_key defined in your config." if fetch(:aws_secret_access_key).nil? || fetch(:aws_secret_access_key).empty?
|
521
|
+
|
522
|
+
begin
|
523
|
+
return amazon = EC2::Base.new(:access_key_id => get(:aws_access_key_id), :secret_access_key => get(:aws_secret_access_key), :use_ssl => use_ssl)
|
524
|
+
rescue Exception => e
|
525
|
+
puts "Your EC2::Base authentication setup failed with the following message : " + e
|
526
|
+
raise e
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# TODO : Finish this...
|
531
|
+
# accept a Response object and provide screen output of the key data from
|
532
|
+
# this response that needs to be permanently added to the users deploy.rb
|
533
|
+
# and/or Capsize config files.
|
534
|
+
def print_config_instructions(response = nil)
|
535
|
+
|
536
|
+
raise Exception, "run_instances Response object expected" if response.nil?
|
537
|
+
|
538
|
+
dns_name = response.reservationSet.item[0].instancesSet.item[0].dnsName
|
539
|
+
|
540
|
+
puts "\n\nConfiguration Instructions:\n"
|
541
|
+
|
542
|
+
config_help <<-HELP
|
543
|
+
In order to control this new server instance from Capsize and Capistrano in the
|
544
|
+
future you will need to store some critical instance information in your
|
545
|
+
deploy.rb configuration file. Please add something like the following to
|
546
|
+
the appropriate places in your config/deploy.rb file. Of course you may need to
|
547
|
+
modify this information to suite your circumstances, this is only an example.
|
548
|
+
\n\n
|
549
|
+
config/deploy.rb
|
550
|
+
--
|
551
|
+
HELP
|
552
|
+
|
553
|
+
puts config_help
|
554
|
+
|
555
|
+
puts "role :app, #{dns_name}"
|
556
|
+
puts "role :web, #{dns_name}"
|
557
|
+
puts "role :db, #{dns_name}, :primary => true"
|
558
|
+
|
559
|
+
end
|
560
|
+
|
561
|
+
# Keeping DRY. This is called from run instances and describe instances.
|
562
|
+
def print_instance_description(result = nil)
|
563
|
+
puts "" if result.nil?
|
564
|
+
unless result.reservationSet.nil?
|
565
|
+
result.reservationSet.item.each do |reservation|
|
566
|
+
puts "reservationSet:reservationId = " + reservation.reservationId
|
567
|
+
puts "reservationSet:ownerId = " + reservation.ownerId
|
568
|
+
|
569
|
+
unless reservation.groupSet.nil?
|
570
|
+
reservation.groupSet.item.each do |group|
|
571
|
+
puts " groupSet:groupId = " + group.groupId unless group.groupId.nil?
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
unless reservation.instancesSet.nil?
|
576
|
+
reservation.instancesSet.item.each do |instance|
|
577
|
+
puts " instancesSet:instanceId = " + instance.instanceId unless instance.instanceId.nil?
|
578
|
+
puts " instancesSet:instanceType = " + instance.instanceType unless instance.instanceType.nil?
|
579
|
+
puts " instancesSet:imageId = " + instance.imageId unless instance.imageId.nil?
|
580
|
+
puts " instancesSet:privateDnsName = " + instance.privateDnsName unless instance.privateDnsName.nil?
|
581
|
+
puts " instancesSet:dnsName = " + instance.dnsName unless instance.dnsName.nil?
|
582
|
+
puts " instancesSet:reason = " + instance.reason unless instance.reason.nil?
|
583
|
+
puts " instancesSet:launchTime = " + instance.launchTime unless instance.launchTime.nil?
|
584
|
+
puts " instancesSet:amiLaunchIndex = " + instance.amiLaunchIndex
|
585
|
+
|
586
|
+
unless instance.instanceState.nil?
|
587
|
+
puts " instanceState:code = " + instance.instanceState.code
|
588
|
+
puts " instanceState:name = " + instance.instanceState.name
|
589
|
+
end
|
590
|
+
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
|
595
|
+
puts ""
|
596
|
+
end
|
597
|
+
else
|
598
|
+
puts "You don't own any running or pending instances"
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
# print the result of an describe_addresses
|
603
|
+
def print_address_description(result = nil)
|
604
|
+
puts "" if result.nil?
|
605
|
+
unless result.addressesSet.nil?
|
606
|
+
result.addressesSet.item.each do |item|
|
607
|
+
puts "addressesSet:publicIp = " + item.publicIp unless item.publicIp.nil?
|
608
|
+
puts "addressesSet:instanceId = " + (item.instanceId ? item.instanceId : '(unassociated)')
|
609
|
+
puts ""
|
610
|
+
end
|
611
|
+
else
|
612
|
+
puts "You don't have any elastic IP addresses. Run 'cap ec2:addresses:allocate' to acquire one."
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
end
|
617
|
+
end
|
618
|
+
Capistrano.plugin :capsize_ec2, Capsize::CapsizeEC2
|