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.
Files changed (57) hide show
  1. data/VERSION.yml +2 -2
  2. data/bin/cloud-contract +6 -1
  3. data/bin/cloud-list +5 -6
  4. data/bin/cloud-vnc +39 -0
  5. data/examples/rds_cloud.rb +44 -0
  6. data/lib/cloud_providers/connections.rb +0 -1
  7. data/lib/cloud_providers/ec2/ec2.rb +86 -25
  8. data/lib/cloud_providers/ec2/ec2_instance.rb +6 -2
  9. data/lib/cloud_providers/ec2/helpers/ec2_helper.rb +19 -6
  10. data/lib/cloud_providers/ec2/helpers/elastic_load_balancer.rb +3 -3
  11. data/lib/cloud_providers/ec2/helpers/rds_instance.rb +110 -0
  12. data/lib/cloud_providers/remote_instance.rb +5 -3
  13. data/lib/poolparty/cloud.rb +8 -3
  14. data/lib/poolparty/pool.rb +12 -0
  15. data/test/fixtures/clouds/rds_cloud.rb +14 -0
  16. data/test/fixtures/clouds/rds_missing_params.rb +11 -0
  17. data/test/fixtures/ec2/ec2-describe-security-groups_response_body.xml +23 -0
  18. data/test/fixtures/ec2/rds-describe-db-instances-empty_response_body.xml +9 -0
  19. data/test/lib/poolparty/rds_test.rb +35 -0
  20. data/test/test_helper.rb +4 -4
  21. data/vendor/gems/amazon-ec2/ChangeLog +15 -0
  22. data/vendor/gems/amazon-ec2/README.rdoc +27 -24
  23. data/vendor/gems/amazon-ec2/README_dev.rdoc +0 -2
  24. data/vendor/gems/amazon-ec2/Rakefile +8 -39
  25. data/vendor/gems/amazon-ec2/VERSION +1 -1
  26. data/vendor/gems/amazon-ec2/amazon-ec2.gemspec +24 -16
  27. data/vendor/gems/amazon-ec2/bin/setup.rb +1 -0
  28. data/vendor/gems/amazon-ec2/lib/AWS/Autoscaling/autoscaling.rb +6 -16
  29. data/vendor/gems/amazon-ec2/lib/AWS/EC2/availability_zones.rb +8 -0
  30. data/vendor/gems/amazon-ec2/lib/AWS/EC2/console.rb +2 -0
  31. data/vendor/gems/amazon-ec2/lib/AWS/EC2/devpay.rb +18 -0
  32. data/vendor/gems/amazon-ec2/lib/AWS/EC2/elastic_ips.rb +37 -32
  33. data/vendor/gems/amazon-ec2/lib/AWS/EC2/images.rb +43 -27
  34. data/vendor/gems/amazon-ec2/lib/AWS/EC2/instances.rb +136 -99
  35. data/vendor/gems/amazon-ec2/lib/AWS/EC2/keypairs.rb +3 -17
  36. data/vendor/gems/amazon-ec2/lib/AWS/EC2/security_groups.rb +4 -23
  37. data/vendor/gems/amazon-ec2/lib/AWS/EC2/snapshots.rb +38 -17
  38. data/vendor/gems/amazon-ec2/lib/AWS/EC2/volumes.rb +6 -21
  39. data/vendor/gems/amazon-ec2/lib/AWS/EC2.rb +2 -2
  40. data/vendor/gems/amazon-ec2/lib/AWS/ELB/load_balancers.rb +11 -38
  41. data/vendor/gems/amazon-ec2/lib/AWS/RDS/rds.rb +522 -0
  42. data/vendor/gems/amazon-ec2/lib/AWS/RDS.rb +73 -0
  43. data/vendor/gems/amazon-ec2/lib/AWS/exceptions.rb +103 -25
  44. data/vendor/gems/amazon-ec2/lib/AWS.rb +19 -9
  45. data/vendor/gems/amazon-ec2/perftools/ec2prof +0 -0
  46. data/vendor/gems/amazon-ec2/perftools/ec2prof-results.dot +130 -191
  47. data/vendor/gems/amazon-ec2/perftools/ec2prof-results.txt +100 -126
  48. data/vendor/gems/amazon-ec2/perftools/ec2prof.symbols +102 -129
  49. data/vendor/gems/amazon-ec2/test/test_Autoscaling_groups.rb +3 -2
  50. data/vendor/gems/amazon-ec2/test/test_EC2_images.rb +32 -0
  51. data/vendor/gems/amazon-ec2/test/test_EC2_instances.rb +204 -22
  52. data/vendor/gems/amazon-ec2/test/test_EC2_snapshots.rb +1 -1
  53. data/vendor/gems/amazon-ec2/test/test_ELB_load_balancers.rb +2 -2
  54. data/vendor/gems/amazon-ec2/test/test_RDS.rb +354 -0
  55. data/vendor/gems/amazon-ec2/wsdl/2009-10-31.ec2.wsdl +4261 -0
  56. data/vendor/gems/amazon-ec2/wsdl/2009-11-30.ec2.wsdl +4668 -0
  57. metadata +17 -2
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :minor: 4
3
- :patch: 6
4
2
  :major: 1
3
+ :minor: 4
5
4
  :build:
5
+ :patch: 7
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
- line = "-----------------------------"
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.capitalize} nodes"
36
- msg << line
36
+ msg << " - #{k} nodes"
37
37
  v.each do |a|
38
- msg << " #{a.instance_id}\t#{a.status}\t#{a.public_ip}\t#{a.private_ip}\t#{a.dns_name}"
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
- ENV['CLOUD_CERT'] || ENV['EUCALYPTUS_CERT'] || load_keys_from_file[:cloud_cert]
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
- #TODO: need to sort by launch time
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 ||= AWS::EC2::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
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
- :group_id => cloud.security_group_names,
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| {:instance_id => a[:instance_id]}}
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| {:instance_id => a.instance_id} }
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| {:instance_id => a.instance_id} }
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
- ssh([
57
- "chef-solo -j /etc/chef/dna.json -c /etc/chef/solo.rb"
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
@@ -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
- opts[:key_by]= :instance_id unless opts[:key_by]
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} ==> #{n.ssh(commands, opts)}"
279
- Thread.new{ results[ n.send(opts[:key_by]) ] = n.ssh(commands, opts) }
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
@@ -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
@@ -0,0 +1,14 @@
1
+ # test RDS pool
2
+
3
+ pool :poolparty do
4
+ cloud :fake_cloud do
5
+ keypair File.dirname(__FILE__)+"/../keys/test_key"
6
+ using :ec2
7
+
8
+ rds :db1 do
9
+ username "admin"
10
+ password "secret"
11
+ storage 5
12
+ end
13
+ end
14
+ end