beaker-aws 0.4.0 → 0.5.0

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.
@@ -1,8 +1,15 @@
1
- require 'aws/ec2'
1
+ require 'aws-sdk-ec2'
2
+ require 'aws-sdk-core/waiters'
2
3
  require 'set'
3
4
  require 'zlib'
4
5
  require 'beaker/hypervisor/ec2_helper'
5
6
 
7
+ class Aws::EC2::Types::Instance
8
+ def ip_address
9
+ public_ip_address || private_ip_address
10
+ end
11
+ end
12
+
6
13
  module Beaker
7
14
  # This is an alternate EC2 driver that implements direct API access using
8
15
  # Amazon's AWS-SDK library: {http://aws.amazon.com/documentation/sdkforruby/ SDK For Ruby}
@@ -12,6 +19,7 @@ module Beaker
12
19
  class AwsSdk < Beaker::Hypervisor
13
20
  ZOMBIE = 3 #anything older than 3 hours is considered a zombie
14
21
  PING_SECURITY_GROUP_NAME = 'beaker-ping'
22
+ attr_reader :default_region
15
23
 
16
24
  # Initialize AwsSdk hypervisor driver
17
25
  #
@@ -21,26 +29,34 @@ module Beaker
21
29
  @hosts = hosts
22
30
  @options = options
23
31
  @logger = options[:logger]
32
+ @default_region = ENV['AWS_REGION'] || 'us-west-2'
24
33
 
25
34
  # Get AWS credentials
26
- creds = options[:use_fog_credentials] ? load_credentials() : Hash.new
35
+ creds = options[:use_fog_credentials] ? load_credentials() : nil
27
36
 
28
37
  config = {
29
- :access_key_id => creds[:access_key],
30
- :secret_access_key => creds[:secret_key],
31
- :session_token => creds[:session_token],
32
- :logger => Logger.new($stdout),
33
- :log_level => :debug,
34
- :log_formatter => AWS::Core::LogFormatter.colored,
35
- :max_retries => 12,
38
+ :credentials => creds,
39
+ :logger => Logger.new($stdout),
40
+ :log_level => :debug,
41
+ :log_formatter => Aws::Log::Formatter.colored,
42
+ :retry_limit => 12,
43
+ :region => ENV['AWS_REGION'] || 'us-west-2'
36
44
  }.delete_if{ |k,v| v.nil? }
37
- AWS.config(config)
45
+ Aws.config.update(config)
46
+
47
+ @client = {}
48
+ @client.default_proc = proc do |hash, key|
49
+ hash[key] = Aws::EC2::Client.new(:region => key)
50
+ end
38
51
 
39
- @ec2 = AWS::EC2.new()
40
52
  test_split_install()
41
53
  end
42
54
 
43
- # Provision all hosts on EC2 using the AWS::EC2 API
55
+ def client(region = default_region)
56
+ @client[region]
57
+ end
58
+
59
+ # Provision all hosts on EC2 using the Aws::EC2 API
44
60
  #
45
61
  # @return [void]
46
62
  def provision
@@ -71,21 +87,29 @@ module Beaker
71
87
  nil #void
72
88
  end
73
89
 
90
+ def regions
91
+ @regions ||= client.describe_regions.regions.map(&:region_name)
92
+ end
93
+
74
94
  # Kill all instances.
75
95
  #
76
- # @param instances [Enumerable<EC2::Instance>]
96
+ # @param instances [Enumerable<Aws::EC2::Types::Instance>]
77
97
  # @return [void]
78
98
  def kill_instances(instances)
79
- instances.each do |instance|
80
- if !instance.nil? and instance.exists?
81
- @logger.notify("aws-sdk: killing EC2 instance #{instance.id}")
82
- instance.terminate
83
- end
99
+ running_instances = instances.compact.select do |instance|
100
+ instance_by_id(instance.instance_id).state.name == 'running'
84
101
  end
102
+ instance_ids = running_instances.map(&:instance_id)
103
+
104
+ return nil if instance_ids.empty?
105
+
106
+ @logger.notify("aws-sdk: killing EC2 instance(s) #{instance_ids.join(', ')}")
107
+ client.terminate_instances(:instance_ids => instance_ids)
108
+
85
109
  nil
86
110
  end
87
111
 
88
- # Cleanup all earlier provisioned hosts on EC2 using the AWS::EC2 library
112
+ # Cleanup all earlier provisioned hosts on EC2 using the Aws::EC2 library
89
113
  #
90
114
  # It goes without saying, but a #cleanup does nothing without a #provision
91
115
  # method call first.
@@ -93,7 +117,7 @@ module Beaker
93
117
  # @return [void]
94
118
  def cleanup
95
119
  # Provisioning should have set the host 'instance' values.
96
- kill_instances(@hosts.map{|h| h['instance']}.select{|x| !x.nil?})
120
+ kill_instances(@hosts.map{ |h| h['instance'] }.select{ |x| !x.nil? })
97
121
  delete_key_pair_all_regions()
98
122
  nil
99
123
  end
@@ -106,17 +130,20 @@ module Beaker
106
130
  # @param [Regex] status The regular expression to match against the instance's status
107
131
  def log_instances(key = key_name, status = /running/)
108
132
  instances = []
109
- @ec2.regions.each do |region|
110
- @logger.debug "Reviewing: #{region.name}"
111
- @ec2.regions[region.name].instances.each do |instance|
112
- if (instance.key_name =~ /#{key}/) and (instance.status.to_s =~ status)
113
- instances << instance
133
+ regions.each do |region|
134
+ @logger.debug "Reviewing: #{region}"
135
+ client(region).describe_instances.reservations.each do |reservation|
136
+ reservation.instances.each do |instance|
137
+ if (instance.key_name =~ /#{key}/) and (instance.state.name =~ status)
138
+ instances << instance
139
+ end
114
140
  end
115
141
  end
116
142
  end
117
143
  output = ""
118
144
  instances.each do |instance|
119
- output << "#{instance.id} keyname: #{instance.key_name}, dns name: #{instance.dns_name}, private ip: #{instance.private_ip_address}, ip: #{instance.ip_address}, launch time #{instance.launch_time}, status: #{instance.status}\n"
145
+ dns_name = instance.public_dns_name || instance.private_dns_name
146
+ output << "#{instance.instance_id} keyname: #{instance.key_name}, dns name: #{dns_name}, private ip: #{instance.private_ip_address}, ip: #{instance.public_ip_address}, launch time #{instance.launch_time}, status: #{instance.state.name}\n"
120
147
  end
121
148
  @logger.notify("aws-sdk: List instances (keyname: #{key})")
122
149
  @logger.notify("#{output}")
@@ -125,46 +152,46 @@ module Beaker
125
152
  # Provided an id return an instance object.
126
153
  # Instance object will respond to methods described here: {http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/Instance.html AWS Instance Object}.
127
154
  # @param [String] id The id of the instance to return
128
- # @return [AWS::EC2::Instance] An AWS::EC2 instance object
155
+ # @return [Aws::EC2::Types::Instance] An Aws::EC2 instance object
129
156
  def instance_by_id(id)
130
- @ec2.instances[id]
157
+ client.describe_instances(:instance_ids => [id]).reservations.first.instances.first
131
158
  end
132
159
 
133
160
  # Return all instances currently on ec2.
134
161
  # @see AwsSdk#instance_by_id
135
- # @return [AWS::EC2::InstanceCollection] An array of AWS::EC2 instance objects
162
+ # @return [Array<Aws::Ec2::Types::Instance>] An array of Aws::EC2 instance objects
136
163
  def instances
137
- @ec2.instances
164
+ client.describe_instances.reservations.map(&:instances).flatten
138
165
  end
139
166
 
140
167
  # Provided an id return a VPC object.
141
168
  # VPC object will respond to methods described here: {http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/VPC.html AWS VPC Object}.
142
169
  # @param [String] id The id of the VPC to return
143
- # @return [AWS::EC2::VPC] An AWS::EC2 vpc object
170
+ # @return [Aws::EC2::Types::Vpc] An Aws::EC2 vpc object
144
171
  def vpc_by_id(id)
145
- @ec2.vpcs[id]
172
+ client.describe_vpcs(:vpc_ids => [id]).vpcs.first
146
173
  end
147
174
 
148
175
  # Return all VPCs currently on ec2.
149
176
  # @see AwsSdk#vpc_by_id
150
- # @return [AWS::EC2::VPCCollection] An array of AWS::EC2 vpc objects
177
+ # @return [Array<Aws::EC2::Types::Vpc>] An array of Aws::EC2 vpc objects
151
178
  def vpcs
152
- @ec2.vpcs
179
+ client.describe_vpcs.vpcs
153
180
  end
154
181
 
155
182
  # Provided an id return a security group object
156
183
  # Security object will respond to methods described here: {http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/EC2/SecurityGroup.html AWS SecurityGroup Object}.
157
184
  # @param [String] id The id of the security group to return
158
- # @return [AWS::EC2::SecurityGroup] An AWS::EC2 security group object
185
+ # @return [Aws::EC2::Types::SecurityGroup] An Aws::EC2 security group object
159
186
  def security_group_by_id(id)
160
- @ec2.security_groups[id]
187
+ client.describe_security_groups(:group_ids => [id]).security_groups.first
161
188
  end
162
189
 
163
190
  # Return all security groups currently on ec2.
164
191
  # @see AwsSdk#security_goup_by_id
165
- # @return [AWS::EC2::SecurityGroupCollection] An array of AWS::EC2 security group objects
192
+ # @return [Array<Aws::EC2::Types::SecurityGroup>] An array of Aws::EC2 security group objects
166
193
  def security_groups
167
- @ec2.security_groups
194
+ client.describe_security_groups.security_groups
168
195
  end
169
196
 
170
197
  # Shutdown and destroy ec2 instances idenfitied by key that have been alive
@@ -174,58 +201,60 @@ module Beaker
174
201
  # @param [String] key The key_name to match for
175
202
  def kill_zombies(max_age = ZOMBIE, key = key_name)
176
203
  @logger.notify("aws-sdk: Kill Zombies! (keyname: #{key}, age: #{max_age} hrs)")
177
- #examine all available regions
178
- kill_count = 0
204
+
205
+ instances_to_kill = []
206
+
179
207
  time_now = Time.now.getgm #ec2 uses GM time
180
- @ec2.regions.each do |region|
181
- @logger.debug "Reviewing: #{region.name}"
182
- # Note: don't use instances.each here as that funtion doesn't allow proper rescue from error states
183
- instances = @ec2.regions[region.name].instances
184
- instances.each do |instance|
185
- begin
208
+
209
+ #examine all available regions
210
+ regions.each do |region|
211
+ @logger.debug "Reviewing: #{region}"
212
+
213
+ client(region).describe_instances.reservations.each do |reservation|
214
+ reservation.instances.each do |instance|
186
215
  if (instance.key_name =~ /#{key}/)
187
- @logger.debug "Examining #{instance.id} (keyname: #{instance.key_name}, launch time: #{instance.launch_time}, status: #{instance.status})"
188
- if ((time_now - instance.launch_time) > max_age*60*60) and instance.status.to_s !~ /terminated/
189
- @logger.debug "Kill! #{instance.id}: #{instance.key_name} (Current status: #{instance.status})"
190
- instance.terminate()
191
- kill_count += 1
216
+ @logger.debug "Examining #{instance.instance_id} (keyname: #{instance.key_name}, launch time: #{instance.launch_time}, state: #{instance.state.name})"
217
+ if ((time_now - instance.launch_time) > max_age*60*60) and instance.state.name !~ /terminated/
218
+ @logger.debug "Kill! #{instance.instance_id}: #{instance.key_name} (Current status: #{instance.state.name})"
219
+ instances_to_kill << instance
192
220
  end
193
221
  end
194
- rescue AWS::Core::Resource::NotFound, AWS::EC2::Errors => e
195
- @logger.debug "Failed to remove instance: #{instance.id}, #{e}"
196
222
  end
197
223
  end
198
224
  end
225
+
226
+ kill_instances(instances_to_kill)
199
227
  delete_key_pair_all_regions(key_name_prefix)
200
228
 
201
- @logger.notify "#{key}: Killed #{kill_count} instance(s)"
229
+ @logger.notify "#{key}: Killed #{instances_to_kill.length} instance(s)"
202
230
  end
203
231
 
204
232
  # Destroy any volumes marked 'available', INCLUDING THOSE YOU DON'T OWN! Use with care.
205
233
  def kill_zombie_volumes
206
234
  # Occasionaly, tearing down ec2 instances leaves orphaned EBS volumes behind -- these stack up quickly.
207
235
  # This simply looks for EBS volumes that are not in use
208
- # Note: don't use volumes.each here as that funtion doesn't allow proper rescue from error states
209
236
  @logger.notify("aws-sdk: Kill Zombie Volumes!")
210
237
  volume_count = 0
211
- @ec2.regions.each do |region|
212
- @logger.debug "Reviewing: #{region.name}"
213
- volumes = @ec2.regions[region.name].volumes.map { |vol| vol.id }
214
- volumes.each do |vol|
238
+
239
+ regions.each do |region|
240
+ @logger.debug "Reviewing: #{region}"
241
+ available_volumes = client(region).describe_volumes(
242
+ :filters => [
243
+ { :name => 'status', :values => ['available'], }
244
+ ]
245
+ ).volumes
246
+
247
+ available_volumes.each do |volume|
215
248
  begin
216
- vol = @ec2.regions[region.name].volumes[vol]
217
- if ( vol.status.to_s =~ /available/ )
218
- @logger.debug "Tear down available volume: #{vol.id}"
219
- vol.delete()
220
- volume_count += 1
221
- end
222
- rescue AWS::EC2::Errors::InvalidVolume::NotFound => e
223
- @logger.debug "Failed to remove volume: #{vol.id}, #{e}"
249
+ client(region).delete_volume(:volume_id => volume.id)
250
+ volume_count += 1
251
+ rescue Aws::EC2::Errors::InvalidVolume::NotFound => e
252
+ @logger.debug "Failed to remove volume: #{volume.id} #{e}"
224
253
  end
225
254
  end
226
255
  end
227
- @logger.notify "Freed #{volume_count} volume(s)"
228
256
 
257
+ @logger.notify "Freed #{volume_count} volume(s)"
229
258
  end
230
259
 
231
260
  # Create an EC2 instance for host, tag it, and return it.
@@ -237,42 +266,40 @@ module Beaker
237
266
  amisize = host['amisize'] || 'm1.small'
238
267
  vpc_id = host['vpc_id'] || @options['vpc_id'] || nil
239
268
 
240
- if vpc_id and !subnet_id
269
+ if vpc_id && !subnet_id
241
270
  raise RuntimeError, "A subnet_id must be provided with a vpc_id"
242
271
  end
243
272
 
244
273
  # Use snapshot provided for this host
245
274
  image_type = host['snapshot']
246
- if not image_type
247
- raise RuntimeError, "No snapshot/image_type provided for EC2 provisioning"
248
- end
275
+ raise RuntimeError, "No snapshot/image_type provided for EC2 provisioning" unless image_type
276
+
249
277
  ami = ami_spec[amitype]
250
278
  ami_region = ami[:region]
251
279
 
252
280
  # Main region object for ec2 operations
253
- region = @ec2.regions[ami_region]
281
+ region = ami_region
254
282
 
255
283
  # If we haven't defined a vpc_id then we use the default vpc for the provided region
256
- if !vpc_id
257
- @logger.notify("aws-sdk: filtering available vpcs in region by 'isDefault")
258
- filtered_vpcs = region.client.describe_vpcs(:filters => [{:name => 'isDefault', :values => ['true']}])
259
- if !filtered_vpcs[:vpc_set].empty?
260
- vpc_id = filtered_vpcs[:vpc_set].first[:vpc_id]
261
- else #there's no default vpc, use nil
262
- vpc_id = nil
263
- end
284
+ unless vpc_id
285
+ @logger.notify("aws-sdk: filtering available vpcs in region by 'isDefault'")
286
+
287
+ default_vpcs = client(region).describe_vpcs(:filters => [{:name => 'isDefault', :values => ['true']}])
288
+ vpc_id = if default_vpcs.vpcs.empty?
289
+ nil
290
+ else
291
+ default_vpcs.vpcs.first.vpc_id
292
+ end
264
293
  end
265
294
 
266
295
  # Grab the vpc object based upon provided id
267
- vpc = vpc_id ? region.vpcs[vpc_id] : nil
296
+ vpc = vpc_id ? client(region).describe_vpcs(:vpc_ids => [vpc_id]).vpcs.first : nil
268
297
 
269
298
  # Grab image object
270
299
  image_id = ami[:image][image_type.to_sym]
271
300
  @logger.notify("aws-sdk: Checking image #{image_id} exists and getting its root device")
272
- image = region.images[image_id]
273
- if image.nil? and not image.exists?
274
- raise RuntimeError, "Image not found: #{image_id}"
275
- end
301
+ image = client(region).describe_images(:image_ids => [image_id]).images.first
302
+ raise RuntimeError, "Image not found: #{image_id}" if image.nil?
276
303
 
277
304
  @logger.notify("Image Storage Type: #{image.root_device_type}")
278
305
 
@@ -280,14 +307,14 @@ module Beaker
280
307
  # ready for a create.
281
308
  block_device_mappings = []
282
309
  if image.root_device_type == :ebs
283
- orig_bdm = image.block_device_mappings()
284
- @logger.notify("aws-sdk: Image block_device_mappings: #{orig_bdm.to_hash}")
285
- orig_bdm.each do |device_name, rest|
310
+ orig_bdm = image.block_device_mappings
311
+ @logger.notify("aws-sdk: Image block_device_mappings: #{orig_bdm}")
312
+ orig_bdm.each do |block_device|
286
313
  block_device_mappings << {
287
- :device_name => device_name,
314
+ :device_name => block_device.device_name,
288
315
  :ebs => {
289
316
  # Change the default size of the root volume.
290
- :volume_size => host['volume_size'] || rest[:volume_size],
317
+ :volume_size => host['volume_size'] || block_device.ebs.volume_size,
291
318
  # This is required to override the images default for
292
319
  # delete_on_termination, forcing all volumes to be deleted once the
293
320
  # instance is terminated.
@@ -306,18 +333,22 @@ module Beaker
306
333
  subnet_id ? ("in %p" % subnet_id) : '']
307
334
  @logger.notify(msg)
308
335
  config = {
309
- :count => 1,
310
- :image_id => image_id,
311
- :monitoring_enabled => true,
312
- :key_pair => ensure_key_pair(region),
313
- :security_groups => [security_group, ping_security_group],
336
+ :max_count => 1,
337
+ :min_count => 1,
338
+ :image_id => image_id,
339
+ :monitoring => {
340
+ :enabled => true,
341
+ },
342
+ :key_name => ensure_key_pair(region).key_pairs.first.key_name,
343
+ :security_groups => [security_group.group_name, ping_security_group.group_name],
314
344
  :instance_type => amisize,
315
345
  :disable_api_termination => false,
316
346
  :instance_initiated_shutdown_behavior => "terminate",
317
- :subnet => subnet_id,
347
+ :subnet_id => subnet_id,
318
348
  }
319
349
  config[:block_device_mappings] = block_device_mappings if image.root_device_type == :ebs
320
- region.instances.create(config)
350
+ reservation = client(region).run_instances(config)
351
+ reservation.instances.first
321
352
  end
322
353
 
323
354
  # For each host, create an EC2 instance in one of the specified
@@ -350,7 +381,7 @@ module Beaker
350
381
  instance = create_instance(host, ami_spec, subnet_id)
351
382
  instances_created.push({:instance => instance, :host => host})
352
383
  break
353
- rescue AWS::EC2::Errors::InsufficientInstanceCapacity => ex
384
+ rescue Aws::EC2::Errors::InsufficientInstanceCapacity
354
385
  @logger.notify("aws-sdk: hit #{subnet_id} capacity limit; moving on")
355
386
  subnet_i = (subnet_i + 1) % shuffnets.length
356
387
  end
@@ -426,7 +457,7 @@ module Beaker
426
457
  # Wait until all instances reach the desired state. Each Hash in
427
458
  # instances must contain an :instance and :host value.
428
459
  #
429
- # @param status [Symbol] EC2 state to wait for, :running :stopped etc.
460
+ # @param state_name [String] EC2 state to wait for, 'running', 'stopped', etc.
430
461
  # @param instances Enumerable<Hash{Symbol=>EC2::Instance,Host}>
431
462
  # @param block [Proc] more complex checks can be made by passing a
432
463
  # block in. This overrides the status parameter.
@@ -434,33 +465,38 @@ module Beaker
434
465
  # yielded to the passed block
435
466
  # @return [void]
436
467
  # @api private
437
- def wait_for_status(status, instances, &block)
468
+ # FIXME: rename to #wait_for_state
469
+ def wait_for_status(state_name, instances, &block)
438
470
  # Wait for each node to reach status :running
439
- @logger.notify("aws-sdk: Waiting for all hosts to be #{status}")
471
+ @logger.notify("aws-sdk: Waiting for all hosts to be #{state_name}")
440
472
  instances.each do |x|
441
- name = x[:name]
473
+ name = x[:host].name
442
474
  instance = x[:instance]
443
- @logger.notify("aws-sdk: Wait for node #{name} to be #{status}")
444
- # Here we keep waiting for the machine state to reach ':running' with an
475
+ @logger.notify("aws-sdk: Wait for node #{name} to be #{state_name}")
476
+ # Here we keep waiting for the machine state to reach 'running' with an
445
477
  # exponential backoff for each poll.
446
478
  # TODO: should probably be a in a shared method somewhere
447
479
  for tries in 1..10
448
- begin
480
+ refreshed_instance = instance_by_id(instance.instance_id)
481
+
482
+ if refreshed_instance.nil?
483
+ @logger.debug("Instance #{name} not yet available (#{e})")
484
+ else
449
485
  if block_given?
450
- test_result = yield instance
486
+ test_result = yield refreshed_instance
451
487
  else
452
- test_result = instance.status == status
488
+ test_result = refreshed_instance.state.name.to_s == state_name.to_s
453
489
  end
454
490
  if test_result
491
+ x[:instance] = refreshed_instance
455
492
  # Always sleep, so the next command won't cause a throttle
456
493
  backoff_sleep(tries)
457
494
  break
458
495
  elsif tries == 10
459
- raise "Instance never reached state #{status}"
496
+ raise "Instance never reached state #{state_name}"
460
497
  end
461
- rescue AWS::EC2::Errors::InvalidInstanceID::NotFound => e
462
- @logger.debug("Instance #{name} not yet available (#{e})")
463
498
  end
499
+
464
500
  backoff_sleep(tries)
465
501
  end
466
502
  end
@@ -479,8 +515,8 @@ module Beaker
479
515
  wait_for_status(:running, @hosts)
480
516
 
481
517
  wait_for_status(nil, @hosts) do |instance|
482
- instance_status_collection = instance.client.describe_instance_status({:instance_ids => [instance.id]})
483
- first_instance = instance_status_collection[:instance_status_set].first
518
+ instance_status_collection = instance.client.describe_instance_status({:instance_ids => [instance.instance_id]})
519
+ first_instance = instance_status_collection.reservations.first.instances.first
484
520
  first_instance[:system_status][:status] == "ok"
485
521
  end
486
522
 
@@ -499,15 +535,38 @@ module Beaker
499
535
 
500
536
  # Define tags for the instance
501
537
  @logger.notify("aws-sdk: Add tags for #{host.name}")
502
- instance.add_tag("jenkins_build_url", :value => @options[:jenkins_build_url])
503
- instance.add_tag("Name", :value => host.name)
504
- instance.add_tag("department", :value => @options[:department])
505
- instance.add_tag("project", :value => @options[:project])
506
- instance.add_tag("created_by", :value => @options[:created_by])
538
+
539
+ tags = [
540
+ {
541
+ :key => 'jenkins_build_url',
542
+ :value => @options[:jenkins_build_url],
543
+ },
544
+ {
545
+ :key => 'Name',
546
+ :value => host.name,
547
+ },
548
+ {
549
+ :key => 'department',
550
+ :value => @options[:department],
551
+ },
552
+ {
553
+ :key => 'project',
554
+ :value => @options[:project],
555
+ },
556
+ {
557
+ :key => 'created_by',
558
+ :value => @options[:created_by],
559
+ },
560
+ ]
507
561
 
508
562
  host[:host_tags].each do |name, val|
509
- instance.add_tag(name.to_s, :value => val)
563
+ tags << { :key => name.to_s, :value => val }
510
564
  end
565
+
566
+ client.create_tags(
567
+ :resources => [instance.instance_id],
568
+ :tags => tags.reject { |r| r[:value].nil? },
569
+ )
511
570
  end
512
571
 
513
572
  nil
@@ -522,10 +581,10 @@ module Beaker
522
581
  @hosts.each do |host|
523
582
  @logger.notify("aws-sdk: Populate DNS for #{host.name}")
524
583
  instance = host['instance']
525
- host['ip'] = instance.ip_address ? instance.ip_address : instance.private_ip_address
584
+ host['ip'] = instance.public_ip_address || instance.private_ip_address
526
585
  host['private_ip'] = instance.private_ip_address
527
- host['dns_name'] = instance.dns_name
528
- @logger.notify("aws-sdk: name: #{host.name} ip: #{host['ip']} private_ip: #{host['private_ip']} dns_name: #{instance.dns_name}")
586
+ host['dns_name'] = instance.public_dns_name || instance.private_dns_name
587
+ @logger.notify("aws-sdk: name: #{host.name} ip: #{host['ip']} private_ip: #{host['private_ip']} dns_name: #{host['dns_name']}")
529
588
  end
530
589
 
531
590
  nil
@@ -628,8 +687,8 @@ module Beaker
628
687
  # @return nil
629
688
  # @api private
630
689
  def enable_root_netscaler(host)
631
- host['ssh'] = {:password => host['instance'].id}
632
- @logger.notify("netscaler: nsroot password is #{host['instance'].id}")
690
+ host['ssh'] = {:password => host['instance'].instance_id}
691
+ @logger.notify("netscaler: nsroot password is #{host['instance'].instance_id}")
633
692
  end
634
693
 
635
694
  # Set the :vmhostname for each host object to be the dns_name, which is accessible
@@ -744,8 +803,8 @@ module Beaker
744
803
 
745
804
  # Creates the KeyPair for this test run
746
805
  #
747
- # @param region [AWS::EC2::Region] region to create the key pair in
748
- # @return [AWS::EC2::KeyPair] created key_pair
806
+ # @param region [Aws::EC2::Region] region to create the key pair in
807
+ # @return [Aws::EC2::KeyPair] created key_pair
749
808
  # @api private
750
809
  def ensure_key_pair(region)
751
810
  pair_name = key_name()
@@ -776,69 +835,52 @@ module Beaker
776
835
  # a simple {::String#start_with?} filter. If no filter is given, the basic key
777
836
  # name returned by {#key_name} will be used.
778
837
  #
779
- # @return [Hash{AWS::EC2::Region=>Array[String]}] a hash of region instance to
838
+ # @return [Hash{String=>Array[String]}] a hash of region name to
780
839
  # an array of the keypair names that match for the filter
781
840
  # @api private
782
841
  def my_key_pairs(name_filter=nil)
783
842
  keypairs_by_region = {}
784
- keyname_default = key_name()
785
- keyname_filtered = "#{name_filter}-*"
786
- @ec2.regions.each do |region|
787
- if name_filter
788
- aws_name_filter = keyname_filtered
789
- else
790
- aws_name_filter = keyname_default
791
- end
792
- keypair_collection = region.key_pairs.filter('key-name', aws_name_filter)
793
- keypair_collection.each do |keypair|
794
- keypairs_by_region[region] ||= []
795
- keypairs_by_region[region] << keypair.name
796
- end
843
+ key_name_filter = name_filter ? "#{name_filter}-*" : key_name
844
+
845
+ regions.each do |region|
846
+ keypairs_by_region[region] = client(region).describe_key_pairs(
847
+ :filters => [{ :name => 'key-name', :values => [key_name_filter] }]
848
+ ).key_pairs.map(&:key_name)
797
849
  end
850
+
798
851
  keypairs_by_region
799
852
  end
800
853
 
801
854
  # Deletes a given key pair
802
855
  #
803
- # @param [AWS::EC2::Region] region the region the key belongs to
856
+ # @param [Aws::EC2::Region] region the region the key belongs to
804
857
  # @param [String] pair_name the name of the key to be deleted
805
858
  #
806
859
  # @api private
807
860
  def delete_key_pair(region, pair_name)
808
- kp = region.key_pairs[pair_name]
809
- if kp.exists?
810
- @logger.debug("aws-sdk: delete key pair in region: #{region.name}")
811
- kp.delete()
861
+ kp = client(region).describe_key_pairs(:key_names => [pair_name]).key_pairs.first
862
+ unless kp.nil?
863
+ @logger.debug("aws-sdk: delete key pair in region: #{region}")
864
+ client(region).delete_key_pair(:key_name => pair_name)
812
865
  end
866
+ rescue Aws::EC2::Errors::InvalidKeyPairNotFound
867
+ nil
813
868
  end
814
869
 
815
870
  # Create a new key pair for a given Beaker run
816
871
  #
817
- # @param [AWS::EC2::Region] region the region the key pair will be imported into
872
+ # @param [Aws::EC2::Region] region the region the key pair will be imported into
818
873
  # @param [String] pair_name the name of the key to be created
819
874
  #
820
- # @return [AWS::EC2::KeyPair] key pair created
875
+ # @return [Aws::EC2::KeyPair] key pair created
821
876
  # @raise [RuntimeError] raised if AWS keypair not created
822
877
  def create_new_key_pair(region, pair_name)
823
878
  @logger.debug("aws-sdk: importing new key pair: #{pair_name}")
824
- ssh_string = public_key()
825
- region.key_pairs.import(pair_name, ssh_string)
826
- kp = region.key_pairs[pair_name]
827
-
828
- exists = false
829
- for tries in 1..5
830
- if kp.exists?
831
- exists = true
832
- break
833
- end
834
- @logger.debug("AWS key pair doesn't appear to exist yet, sleeping before retry ")
835
- backoff_sleep(tries)
836
- end
879
+ client(region).import_key_pair(:key_name => pair_name, :public_key_material => public_key)
837
880
 
838
- if exists
839
- @logger.debug("aws-sdk: key pair #{pair_name} imported")
840
- kp
841
- else
881
+ begin
882
+ client(region).wait_until(:key_pair_exists, { :key_names => [pair_name] }, :max_attempts => 5, :delay => 2)
883
+ rescue Aws::Waiters::Errors::WaiterFailed
842
884
  raise RuntimeError, "AWS key pair #{pair_name} can not be queried, even after import"
843
885
  end
844
886
  end
@@ -865,13 +907,18 @@ module Beaker
865
907
  #
866
908
  # Accepts a VPC as input for checking & creation.
867
909
  #
868
- # @param vpc [AWS::EC2::VPC] the AWS vpc control object
869
- # @return [AWS::EC2::SecurityGroup] created security group
910
+ # @param vpc [Aws::EC2::VPC] the AWS vpc control object
911
+ # @return [Aws::EC2::SecurityGroup] created security group
870
912
  # @api private
871
913
  def ensure_ping_group(vpc)
872
914
  @logger.notify("aws-sdk: Ensure security group exists that enables ping, create if not")
873
915
 
874
- group = vpc.security_groups.filter('group-name', PING_SECURITY_GROUP_NAME).first
916
+ group = client.describe_security_groups(
917
+ :filters => [
918
+ { :name => 'group-name', :values => [PING_SECURITY_GROUP_NAME] },
919
+ { :name => 'vpc-id', :values => [vpc.vpc_id] },
920
+ ]
921
+ ).security_groups.first
875
922
 
876
923
  if group.nil?
877
924
  group = create_ping_group(vpc)
@@ -884,15 +931,20 @@ module Beaker
884
931
  #
885
932
  # Accepts a VPC as input for checking & creation.
886
933
  #
887
- # @param vpc [AWS::EC2::VPC] the AWS vpc control object
934
+ # @param vpc [Aws::EC2::VPC] the AWS vpc control object
888
935
  # @param ports [Array<Number>] an array of port numbers
889
- # @return [AWS::EC2::SecurityGroup] created security group
936
+ # @return [Aws::EC2::SecurityGroup] created security group
890
937
  # @api private
891
938
  def ensure_group(vpc, ports)
892
939
  @logger.notify("aws-sdk: Ensure security group exists for ports #{ports.to_s}, create if not")
893
940
  name = group_id(ports)
894
941
 
895
- group = vpc.security_groups.filter('group-name', name).first
942
+ group = client.describe_security_groups(
943
+ :filters => [
944
+ { :name => 'group-name', :values => [name] },
945
+ { :name => 'vpc-id', :values => [vpc.vpc_id] },
946
+ ]
947
+ ).security_groups.first
896
948
 
897
949
  if group.nil?
898
950
  group = create_group(vpc, ports)
@@ -905,15 +957,28 @@ module Beaker
905
957
  #
906
958
  # Accepts a region or VPC for group creation.
907
959
  #
908
- # @param rv [AWS::EC2::Region, AWS::EC2::VPC] the AWS region or vpc control object
909
- # @return [AWS::EC2::SecurityGroup] created security group
960
+ # @param rv [Aws::EC2::Region, Aws::EC2::VPC] the AWS region or vpc control object
961
+ # @return [Aws::EC2::SecurityGroup] created security group
910
962
  # @api private
911
- def create_ping_group(rv)
963
+ def create_ping_group(region_or_vpc)
912
964
  @logger.notify("aws-sdk: Creating group #{PING_SECURITY_GROUP_NAME}")
913
- group = rv.security_groups.create(PING_SECURITY_GROUP_NAME,
914
- :description => "Custom Beaker security group to enable ping")
965
+ cl = region_or_vpc.is_a?(String) ? client(region_or_vpc) : client
966
+
967
+ params = {
968
+ :description => 'Custom Beaker security group to enable ping',
969
+ :group_name => PING_SECURITY_GROUP_NAME,
970
+ }
971
+ params[:vpc_id] = region_or_vpc.vpc_id if region_or_vpc.is_a?(Aws::EC2::Types::Vpc)
972
+
973
+ group = cl.create_security_group(params)
915
974
 
916
- group.allow_ping
975
+ cl.authorize_security_group_ingress(
976
+ :cidr_ip => '0.0.0.0/0',
977
+ :ip_protocol => 'icmp',
978
+ :from_port => '8', # 8 == ICMPv4 ECHO request
979
+ :to_port => '-1', # -1 == All ICMP codes
980
+ :group_id => group.group_id,
981
+ )
917
982
 
918
983
  group
919
984
  end
@@ -922,22 +987,32 @@ module Beaker
922
987
  #
923
988
  # Accepts a region or VPC for group creation.
924
989
  #
925
- # @param rv [AWS::EC2::Region, AWS::EC2::VPC] the AWS region or vpc control object
990
+ # @param rv [Aws::EC2::Region, Aws::EC2::VPC] the AWS region or vpc control object
926
991
  # @param ports [Array<Number>] an array of port numbers
927
- # @return [AWS::EC2::SecurityGroup] created security group
992
+ # @return [Aws::EC2::SecurityGroup] created security group
928
993
  # @api private
929
- def create_group(rv, ports)
994
+ def create_group(region_or_vpc, ports)
930
995
  name = group_id(ports)
931
996
  @logger.notify("aws-sdk: Creating group #{name} for ports #{ports.to_s}")
932
- group = rv.security_groups.create(name,
933
- :description => "Custom Beaker security group for #{ports.to_a}")
997
+ cl = region_or_vpc.is_a?(String) ? client(region_or_vpc) : client
998
+
999
+ group = cl.create_security_group(
1000
+ :group_name => name,
1001
+ :description => "Custom Beaker security group for #{ports.to_a}"
1002
+ )
934
1003
 
935
1004
  unless ports.is_a? Set
936
1005
  ports = Set.new(ports)
937
1006
  end
938
1007
 
939
1008
  ports.each do |port|
940
- group.authorize_ingress(:tcp, port)
1009
+ cl.authorize_security_group_ingress(
1010
+ :cidr_ip => '0.0.0.0/0',
1011
+ :ip_protocol => 'tcp',
1012
+ :from_port => port,
1013
+ :to_port => port,
1014
+ :group_id => group.group_id,
1015
+ )
941
1016
  end
942
1017
 
943
1018
  group
@@ -948,32 +1023,27 @@ module Beaker
948
1023
  # @return [Hash<Symbol, String>] AWS credentials
949
1024
  # @api private
950
1025
  def load_credentials
951
- return load_env_credentials unless load_env_credentials.empty?
1026
+ return load_env_credentials if load_env_credentials.set?
952
1027
  load_fog_credentials(@options[:dot_fog])
953
1028
  end
954
1029
 
955
1030
  # Return AWS credentials loaded from environment variables
956
1031
  #
957
1032
  # @param prefix [String] environment variable prefix
958
- # @return [Hash<Symbol, String>] ec2 credentials
1033
+ # @return [Aws::Credentials] ec2 credentials
959
1034
  # @api private
960
1035
  def load_env_credentials(prefix='AWS')
961
- provider = AWS::Core::CredentialProviders::ENVProvider.new prefix
962
-
963
- if provider.set?
964
- {
965
- :access_key => provider.access_key_id,
966
- :secret_key => provider.secret_access_key,
967
- :session_token => provider.session_token,
968
- }
969
- else
970
- {}
971
- end
1036
+ Aws::Credentials.new(
1037
+ ENV["#{prefix}_ACCESS_KEY_ID"],
1038
+ ENV["#{prefix}_SECRET_ACCESS_KEY"],
1039
+ ENV["#{prefix}_SESSION_TOKEN"]
1040
+ )
972
1041
  end
1042
+
973
1043
  # Return a hash containing the fog credentials for EC2
974
1044
  #
975
1045
  # @param dot_fog [String] dot fog path
976
- # @return [Hash<Symbol, String>] ec2 credentials
1046
+ # @return [Aws::Credentials] ec2 credentials
977
1047
  # @api private
978
1048
  def load_fog_credentials(dot_fog = '.fog')
979
1049
  fog = YAML.load_file( dot_fog )
@@ -982,11 +1052,11 @@ module Beaker
982
1052
  raise "You must specify an aws_access_key_id in your .fog file (#{dot_fog}) for ec2 instances!" unless default[:aws_access_key_id]
983
1053
  raise "You must specify an aws_secret_access_key in your .fog file (#{dot_fog}) for ec2 instances!" unless default[:aws_secret_access_key]
984
1054
 
985
- {
986
- :access_key => default[:aws_access_key_id],
987
- :secret_key => default[:aws_secret_access_key],
988
- :session_token => default[:aws_session_token],
989
- }
1055
+ Aws::Credentials.new(
1056
+ default[:aws_access_key_id],
1057
+ default[:aws_secret_access_key],
1058
+ default[:aws_session_token]
1059
+ )
990
1060
  end
991
1061
 
992
1062
  # Adds port 8143 to host[:additional_ports]