poolparty 1.4.6 → 1.4.7
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.
- 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
|