poolparty 1.4.6 → 1.4.7
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +2 -2
- data/bin/cloud-contract +6 -1
- data/bin/cloud-list +5 -6
- data/bin/cloud-vnc +39 -0
- data/examples/rds_cloud.rb +44 -0
- data/lib/cloud_providers/connections.rb +0 -1
- data/lib/cloud_providers/ec2/ec2.rb +86 -25
- data/lib/cloud_providers/ec2/ec2_instance.rb +6 -2
- data/lib/cloud_providers/ec2/helpers/ec2_helper.rb +19 -6
- data/lib/cloud_providers/ec2/helpers/elastic_load_balancer.rb +3 -3
- data/lib/cloud_providers/ec2/helpers/rds_instance.rb +110 -0
- data/lib/cloud_providers/remote_instance.rb +5 -3
- data/lib/poolparty/cloud.rb +8 -3
- data/lib/poolparty/pool.rb +12 -0
- data/test/fixtures/clouds/rds_cloud.rb +14 -0
- data/test/fixtures/clouds/rds_missing_params.rb +11 -0
- data/test/fixtures/ec2/ec2-describe-security-groups_response_body.xml +23 -0
- data/test/fixtures/ec2/rds-describe-db-instances-empty_response_body.xml +9 -0
- data/test/lib/poolparty/rds_test.rb +35 -0
- data/test/test_helper.rb +4 -4
- data/vendor/gems/amazon-ec2/ChangeLog +15 -0
- data/vendor/gems/amazon-ec2/README.rdoc +27 -24
- data/vendor/gems/amazon-ec2/README_dev.rdoc +0 -2
- data/vendor/gems/amazon-ec2/Rakefile +8 -39
- data/vendor/gems/amazon-ec2/VERSION +1 -1
- data/vendor/gems/amazon-ec2/amazon-ec2.gemspec +24 -16
- data/vendor/gems/amazon-ec2/bin/setup.rb +1 -0
- data/vendor/gems/amazon-ec2/lib/AWS/Autoscaling/autoscaling.rb +6 -16
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/availability_zones.rb +8 -0
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/console.rb +2 -0
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/devpay.rb +18 -0
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/elastic_ips.rb +37 -32
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/images.rb +43 -27
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/instances.rb +136 -99
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/keypairs.rb +3 -17
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/security_groups.rb +4 -23
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/snapshots.rb +38 -17
- data/vendor/gems/amazon-ec2/lib/AWS/EC2/volumes.rb +6 -21
- data/vendor/gems/amazon-ec2/lib/AWS/EC2.rb +2 -2
- data/vendor/gems/amazon-ec2/lib/AWS/ELB/load_balancers.rb +11 -38
- data/vendor/gems/amazon-ec2/lib/AWS/RDS/rds.rb +522 -0
- data/vendor/gems/amazon-ec2/lib/AWS/RDS.rb +73 -0
- data/vendor/gems/amazon-ec2/lib/AWS/exceptions.rb +103 -25
- data/vendor/gems/amazon-ec2/lib/AWS.rb +19 -9
- data/vendor/gems/amazon-ec2/perftools/ec2prof +0 -0
- data/vendor/gems/amazon-ec2/perftools/ec2prof-results.dot +130 -191
- data/vendor/gems/amazon-ec2/perftools/ec2prof-results.txt +100 -126
- data/vendor/gems/amazon-ec2/perftools/ec2prof.symbols +102 -129
- data/vendor/gems/amazon-ec2/test/test_Autoscaling_groups.rb +3 -2
- data/vendor/gems/amazon-ec2/test/test_EC2_images.rb +32 -0
- data/vendor/gems/amazon-ec2/test/test_EC2_instances.rb +204 -22
- data/vendor/gems/amazon-ec2/test/test_EC2_snapshots.rb +1 -1
- data/vendor/gems/amazon-ec2/test/test_ELB_load_balancers.rb +2 -2
- data/vendor/gems/amazon-ec2/test/test_RDS.rb +354 -0
- data/vendor/gems/amazon-ec2/wsdl/2009-10-31.ec2.wsdl +4261 -0
- data/vendor/gems/amazon-ec2/wsdl/2009-11-30.ec2.wsdl +4668 -0
- metadata +17 -2
data/VERSION.yml
CHANGED
data/bin/cloud-contract
CHANGED
@@ -19,6 +19,11 @@ EOS
|
|
19
19
|
node_to_terminate = cld.nodes.last
|
20
20
|
# node_to_terminate.cloud = cld
|
21
21
|
|
22
|
+
puts cld.nodes.size
|
23
|
+
if cld.nodes.size - 1 < cld.minimum_instances
|
24
|
+
puts "Contracting instances by 1 in cloud #{cld.name} will lower the number of instances below specified minimum (#{cld.minimum_instances})"
|
25
|
+
next
|
26
|
+
end
|
22
27
|
msg = [
|
23
28
|
"Contracting cloud #{cld.name} (#{cld.keypair}) by 1",
|
24
29
|
"#{cld.nodes.size} running instances (#{cld.minimum_instances} - #{cld.maximum_instances})",
|
@@ -32,4 +37,4 @@ EOS
|
|
32
37
|
puts "#{node_to_terminate.public_ip} has been terminated"
|
33
38
|
end
|
34
39
|
end
|
35
|
-
end
|
40
|
+
end
|
data/bin/cloud-list
CHANGED
@@ -17,13 +17,14 @@ EOS
|
|
17
17
|
short_desc "show a list of the current instances on the clouds"
|
18
18
|
|
19
19
|
run do |command|
|
20
|
+
puts "#{pool.name}"
|
21
|
+
puts "==="
|
20
22
|
@loaded_clouds.each do |cld|
|
21
23
|
if command[:instance_id]
|
22
24
|
require 'pp'
|
23
25
|
pp result = cld.nodes(:instance_id=>command[:instance_id])
|
24
26
|
else
|
25
|
-
|
26
|
-
msg = ["Listing cloud #{cld.name}"]
|
27
|
+
msg = ["* #{cld.name} cloud, #{cld.image_id}, #{cld.instance_type}"]
|
27
28
|
|
28
29
|
hsh = {}
|
29
30
|
|
@@ -32,14 +33,12 @@ EOS
|
|
32
33
|
end
|
33
34
|
|
34
35
|
hsh.each do |k,v|
|
35
|
-
msg << "#{k
|
36
|
-
msg << line
|
36
|
+
msg << " - #{k} nodes"
|
37
37
|
v.each do |a|
|
38
|
-
msg << "
|
38
|
+
msg << " - #{a.instance_id}\t#{a.status}\t#{a.public_ip}\t#{a.private_ip}\t#{a.zone}"
|
39
39
|
end
|
40
40
|
msg << ""
|
41
41
|
end
|
42
|
-
|
43
42
|
puts msg
|
44
43
|
end
|
45
44
|
|
data/bin/cloud-vnc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib")
|
3
|
+
require "poolparty"
|
4
|
+
|
5
|
+
require 'git-style-binary/command'
|
6
|
+
|
7
|
+
GitStyleBinary.command do
|
8
|
+
banner <<-EOS
|
9
|
+
Usage: #{$0} #{all_options_string}
|
10
|
+
|
11
|
+
start a vnc server and create an ssh tunnel
|
12
|
+
EOS
|
13
|
+
|
14
|
+
short_desc "ssh into an instance of the cloud"
|
15
|
+
|
16
|
+
opt :instance_number, "Start VNC server on this instance number", :type => :integer, :default => 0
|
17
|
+
opt :local_port, "The local port to tunnel to", :type => :integer, :default => 5900
|
18
|
+
opt :display, "The remote display number to start the VNC Server on", :type => :integer, :default => 0
|
19
|
+
|
20
|
+
run do |command|
|
21
|
+
cloud = @loaded_clouds.first
|
22
|
+
|
23
|
+
if !cloud.nodes.empty?
|
24
|
+
inst = cloud.nodes[command[:instance_number]]
|
25
|
+
if inst
|
26
|
+
inst.ssh(["x11vnc -localhost -nopw -display :#{command[:display]}"],
|
27
|
+
{'-X' => nil,
|
28
|
+
'-C' => nil,
|
29
|
+
' -o' => 'UserKnownHostsFile=/dev/null',
|
30
|
+
'-L' => "#{command[:local_port]}:localhost:5900"})
|
31
|
+
else
|
32
|
+
puts("Error: The instance number (#{command[:instance_number]}) is too high for the current number of instances")
|
33
|
+
end
|
34
|
+
else
|
35
|
+
puts "No running instances can be found"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# example pool with RDS enabled
|
2
|
+
|
3
|
+
pool :poolparty do
|
4
|
+
cloud :app do
|
5
|
+
using :ec2
|
6
|
+
|
7
|
+
# this block will create an RDS DB instance
|
8
|
+
# by default, the instance id will match the containing cloud
|
9
|
+
# ("poolparty-app" in this example)
|
10
|
+
rds do
|
11
|
+
username "admin" # required
|
12
|
+
password "secret" # required
|
13
|
+
|
14
|
+
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
15
|
+
# these properties have the following overridable defaults:
|
16
|
+
#
|
17
|
+
# storage 5
|
18
|
+
# instance_class "db.m1.small"
|
19
|
+
# engine "db.m1.small"
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
24
|
+
# by default, a user DB will be created with a name that matches the containing cloud...
|
25
|
+
# ...except RDS does not allow hyphens in DB names, so it will be pool name *underscore* cloud name
|
26
|
+
# ("poolparty_app" in this example).
|
27
|
+
#
|
28
|
+
# to override this, use the database method, like so:
|
29
|
+
# database :db1
|
30
|
+
# - or -
|
31
|
+
# databases :production, :staging # :databases is aliased to :database for nice DSL reading. :)
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
36
|
+
# You are required to explicitly open access to the DB server to IP addresses (for outside EC2) or security groups (for inside EC2)
|
37
|
+
# access will be granted to your containing cloud's security groups unless you explicitly list security group authorization.
|
38
|
+
#
|
39
|
+
# to override, use the :authorize method, like so:
|
40
|
+
# authorize :networks => "1.2.3.4/24" # <--- will authorize access to this CIDR block *in addition to* your EC2 security group
|
41
|
+
# authorize :networks => ["1.2.3.4/32", "10.10.10.10/24"], :security_groups => "my_ec2_group"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -31,7 +31,6 @@ module CloudProviders
|
|
31
31
|
def ssh( commands=[], extra_ssh_ops={})
|
32
32
|
commands = commands.compact.join(' && ') if commands.is_a?(Array)
|
33
33
|
cmd_string = "ssh #{user}@#{host} #{ssh_options(extra_ssh_ops)}"
|
34
|
-
p commands
|
35
34
|
if commands.empty?
|
36
35
|
#TODO: replace this with a IO.popen call with read_nonblocking to show progress, and accept input
|
37
36
|
Kernel.system(cmd_string)
|
@@ -14,11 +14,11 @@ module CloudProviders
|
|
14
14
|
class Ec2 < CloudProvider
|
15
15
|
# Set the aws keys from the environment, or load from /etc/poolparty/env.yml if the environment variable is not set
|
16
16
|
def self.default_access_key
|
17
|
-
ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key]
|
17
|
+
ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key] || load_keys_from_credential_file[:access_key]
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.default_secret_access_key
|
21
|
-
ENV['EC2_SECRET_KEY'] || load_keys_from_file[:secret_access_key]
|
21
|
+
ENV['EC2_SECRET_KEY'] || load_keys_from_file[:secret_access_key] || load_keys_from_credential_file[:secret_access_key]
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.default_private_key
|
@@ -42,7 +42,11 @@ module CloudProviders
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.default_cloud_cert
|
45
|
-
|
45
|
+
ENV['CLOUD_CERT'] || ENV['EUCALYPTUS_CERT'] || load_keys_from_file[:cloud_cert]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.default_credential_file
|
49
|
+
ENV['AWS_CREDENTIAL_FILE'] || load_keys_from_file[:credential_file]
|
46
50
|
end
|
47
51
|
|
48
52
|
# Load the yaml file containing keys. If the file does not exist, return an empty hash
|
@@ -52,6 +56,22 @@ module CloudProviders
|
|
52
56
|
puts("Reading keys from file: #{filename}")
|
53
57
|
@aws_yml = YAML::load( open(filename).read ) || {}
|
54
58
|
end
|
59
|
+
|
60
|
+
# Load credentials from file
|
61
|
+
def self.load_keys_from_credential_file(filename=default_credential_file, caching=true)
|
62
|
+
return {:access_key => @access_key, :secret_access_key => @secret_access_key} if @access_key and @secret_access_key
|
63
|
+
return {} if filename.nil? or not File.exists?(filename)
|
64
|
+
puts("Reading keys from file: #{filename}")
|
65
|
+
File.open(filename).each_line {|line|
|
66
|
+
if line =~ /AWSAccessKeyId=([a-zA-Z0-9]+)$/
|
67
|
+
@access_key=$1.chomp
|
68
|
+
elsif line =~ /AWSSecretKey=([^ ]+)$/
|
69
|
+
@secret_access_key=$1.chomp
|
70
|
+
end
|
71
|
+
}
|
72
|
+
return {:access_key => @access_key, :secret_access_key => @secret_access_key}
|
73
|
+
end
|
74
|
+
|
55
75
|
|
56
76
|
default_options(
|
57
77
|
:instance_type => 'm1.small',
|
@@ -65,6 +85,7 @@ module CloudProviders
|
|
65
85
|
:secret_access_key => default_secret_access_key,
|
66
86
|
:ec2_url => default_ec2_url,
|
67
87
|
:s3_url => default_s3_url,
|
88
|
+
:credential_file => default_credential_file,
|
68
89
|
:min_count => 1,
|
69
90
|
:max_count => 1,
|
70
91
|
:user_data => '',
|
@@ -74,33 +95,40 @@ module CloudProviders
|
|
74
95
|
:block_device_mappings => nil,
|
75
96
|
:ebs_volumes => [] # The volume id of an ebs volume # TODO: ensure this is consistent with :block_device_mappings
|
76
97
|
)
|
77
|
-
|
98
|
+
|
78
99
|
# Called when the create command is called on the cloud
|
79
100
|
def create!
|
80
|
-
[:security_groups, :load_balancers].each do |type|
|
101
|
+
[:security_groups, :load_balancers, :rds_instances].each do |type|
|
81
102
|
self.send(type).each {|ele| ele.create! }
|
82
103
|
end
|
83
104
|
end
|
84
|
-
|
85
|
-
def run
|
105
|
+
|
106
|
+
def run
|
86
107
|
puts " for cloud: #{cloud.name}"
|
87
108
|
puts " minimum_instances: #{minimum_instances}"
|
88
109
|
puts " maximum_instances: #{maximum_instances}"
|
89
110
|
puts " security_groups: #{security_group_names.join(", ")}"
|
90
111
|
puts " using keypair: #{keypair}"
|
91
112
|
puts " user: #{user}\n"
|
92
|
-
|
113
|
+
|
93
114
|
security_groups.each do |sg|
|
94
115
|
sg.run
|
95
116
|
end
|
96
|
-
|
117
|
+
|
97
118
|
unless load_balancers.empty?
|
98
119
|
load_balancers.each do |lb|
|
99
120
|
puts " load balancer: #{lb.name}"
|
100
121
|
lb.run
|
101
122
|
end
|
102
123
|
end
|
103
|
-
|
124
|
+
|
125
|
+
unless rds_instances.empty?
|
126
|
+
rds_instances.each do |rdsi|
|
127
|
+
puts " rds instance: #{rdsi.name}"
|
128
|
+
rdsi.run
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
104
132
|
if autoscalers.empty? # not using autoscaling
|
105
133
|
puts "---- live, running instances (#{nodes.size}) ----"
|
106
134
|
if nodes.size < minimum_instances
|
@@ -191,6 +219,7 @@ module CloudProviders
|
|
191
219
|
end
|
192
220
|
|
193
221
|
def contract_by(num=1)
|
222
|
+
raise RuntimeError, "Contracting instances by #{num} will lower the number of instances below specified minimum" unless nodes.size - num > minimum_instances
|
194
223
|
num.times do |i|
|
195
224
|
id = nodes[-num].instance_id
|
196
225
|
Ec2Instance.terminate!(:instance_id => id, :cloud => cloud)
|
@@ -248,9 +277,7 @@ module CloudProviders
|
|
248
277
|
end
|
249
278
|
|
250
279
|
def all_nodes
|
251
|
-
|
252
|
-
#
|
253
|
-
@nodes ||= describe_instances.select {|i| security_group_names.include?(i.security_groups) }
|
280
|
+
@nodes ||= describe_instances.select {|i| security_group_names.include?(i.security_groups) }.sort {|a,b| DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime)}
|
254
281
|
end
|
255
282
|
|
256
283
|
# Describe instances
|
@@ -266,6 +293,9 @@ module CloudProviders
|
|
266
293
|
Ec2Instance.new(inst_options)
|
267
294
|
end
|
268
295
|
end.flatten
|
296
|
+
rescue AWS::InvalidClientTokenId => e # AWS credentials invalid
|
297
|
+
puts "Error contacting AWS: #{e}"
|
298
|
+
raise e
|
269
299
|
rescue Exception => e
|
270
300
|
[]
|
271
301
|
end
|
@@ -285,21 +315,37 @@ module CloudProviders
|
|
285
315
|
def elastic_ip(*ips)
|
286
316
|
ips.each {|ip| elastic_ips << ip}
|
287
317
|
end
|
288
|
-
|
318
|
+
|
319
|
+
def rds(given_name=cloud.proper_name, o={}, &block)
|
320
|
+
rds_instances << RdsInstance.new(given_name, sub_opts.merge(o || {}), &block)
|
321
|
+
end
|
322
|
+
|
289
323
|
# Proxy to the raw Grempe amazon-aws @ec2 instance
|
290
324
|
def ec2
|
291
|
-
@ec2 ||=
|
325
|
+
@ec2 ||= begin
|
326
|
+
AWS::EC2::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
|
327
|
+
rescue AWS::ArgumentError => e # AWS credentials missing?
|
328
|
+
puts "Error contacting AWS: #{e}"
|
329
|
+
raise e
|
330
|
+
rescue Exception => e
|
331
|
+
puts "Generic error #{e.class}: #{e}"
|
332
|
+
end
|
292
333
|
end
|
293
|
-
|
334
|
+
|
294
335
|
# Proxy to the raw Grempe amazon-aws autoscaling instance
|
295
336
|
def as
|
296
337
|
@as = AWS::Autoscaling::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
|
297
338
|
end
|
298
|
-
|
339
|
+
|
299
340
|
# Proxy to the raw Grempe amazon-aws elastic_load_balancing instance
|
300
341
|
def elb
|
301
342
|
@elb ||= AWS::ELB::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
|
302
343
|
end
|
344
|
+
|
345
|
+
def awsrds
|
346
|
+
@awsrds ||= AWS::RDS::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
|
347
|
+
end
|
348
|
+
|
303
349
|
def security_group_names
|
304
350
|
security_groups.map {|a| a.to_s }
|
305
351
|
end
|
@@ -315,11 +361,25 @@ module CloudProviders
|
|
315
361
|
def elastic_ips
|
316
362
|
@elastic_ips ||= []
|
317
363
|
end
|
318
|
-
|
364
|
+
|
365
|
+
def rds_instances
|
366
|
+
@rds_instances ||= []
|
367
|
+
end
|
368
|
+
|
319
369
|
# Clear the cache
|
320
370
|
def reset!
|
321
371
|
@nodes = @describe_instances = nil
|
322
372
|
end
|
373
|
+
|
374
|
+
# Read credentials from credential_file if one exists
|
375
|
+
def credential_file(file=nil)
|
376
|
+
unless file.nil?
|
377
|
+
dsl_options[:credential_file]=file
|
378
|
+
dsl_options.merge((Ec2.load_keys_from_credential_file(file)))
|
379
|
+
else
|
380
|
+
fetch(:credential_file)
|
381
|
+
end
|
382
|
+
end
|
323
383
|
|
324
384
|
private
|
325
385
|
# Helper to get the options with self as parent
|
@@ -341,18 +401,19 @@ module CloudProviders
|
|
341
401
|
end
|
342
402
|
keypair n
|
343
403
|
end
|
344
|
-
|
404
|
+
|
345
405
|
end
|
346
406
|
end
|
347
407
|
|
348
408
|
require "#{File.dirname(__FILE__)}/ec2_instance"
|
349
409
|
require "#{File.dirname(__FILE__)}/helpers/ec2_helper"
|
350
|
-
%w( security_group
|
351
|
-
authorize
|
352
|
-
elastic_auto_scaler
|
353
|
-
elastic_block_store
|
354
|
-
elastic_load_balancer
|
410
|
+
%w( security_group
|
411
|
+
authorize
|
412
|
+
elastic_auto_scaler
|
413
|
+
elastic_block_store
|
414
|
+
elastic_load_balancer
|
355
415
|
elastic_ip
|
416
|
+
rds_instance
|
356
417
|
revoke).each do |lib|
|
357
418
|
require "#{File.dirname(__FILE__)}/helpers/#{lib}"
|
358
|
-
end
|
419
|
+
end
|
@@ -12,7 +12,7 @@ module CloudProviders
|
|
12
12
|
:availability_zones => []
|
13
13
|
)
|
14
14
|
|
15
|
-
def initialize(raw_response={})
|
15
|
+
def initialize(raw_response={})
|
16
16
|
@raw_response = raw_response
|
17
17
|
self.instance_id = raw_response["instanceId"] rescue nil
|
18
18
|
self.security_groups = raw_response.groupSet.item[0].groupId rescue nil
|
@@ -32,6 +32,10 @@ module CloudProviders
|
|
32
32
|
@keypair ||= Keypair.new(self.key_name)
|
33
33
|
end
|
34
34
|
|
35
|
+
def zone
|
36
|
+
availability_zones.first
|
37
|
+
end
|
38
|
+
|
35
39
|
def reachable?
|
36
40
|
ping_port self.public_ip, 22
|
37
41
|
end
|
@@ -45,7 +49,7 @@ module CloudProviders
|
|
45
49
|
:min_count => min_count,
|
46
50
|
:max_count => max_count,
|
47
51
|
:key_name => keypair.basename,
|
48
|
-
:
|
52
|
+
:security_group => cloud.security_group_names,
|
49
53
|
:user_data => user_data,
|
50
54
|
:instance_type => instance_type,
|
51
55
|
:availability_zone => availability_zone,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module CloudProviders
|
2
2
|
class Ec2Helper < CloudProvider
|
3
|
-
|
3
|
+
|
4
4
|
def initialize(name=cloud.proper_name, init_opts={}, &block)
|
5
5
|
@name = name
|
6
6
|
if name.is_a?(Hash)
|
@@ -13,22 +13,35 @@ module CloudProviders
|
|
13
13
|
instance_eval &block if block
|
14
14
|
after_initialized
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def elb
|
18
18
|
cloud.elb
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def ec2
|
22
22
|
cloud.ec2
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def as
|
26
26
|
cloud.as
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
def rds
|
30
|
+
cloud.awsrds
|
31
|
+
end
|
32
|
+
|
29
33
|
def pool
|
30
34
|
cloud.parent
|
31
35
|
end
|
32
|
-
|
36
|
+
|
37
|
+
def self.property(*names)
|
38
|
+
names.each do |name|
|
39
|
+
define_method name do |*args|
|
40
|
+
instance_variable_set("@#{name}", args.first) unless args.empty?
|
41
|
+
instance_variable_get("@#{name}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
33
46
|
end
|
34
47
|
end
|
@@ -21,7 +21,7 @@ module CloudProviders
|
|
21
21
|
# Remove old nodes that are no longer alive
|
22
22
|
detach_instances_if_necessary
|
23
23
|
# Try to unregister and reregister nodes that are out of service, perhaps it was just a setup bug that the setup took too long
|
24
|
-
out_of_service_node_listing = instance_healths.select {|a| a[:state] == "OutOfService" }.map {|a|
|
24
|
+
out_of_service_node_listing = instance_healths.select {|a| a[:state] == "OutOfService" }.map {|a| a[:instance_id] }
|
25
25
|
reset!
|
26
26
|
out_of_service_nodes = nodes.select {|n| out_of_service_node_listing.include?(n.instance_id)}
|
27
27
|
unless out_of_service_nodes.empty?
|
@@ -63,13 +63,13 @@ module CloudProviders
|
|
63
63
|
public
|
64
64
|
def attach_instances_if_necessary
|
65
65
|
parent.reset!
|
66
|
-
instances = parent.nodes.map {|a|
|
66
|
+
instances = parent.nodes.map {|a| a.instance_id } # ec2 gem requires this be an array of names
|
67
67
|
elb.register_instances_with_load_balancer(:instances => instances, :load_balancer_name => "#{name}") unless instances.empty?
|
68
68
|
end
|
69
69
|
def detach_instances_if_necessary
|
70
70
|
parent.reset!
|
71
71
|
begin
|
72
|
-
instances = parent.all_nodes.select {|a| !a.running? }.map {|a|
|
72
|
+
instances = parent.all_nodes.select {|a| !a.running? }.map {|a| a.instance_id }
|
73
73
|
elb.deregister_instances_from_load_balancer(:instances => instances, :load_balancer_name => "#{name}") unless instances.empty?
|
74
74
|
rescue Exception => e
|
75
75
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module CloudProviders
|
2
|
+
class RdsInstance < Ec2Helper
|
3
|
+
property :username, :password, :storage, :instance_class, :engine
|
4
|
+
|
5
|
+
def authorize(options)
|
6
|
+
options.each do |key, value|
|
7
|
+
authorizations[key.to_s] = value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def database(*db_names)
|
12
|
+
@databases ||= []
|
13
|
+
@databases = @databases + db_names unless db_names.empty?
|
14
|
+
@databases
|
15
|
+
end
|
16
|
+
alias_method :databases, :database
|
17
|
+
|
18
|
+
def create!
|
19
|
+
if should_create_rds_instance?
|
20
|
+
puts "-----> Creating RDS Instance: #{instance_id}"
|
21
|
+
create_rds_instance!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
create! # Just for now, while we migrate to 2 commands
|
27
|
+
authorize_access
|
28
|
+
# TODO : wait until accessible?
|
29
|
+
end
|
30
|
+
|
31
|
+
def teardown
|
32
|
+
puts "-----> Tearing down RDS Instance: #{instance_id}"
|
33
|
+
delete_rds_instance!
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def authorizations
|
39
|
+
@authorizations ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def after_initialized
|
43
|
+
raise "username must be specified" if self.username.nil?
|
44
|
+
raise "password must be specified" if self.password.nil?
|
45
|
+
raise "invalid password format (letters and digits only)" unless self.password =~ /[a-z][a-z0-9]*/i
|
46
|
+
raise "EC2 user id must be defined in ENV or config" if Ec2.default_user_id.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
def rds_instances
|
50
|
+
@rds_instances ||= (rds.describe_db_instances.DescribeDBInstancesResult.DBInstances || {})['DBInstance'] || []
|
51
|
+
@rds_instances = [@rds_instances] unless @rds_instances.is_a?(Array)
|
52
|
+
@rds_instances
|
53
|
+
end
|
54
|
+
|
55
|
+
def should_create_rds_instance?
|
56
|
+
rds_instances.select{|i| i.DBInstanceIdentifier == instance_id }.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
def instance_id
|
60
|
+
name.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_rds_instance!
|
64
|
+
db_name = (databases.shift || instance_id).to_s.gsub(/\-/, '_')
|
65
|
+
params = {
|
66
|
+
:db_instance_identifier => instance_id,
|
67
|
+
:allocated_storage => storage || 5,
|
68
|
+
:db_instance_class => instance_class || "db.m1.small",
|
69
|
+
:engine => engine || "MySQL5.1",
|
70
|
+
:master_username => username,
|
71
|
+
:master_user_password => password,
|
72
|
+
:db_name => db_name
|
73
|
+
}
|
74
|
+
|
75
|
+
# TODO : optional params : :port, :db_parameter_group, :db_security_groups, :availability_zone, :preferred_backup_window, :backend_retention_period
|
76
|
+
rds.create_db_instance(params)
|
77
|
+
|
78
|
+
# TODO : create additional databases
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_rds_instance!
|
82
|
+
rds.delete_db_instance(:db_instance_identifier => instance_id, :skip_final_snapshot => "true")
|
83
|
+
end
|
84
|
+
|
85
|
+
def authorize_access
|
86
|
+
authorizations[:security_groups] = cloud.security_groups.map{|sg| sg.name } if authorizations[:security_groups].nil?
|
87
|
+
authorizations.each do |type, values|
|
88
|
+
[*values].each do |value|
|
89
|
+
begin
|
90
|
+
# TODO : allow customization of db sec group name
|
91
|
+
params = {:db_security_group_name => "default"}
|
92
|
+
|
93
|
+
if type.to_s =~ /network/
|
94
|
+
puts "authorizing NET access for #{value}..."
|
95
|
+
params[:cidrip] = value
|
96
|
+
else
|
97
|
+
puts "authorizing SECGRP access for #{value}/#{Ec2.default_user_id}..."
|
98
|
+
params[:ec2_security_group_name] = value
|
99
|
+
params[:ec2_security_group_owner_id] = Ec2.default_user_id
|
100
|
+
end
|
101
|
+
|
102
|
+
rds.authorize_db_security_group(params)
|
103
|
+
rescue AWS::Error => e
|
104
|
+
raise e unless e.message =~ /Authorization already exists/
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -53,9 +53,11 @@ module CloudProviders
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def run_chef!
|
56
|
-
|
57
|
-
"
|
58
|
-
|
56
|
+
chef_solo_cmd = <<-CMD
|
57
|
+
GEM_BIN=$(gem env | grep "EXECUTABLE DIRECTORY" | awk "{print \\$4}") \
|
58
|
+
&& $GEM_BIN/chef-solo -j /etc/chef/dna.json -c /etc/chef/solo.rb
|
59
|
+
CMD
|
60
|
+
ssh([chef_solo_cmd.strip.squeeze(' ')])
|
59
61
|
end
|
60
62
|
|
61
63
|
def run
|
data/lib/poolparty/cloud.rb
CHANGED
@@ -180,6 +180,11 @@ log_level :info
|
|
180
180
|
puts "-----> Tearing down load balancer: #{lb.name}"
|
181
181
|
lb.teardown
|
182
182
|
end
|
183
|
+
|
184
|
+
rds_instances.each do |rds|
|
185
|
+
puts "-----> Tearing down RDS Instance: #{rds.name}"
|
186
|
+
rds.teardown
|
187
|
+
end
|
183
188
|
# instances belonging to an auto_scaling group must be deleted before the auto_scaling group
|
184
189
|
#THIS SCARES ME! nodes.each{|n| n.terminate_instance!}
|
185
190
|
# loop {nodes.size>0 ? sleep(4) : break }
|
@@ -272,11 +277,11 @@ No autoscalers defined
|
|
272
277
|
# Run command/s on all nodes in the cloud.
|
273
278
|
# Returns a hash of instance_id=>result pairs
|
274
279
|
def cmd(commands, opts={})
|
275
|
-
|
280
|
+
key_by = opts.delete(:key_by) || :instance_id
|
276
281
|
results = {}
|
277
282
|
threads = nodes.collect do |n|
|
278
|
-
puts "result for #{n.instance_id} ==>
|
279
|
-
|
283
|
+
puts "result for #{n.instance_id} ==> n.ssh(#{commands.inspect}, #{opts.inspect})"
|
284
|
+
Thread.new{ results[ n.send(key_by) ] = n.ssh(commands, opts) }
|
280
285
|
end
|
281
286
|
threads.each{ |aThread| aThread.join }
|
282
287
|
results
|
data/lib/poolparty/pool.rb
CHANGED
@@ -6,9 +6,21 @@ module PoolParty
|
|
6
6
|
def cloud(name, &block)
|
7
7
|
clouds[name.to_s] = Cloud.new(name.to_s, {:parent => self}, &block)
|
8
8
|
end
|
9
|
+
|
9
10
|
def clouds
|
10
11
|
@clouds ||= {}
|
11
12
|
end
|
13
|
+
|
14
|
+
# Run command/s on all nodes in the pool.
|
15
|
+
# Returns a hash in the form of {cloud => [{instance_id=>result}]}
|
16
|
+
def cmd(commands, opts={})
|
17
|
+
results = {}
|
18
|
+
threads = clouds.collect do |name, c|
|
19
|
+
Thread.new{ results[ name ] = c.cmd(commands, opts) }
|
20
|
+
end
|
21
|
+
threads.each{ |aThread| aThread.join }
|
22
|
+
results
|
23
|
+
end
|
12
24
|
|
13
25
|
at_exit do
|
14
26
|
if pool.auto_execute
|