poolparty 1.6.8 → 1.6.9

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -14,7 +14,17 @@ require 'config/jeweler' # setup gem configuration
14
14
 
15
15
  task :default => [:test, :cleanup_test]
16
16
  desc "Update vendor directory and run tests"
17
- task :ci => ["poolparty:vendor:setup", "poolparty:vendor:update", :spec, :test]
17
+
18
+ namespace :poolparty do
19
+ namespace :vendor do
20
+ desc "Fetch all the submodules"
21
+ task :submodules do
22
+ `git submodule update`
23
+ end
24
+ end
25
+ end
26
+
27
+ task :vendor => ["poolparty:vendor:submodules"]
18
28
 
19
29
  task :cleanup_test do
20
30
  ::FileUtils.rm_rf "/tmp/poolparty"
@@ -83,4 +93,4 @@ Rake::RDocTask.new do |rd|
83
93
  rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
84
94
  rd.rdoc_dir = "rdoc"
85
95
  # rd.template = "hanaa"
86
- end
96
+ end
@@ -1,4 +1,5 @@
1
1
  ---
2
- :patch: 8
3
2
  :major: 1
4
3
  :minor: 6
4
+ :patch: 9
5
+ :build:
data/bin/cloud CHANGED
@@ -23,6 +23,7 @@ EOS
23
23
  opt :debug, "Debug the output", :type => :boolean, :default => false
24
24
  opt :very_debug, "Set very debug mode on", :type => :boolean, :default => false
25
25
  opt :name, "Name of the working cloud", :type => String, :default => nil
26
+ opt :chef_task, "Name of chef task to execute", :type => String, :default => 'default'
26
27
 
27
28
  before_run do |command|
28
29
  # Setup testing/debugging
@@ -38,9 +39,11 @@ EOS
38
39
  exit
39
40
  end
40
41
 
42
+ pool.chef_step command[:chef_task].to_sym
43
+
41
44
  @loaded_pool = pool
42
45
  @loaded_clouds = command[:name] ? [pool.clouds[command[:name]]] : pool.clouds.map {|name,cld|cld}
43
- if @loaded_clouds.count == 0
46
+ if @loaded_clouds.size == 0
44
47
  puts "No clouds loaded. Check your clouds.rb or -n option"
45
48
  exit
46
49
  end
@@ -24,8 +24,8 @@ EOS
24
24
  n.ssh(command[:command])
25
25
  end
26
26
 
27
- p output
27
+ #p output
28
28
 
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -6,8 +6,8 @@ require 'git-style-binary/command'
6
6
 
7
7
  GitStyleBinary.command do
8
8
  @theme = :short
9
-
10
- version "PoolParty #{$0} command"
9
+
10
+ version "PoolParty #{$0} command"
11
11
  banner <<-EOS
12
12
  Usage: #{$0} #{all_options_string}
13
13
 
@@ -20,7 +20,7 @@ EOS
20
20
  run do |command|
21
21
 
22
22
  @loaded_clouds.each do |cld|
23
-
23
+
24
24
  msg = [
25
25
  "Cloud: #{cld.name}",
26
26
  "----------------------------",
@@ -29,11 +29,12 @@ EOS
29
29
  "Maximum instances: #{cld.maximum_instances}",
30
30
  "Running on: #{cld.cloud_provider.name}",
31
31
  "Keypair: #{cld.keypair.basename}",
32
- "Security group: #{cld.cloud_provider.security_group_names}",
33
- "Availability zones: #{cld.cloud_provider.availability_zones}",
34
- "User: #{cld.user}"
32
+ "Security group: #{cld.cloud_provider.security_group_names.join(', ')}",
33
+ "Availability zones: #{cld.cloud_provider.availability_zones.join(', ')}",
34
+ "User: #{cld.user}",
35
+ "Active recipes: #{cld.chef._recipes(cld.pool.chef_step).join ", " }"
35
36
  ]
36
-
37
+
37
38
  if cld.load_balancers.size > 0
38
39
  load_balancers = cld.cloud_provider.load_balancers.first.running_load_balancers.map {|a| a[:dns_name]}
39
40
  msg << "Load balancers: #{load_balancers.join("\n\t\t\t")}"
@@ -45,10 +46,10 @@ EOS
45
46
 
46
47
  msg << available.join("\n\t\t\t")
47
48
  end
48
-
49
+
49
50
  puts msg.flatten
50
-
51
+
51
52
  end
52
-
53
+
53
54
  end
54
55
  end
@@ -108,7 +108,7 @@ module CloudProviders
108
108
  rsync_opts += %q% --rsync-path="sudo rsync"% unless user=="root"
109
109
  rsync_opts += %q% --exclude=.svn --exclude=.git --exclude=.cvs %
110
110
  cmd_string = "rsync -L -e 'ssh #{ssh_options}' #{rsync_opts} #{opts[:source]} #{user}@#{host}:#{destination_path}"
111
- out = system_run(cmd_string)
111
+ out = system_run(cmd_string, :quiet => true)
112
112
  out
113
113
  end
114
114
 
@@ -137,7 +137,9 @@ module CloudProviders
137
137
  while (chunk = stdin.readpartial(opts[:sysread]))
138
138
  buf << chunk
139
139
  unless chunk.nil? || chunk.empty?
140
- $stdout.write(chunk) #if debugging? || verbose?
140
+ if not opts[:quiet]
141
+ $stdout.write(chunk) #if debugging? || verbose?
142
+ end
141
143
  end
142
144
  end
143
145
  err = stderr.readlines
@@ -10,37 +10,39 @@ rescue LoadError
10
10
  EOM
11
11
  end
12
12
 
13
+ require 'pp'
14
+
13
15
  module CloudProviders
14
16
  class Ec2 < CloudProvider
15
17
  # Set the aws keys from the environment, or load from /etc/poolparty/env.yml if the environment variable is not set
16
18
  def self.default_access_key
17
19
  ENV['EC2_ACCESS_KEY'] || load_keys_from_file[:access_key] || load_keys_from_credential_file[:access_key]
18
20
  end
19
-
21
+
20
22
  def self.default_secret_access_key
21
23
  ENV['EC2_SECRET_KEY'] || load_keys_from_file[:secret_access_key] || load_keys_from_credential_file[:secret_access_key]
22
24
  end
23
-
25
+
24
26
  def self.default_private_key
25
27
  ENV['EC2_PRIVATE_KEY'] || load_keys_from_file[:private_key]
26
28
  end
27
-
29
+
28
30
  def self.default_cert
29
31
  ENV['EC2_CERT'] || load_keys_from_file[:cert]
30
32
  end
31
-
33
+
32
34
  def self.default_user_id
33
35
  ENV['EC2_USER_ID'] || load_keys_from_file[:user_id]
34
36
  end
35
-
37
+
36
38
  def self.default_ec2_url
37
39
  ENV['EC2_URL'] || load_keys_from_file[:ec2_url]
38
40
  end
39
-
41
+
40
42
  def self.default_s3_url
41
43
  ENV['S3_URL'] || load_keys_from_file[:s3_url]
42
44
  end
43
-
45
+
44
46
  def self.default_cloud_cert
45
47
  ENV['CLOUD_CERT'] || ENV['EUCALYPTUS_CERT'] || load_keys_from_file[:cloud_cert]
46
48
  end
@@ -48,7 +50,7 @@ module CloudProviders
48
50
  def self.default_credential_file
49
51
  ENV['AWS_CREDENTIAL_FILE'] || load_keys_from_file[:credential_file]
50
52
  end
51
-
53
+
52
54
  # Load the yaml file containing keys. If the file does not exist, return an empty hash
53
55
  def self.load_keys_from_file(filename="#{ENV["HOME"]}/.poolparty/aws", caching=true)
54
56
  return @aws_yml if @aws_yml && caching==true
@@ -71,8 +73,8 @@ module CloudProviders
71
73
  }
72
74
  return {:access_key => @access_key, :secret_access_key => @secret_access_key}
73
75
  end
74
-
75
-
76
+
77
+
76
78
  default_options(
77
79
  :instance_type => 'm1.small',
78
80
  :availability_zones => ["us-east-1a"],
@@ -84,7 +86,7 @@ module CloudProviders
84
86
  :secret_access_key => default_secret_access_key,
85
87
  :ec2_url => default_ec2_url,
86
88
  :s3_url => default_s3_url,
87
- :credential_file => default_credential_file,
89
+ :credential_file => default_credential_file,
88
90
  :min_count => 1,
89
91
  :max_count => 1,
90
92
  :user_data => '',
@@ -109,6 +111,7 @@ module CloudProviders
109
111
  puts " maximum_instances: #{maximum_instances}"
110
112
  puts " security_groups: #{security_group_names.join(", ")}"
111
113
  puts " using keypair: #{keypair}"
114
+ puts " with user_data #{user_data.to_s[0..100]}"
112
115
  puts " user: #{user}\n"
113
116
 
114
117
  security_groups.each do |sg|
@@ -152,7 +155,7 @@ module CloudProviders
152
155
  puts " autoscaler: #{a.name}"
153
156
  puts "-----> The autoscaling groups will launch the instances"
154
157
  a.run
155
-
158
+
156
159
  progress_bar_until("Waiting for autoscaler to launch instances") do
157
160
  reset!
158
161
  running_nodes = nodes.select {|n| n.running? }
@@ -161,8 +164,8 @@ module CloudProviders
161
164
  reset!
162
165
  end
163
166
  end
164
-
165
- from_ports = security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten
167
+
168
+ from_ports = security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten
166
169
  if from_ports.include?(22)
167
170
  progress_bar_until("Waiting for the instances to be accessible by ssh") do
168
171
  running_nodes = nodes.select {|n| n.running? }
@@ -172,20 +175,20 @@ module CloudProviders
172
175
  accessible_count == running_nodes.size
173
176
  end
174
177
  end
175
-
178
+
176
179
  assign_elastic_ips
177
180
  cleanup_ssh_known_hosts!
178
181
  puts "Attaching EBS volumes"
179
182
  assign_ebs_volumes # Assign EBS volumes
180
183
  end
181
-
184
+
182
185
  def teardown
183
186
  puts "------ Tearing down and cleaning up #{cloud.name} cloud"
184
187
  unless autoscalers.empty?
185
188
  puts "Tearing down autoscalers"
186
189
  end
187
190
  end
188
-
191
+
189
192
  def expand_by(num=1)
190
193
  e = Ec2Instance.run!({
191
194
  :image_id => image_id,
@@ -208,7 +211,7 @@ module CloudProviders
208
211
  end
209
212
  all_nodes.detect {|n| n.instance_id == e.instance_id }
210
213
  end
211
-
214
+
212
215
  def decoded_user_data
213
216
  if user_data
214
217
  if File.file?(user_data)
@@ -218,13 +221,13 @@ module CloudProviders
218
221
  end
219
222
  end
220
223
  end
221
-
224
+
222
225
  def wait_for_node(instance)
223
226
  reset!
224
227
  inst = all_nodes.detect {|n| n.instance_id == instance.instance_id }
225
228
  inst.running? if inst
226
229
  end
227
-
230
+
228
231
  def contract_by(num=1)
229
232
  raise RuntimeError, "Contracting instances by #{num} will lower the number of instances below specified minimum" unless nodes.size - num > minimum_instances
230
233
  num.times do |i|
@@ -235,9 +238,9 @@ module CloudProviders
235
238
  end
236
239
  reset!
237
240
  end
238
-
241
+
239
242
  def bootstrap_nodes!(tmp_path=nil)
240
- unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22)
243
+ unless security_groups.map {|a| a.authorizes.map {|t| t.from_port.to_i }.flatten }.flatten.include?(22)
241
244
  warn "Cloud security_groups are not authorized for ssh. Cannot bootstrap."
242
245
  return
243
246
  end
@@ -250,18 +253,18 @@ module CloudProviders
250
253
  node.run_chef!
251
254
  end
252
255
  end
253
-
256
+
254
257
  def configure_nodes!(tmp_path=nil)
255
258
  # removed duplicated code (now configure_nodes! invokes
256
259
  # node.bootstrap_chef!, while old version did not, but I believe
257
260
  # this is harmless)
258
- bootstrap_nodes!(tmp_path)
261
+ bootstrap_nodes!(tmp_path)
259
262
 
260
263
  ebs_volume_groups.each do |vol_grp|
261
264
  vol_grp.verify_attachments nodes
262
265
  end
263
266
  end
264
-
267
+
265
268
  def assign_elastic_ips
266
269
  unless elastic_ips.empty?
267
270
  unused_elastic_ip_addresses = ElasticIp.unused_elastic_ips(self).map {|i| i.public_ip }
@@ -299,11 +302,27 @@ module CloudProviders
299
302
  def nodes
300
303
  all_nodes.select {|i| i.in_service? }#describe_instances.select {|i| i.in_service? && security_groups.include?(i.security_groups) }
301
304
  end
302
-
305
+
306
+ # === Description
307
+ #
308
+ # Return all the security groups of the instance that are prefixed with #poolparty.
309
+ #
310
+ # These are special security groups used only for tagging
311
+ #
312
+ # === Parameters
313
+ # instance - An ec2 instance as returned from describe_instances
314
+ def tags instance
315
+ instance.groupSet.item.collect{|g| g.groupId }.select {|s| s.start_with? "#poolparty"}
316
+ end
317
+
303
318
  def all_nodes
304
- @nodes ||= describe_instances.select {|i| security_group_names.include?(i.security_groups) }.sort {|a,b| DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime)}
319
+ @nodes ||= describe_instances.select { |i|
320
+ !(security_group_names & tags(i)).empty?
321
+ }.sort {|a,b|
322
+ DateTime.parse(a.launchTime) <=> DateTime.parse(b.launchTime)
323
+ }
305
324
  end
306
-
325
+
307
326
  # Describe instances
308
327
  # Describe the instances that are available on this cloud
309
328
  # @params id (optional) if present, details about the instance
@@ -318,15 +337,15 @@ module CloudProviders
318
337
  end
319
338
  end.flatten
320
339
  rescue AWS::InvalidClientTokenId => e # AWS credentials invalid
321
- puts "Error contacting AWS: #{e}"
322
- raise e
340
+ puts "Error contacting AWS: #{e}"
341
+ raise e
323
342
  rescue Exception => e
324
343
  []
325
344
  end
326
345
  end
327
-
346
+
328
347
  # Extras!
329
-
348
+
330
349
  def block_device_mapping(o=[], given_name=cloud.proper_name )
331
350
  @mappings ||= o
332
351
  end
@@ -357,10 +376,10 @@ module CloudProviders
357
376
  @ec2 ||= begin
358
377
  AWS::EC2::Base.new( :access_key_id => access_key, :secret_access_key => secret_access_key )
359
378
  rescue AWS::ArgumentError => e # AWS credentials missing?
360
- puts "Error contacting AWS: #{e}"
361
- raise e
379
+ puts "Error contacting AWS: #{e}"
380
+ raise e
362
381
  rescue Exception => e
363
- puts "Generic error #{e.class}: #{e}"
382
+ puts "Generic error #{e.class}: #{e}"
364
383
  end
365
384
  end
366
385
 
@@ -398,7 +417,7 @@ module CloudProviders
398
417
  @ebs_volume_groups ||= []
399
418
  end
400
419
 
401
- # dsl method for EBS volumes. E.G.:
420
+ # dsl method for EBS volumes. E.G.:
402
421
  # ebs_volumes do
403
422
  # volumes "vol-001248ff", "vol-01ff4b85" # use existing volumes, not mandatory
404
423
  # device "/dev/sdf"
@@ -412,7 +431,7 @@ module CloudProviders
412
431
  def assign_ebs_volumes
413
432
  ebs_volume_groups.each{|ebs_volume_group| ebs_volume_group.attach(nodes)}
414
433
  end
415
-
434
+
416
435
  def rds_instances
417
436
  @rds_instances ||= []
418
437
  end
@@ -447,13 +466,13 @@ module CloudProviders
447
466
  # Read credentials from credential_file if one exists
448
467
  def credential_file(file=nil)
449
468
  unless file.nil?
450
- dsl_options[:credential_file]=file
469
+ dsl_options[:credential_file]=file
451
470
  dsl_options.merge!(Ec2.load_keys_from_credential_file(file))
452
471
  else
453
472
  fetch(:credential_file)
454
473
  end
455
474
  end
456
-
475
+
457
476
  private
458
477
  # Helper to get the options with self as parent
459
478
  def sub_opts
@@ -1,6 +1,6 @@
1
1
  module CloudProviders
2
2
  class Ec2Instance < RemoteInstance
3
-
3
+
4
4
  default_options(
5
5
  :security_groups => [],
6
6
  :private_ip => nil,
@@ -15,35 +15,41 @@ module CloudProviders
15
15
  :instance_initiated_shutdown_behavior => nil,
16
16
  :subnet_id => nil
17
17
  )
18
-
18
+
19
19
  def initialize(raw_response={})
20
20
  @raw_response = raw_response
21
- self.instance_id = raw_response["instanceId"] rescue nil
22
- self.security_groups = raw_response.groupSet.item[0].groupId rescue nil
23
- self.image_id = raw_response["imageId"] rescue nil
24
- self.private_ip = raw_response["privateIpAddress"] rescue nil
25
- self.dns_name = raw_response["dnsName"] rescue nil
26
- self.instance_type = raw_response["instanceType"] rescue nil
27
- self.public_ip = raw_response["ipAddress"] rescue nil
28
- self.key_name = raw_response["keyName"] rescue nil
29
- self.launch_time = raw_response["launchTime"] rescue nil
30
- self.availability_zones = raw_response["placement"]["availabilityZone"] rescue nil
31
- self.status = raw_response["instanceState"]["name"] rescue nil
32
- self.block_device_mapping = raw_response["blockDeviceMapping"] rescue nil
33
- self.disable_api_termination = raw_response["disableApiTermination"] rescue nil
34
- self.instance_initiated_shutdown_behavior = raw_response["instance_initiated_shutdown_behavior"] rescue nil
35
- self.subnet_id = raw_response["subnetId"] rescue nil
21
+ self.instance_id = raw_response["instanceId"] rescue nil
22
+ self.security_groups = raw_response.groupSet.item.map{|sg| sg.groupId }.sort rescue nil
23
+ self.image_id = raw_response["imageId"] rescue nil
24
+ self.private_ip = raw_response["privateIpAddress"] rescue nil
25
+ self.dns_name = raw_response["dnsName"] rescue nil
26
+ self.instance_type = raw_response["instanceType"] rescue nil
27
+ self.public_ip = raw_response["ipAddress"] rescue nil
28
+ self.key_name = raw_response["keyName"] rescue nil
29
+ self.launch_time = raw_response["launchTime"] rescue nil
30
+ self.availability_zones = raw_response["placement"]["availabilityZone"] rescue nil
31
+ self.status = raw_response["instanceState"]["name"] rescue nil
32
+ self.block_device_mapping = raw_response["blockDeviceMapping"] rescue nil
33
+ self.subnet_id = raw_response["subnetId"] rescue nil
34
+ # disable_api_termination and instance_initiated_shutdown_behavior don't currently get returned in the request -- you'd need to later call describe_instance_attribute
35
+ self.disable_api_termination = raw_response["disableApiTermination"] rescue nil
36
+ self.instance_initiated_shutdown_behavior = raw_response["instanceInitiatedShutdownBehavior"] rescue nil
36
37
  super
37
38
  end
38
-
39
+
39
40
  def keypair(n=nil)
40
- @keypair ||= Keypair.new(self.key_name)
41
+ return @keypair if @keypair
42
+ @keypair = (cloud.keypair.basename == self.key_name) ? cloud.keypair : Keypair.new(self.key_name, cloud.keypair.extra_paths)
41
43
  end
42
-
44
+
45
+ def security_group_names
46
+ security_groups.map{|a| a.to_s }
47
+ end
48
+
43
49
  def zone
44
50
  availability_zones.first
45
51
  end
46
-
52
+
47
53
  def reachable?
48
54
  ping_port self.public_ip, 22
49
55
  end
@@ -56,38 +62,42 @@ module CloudProviders
56
62
  in_service? and
57
63
  keypair and keypair.exists?
58
64
  end
59
-
65
+
60
66
  def in_service?
61
67
  running?
62
68
  end
63
-
69
+
64
70
  def run!
65
- r = cloud_provider.ec2.run_instances(:image_id => image_id,
66
- :min_count => min_count,
67
- :max_count => max_count,
68
- :key_name => keypair.basename,
69
- :security_group => cloud.security_group_names,
70
- :user_data => user_data,
71
- :instance_type => instance_type,
72
- :availability_zone => availability_zone,
73
- :block_device_mapping => block_device_mapping,
74
- :base64_encoded => true)
71
+ r = cloud_provider.ec2.run_instances(
72
+ :image_id => image_id,
73
+ :min_count => min_count,
74
+ :max_count => max_count,
75
+ :key_name => keypair.basename,
76
+ :security_group => cloud.security_group_names,
77
+ :user_data => user_data,
78
+ :instance_type => instance_type,
79
+ :availability_zone => availability_zone,
80
+ :block_device_mapping => block_device_mapping,
81
+ :disable_api_termination => disable_api_termination,
82
+ :instance_initiated_shutdown_behavior => instance_initiated_shutdown_behavior,
83
+ :base64_encoded => true)
75
84
  r.instancesSet.item.map do |i|
76
85
  inst_options = i.merge(r.merge(:cloud => cloud)).merge(cloud.cloud_provider.dsl_options)
77
86
  Ec2Instance.new(inst_options)
78
87
  end.first
79
88
  end
80
89
  def self.run!(hsh); new(hsh).run!; end
81
-
90
+
82
91
  def terminate!
83
92
  cloud_provider.ec2.terminate_instances(:instance_id => [self.instance_id])
84
93
  cloud_provider.reset!
85
94
  end
86
95
  def self.terminate!(hsh={}); new(hsh).terminate!; end
87
-
96
+
88
97
  # list of directories and files to exclude when bundling an instance
89
98
  def rsync_excludes(array_of_abs_paths_to_exclude=nil)
90
- array_of_abs_paths_to_exclude ||= %w( /sys
99
+ array_of_abs_paths_to_exclude ||= %w[
100
+ /sys
91
101
  /proc
92
102
  /dev/pts
93
103
  /dev
@@ -99,8 +109,8 @@ module CloudProviders
99
109
  /etc/ssh/moduli
100
110
  /etc/udev/rules.d/70-persistent-net.rules
101
111
  /etc/udev/rules.d/z25_persistent-net.rules
102
- )
103
- array_of_abs_paths_to_exclude.inject(''){|str, path| str<<"--exclude=#{path}"; str}
112
+ ]
113
+ array_of_abs_paths_to_exclude.inject(''){|str, path| str << "--exclude=#{path}" ; str}
104
114
  end
105
115
 
106
116
  # create an image file and copy this instance to the image file.
@@ -138,7 +148,7 @@ module CloudProviders
138
148
  scp ec2cert, "/mnt/bundle/"
139
149
  scp cert, "/mnt/bundle/"
140
150
  arch = self[:instanceType].match(/m1\.small|c1\.medium/) ? 'i386' : 'x86_64'
141
- image = img ? img : make_image(opts)
151
+ image = img ? img : make_image(opts)
142
152
  ssh "ec2-bundle-image #{image} -d /mnt/bundle -u #{self[:ownerId]} -k /mnt/bundle/pk-*.pem -c /tmp/cert-*.pem"
143
153
  manifest = "/mnt/bundle/#{opts[:prefix]}.manifest.xml"
144
154
  ssh "ec2-upload-bundle -a #{access_key} -s #{secret_access_key} -m #{manifest}"
@@ -146,6 +156,6 @@ module CloudProviders
146
156
  ami = ami_str.grep(/ami-\w*/).first
147
157
  return ami
148
158
  end
149
-
159
+
150
160
  end
151
161
  end