kitchen-ec2 3.6.0 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba67c03a470c6f9e73dc53b886a468a0003c24d9116cee97075ddc99da8a7529
4
- data.tar.gz: 8c77eba9c51421b234c0f5b2a7b75295eca8b5385a13cb9ca4213d4e65d5ddb9
3
+ metadata.gz: aac8aebec145ee67e00276453fd2f8a1aaae0466d58bec03ffdc8b368f001118
4
+ data.tar.gz: f14c9b4f85a0ebd1bf5c6398efc54b93e195e465ef4fc1c944688f96f53f1d39
5
5
  SHA512:
6
- metadata.gz: fdd64c2dd9a1b9e1a0811cc7a9bb34984f5a6bf9791b06d4ee2dcf05136d491a4024fd25b2c8cc996160970a155facad432c64270e2afebee9b6e4b3539ac30b
7
- data.tar.gz: 7597be672135a237bbfd11b5e40a0f1397d0f374253ac6eb1123834263c6d53b14509f2c7d662dae225bbd399acd494793c9d07fae225cf1480bbb986fc0b871
6
+ metadata.gz: d1e8f020cde0872c27abade96517dae1dcafa5d50aa4b08245ec928adfc024e309cc1977e5bdaebb30f3dfcea1f463ee01dabfe62baf4a0640b8bce527aa80b4
7
+ data.tar.gz: 958a08022889c1bef5e0becc8cd75d53b5a0cd68445043c0c934bdc9a03822afcd9cc6f6dc49f15fe63928933f61f6d383d3b3bd3b7ad278d851f2c4957726a1
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  #
3
2
  # Author:: Tyler Ball (<tball@chef.io>)
4
3
  #
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  #
3
2
  # Author:: Tyler Ball (<tball@chef.io>)
4
3
  #
@@ -17,7 +16,7 @@
17
16
  # See the License for the specific language governing permissions and
18
17
  # limitations under the License.
19
18
 
20
- require "base64"
19
+ require "base64" unless defined?(Base64)
21
20
  require "aws-sdk-ec2"
22
21
 
23
22
  module Kitchen
@@ -59,7 +58,7 @@ module Kitchen
59
58
  raise "The subnet tagged '#{config[:subnet_filter][:tag]}:#{config[:subnet_filter][:value]}' does not exist!" unless subnets.any?
60
59
 
61
60
  # => Select the least-populated subnet if we have multiple matches
62
- subnet = subnets.sort_by { |s| s[:available_ip_address_count] }.last
61
+ subnet = subnets.max_by { |s| s[:available_ip_address_count] }
63
62
  vpc_id = subnet.vpc_id
64
63
  config[:subnet_id] = subnet.subnet_id
65
64
  end
@@ -115,11 +114,25 @@ module Kitchen
115
114
  key_name: config[:aws_ssh_key_id],
116
115
  subnet_id: config[:subnet_id],
117
116
  private_ip_address: config[:private_ip_address],
117
+ min_count: 1,
118
+ max_count: 1,
118
119
  }
119
120
 
121
+ if config[:tags] && !config[:tags].empty?
122
+ tags = config[:tags].map do |k, v|
123
+ # we convert the value to a string because
124
+ # nils should be passed as an empty String
125
+ # and Integers need to be represented as Strings
126
+ { key: k, value: v.to_s }
127
+ end
128
+ instance_tag_spec = { resource_type: "instance", tags: tags }
129
+ volume_tag_spec = { resource_type: "volume", tags: tags }
130
+ i[:tag_specifications] = [instance_tag_spec, volume_tag_spec]
131
+ end
132
+
120
133
  availability_zone = config[:availability_zone]
121
134
  if availability_zone
122
- if availability_zone =~ /^[a-z]$/i
135
+ if /^[a-z]$/i.match?(availability_zone)
123
136
  availability_zone = "#{config[:region]}#{availability_zone}"
124
137
  end
125
138
  i[:placement] = { availability_zone: availability_zone.downcase }
@@ -162,7 +175,7 @@ module Kitchen
162
175
  end
163
176
  availability_zone = config[:availability_zone]
164
177
  if availability_zone
165
- if availability_zone =~ /^[a-z]$/i
178
+ if /^[a-z]$/i.match?(availability_zone)
166
179
  availability_zone = "#{config[:region]}#{availability_zone}"
167
180
  end
168
181
  i[:placement] = { availability_zone: availability_zone.downcase }
@@ -210,10 +210,10 @@ module Kitchen
210
210
  images = images.sort_by(&:creation_date).reverse
211
211
  # P5: We prefer x86_64 over i386 (if available)
212
212
  images = prefer(images) { |image| image.architecture == "x86_64" }
213
- # P4: We prefer gp2 (SSD) (if available)
213
+ # P4: We prefer (SSD) (if available)
214
214
  images = prefer(images) do |image|
215
215
  image.block_device_mappings.any? do |b|
216
- b.device_name == image.root_device_name && b.ebs && b.ebs.volume_type == "gp2"
216
+ b.device_name == image.root_device_name && b.ebs && %w{gp3 gp2}.any? { |t| b.ebs.volume_type == t }
217
217
  end
218
218
  end
219
219
  # P3: We prefer ebs over instance_store (if available)
@@ -39,7 +39,7 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if image.name =~ /amzn-ami/i
42
+ if /amzn-ami/i.match?(image.name)
43
43
  image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
44
  new(driver, "amazon", (Regexp.last_match || [])[1], image.architecture)
45
45
  end
@@ -39,7 +39,7 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if image.name =~ /amzn2-ami/i
42
+ if /amzn2-ami/i.match?(image.name)
43
43
  image.name =~ /\b(\d+(\.\d+[\.\d])?)/i
44
44
  new(driver, "amazon2", (Regexp.last_match || [])[1], image.architecture)
45
45
  end
@@ -23,6 +23,14 @@ module Kitchen
23
23
  class Centos < StandardPlatform
24
24
  StandardPlatform.platforms["centos"] = self
25
25
 
26
+ CENTOS_OWNER_ID = "125523088429".freeze
27
+ PRODUCT_CODES = {
28
+ "6" => "6x5jmcajty9edm3f211pqjfn2",
29
+ "7" => "aw0evgkw8e5c1q413zgy5pjce",
30
+ # It appears that v8 is not published to the
31
+ # AWS marketplace and hence does not have a product code
32
+ }.freeze
33
+
26
34
  # default username for this platform's ami
27
35
  # @return [String]
28
36
  def username
@@ -34,10 +42,25 @@ module Kitchen
34
42
  end
35
43
 
36
44
  def image_search
45
+ # Version 8+ are published directly, not to the AWS marketplace. Use OWNER ID.
37
46
  search = {
38
- "owner-alias" => "aws-marketplace",
39
- "name" => ["CentOS Linux #{version}*", "CentOS-#{version}*-GA-*"],
47
+ "owner-id" => CENTOS_OWNER_ID,
48
+ "name" => ["CentOS #{version}*", "CentOS-#{version}*-GA-*"],
40
49
  }
50
+
51
+ if version && version.split(".").first.to_i < 8
52
+ # Versions <8 are published to the AWS marketplace and use a different naming convention
53
+ search = {
54
+ "owner-alias" => "aws-marketplace",
55
+ "name" => ["CentOS Linux #{version}*", "CentOS-#{version}*-GA-*"],
56
+ }
57
+ # For versions published to aws-marketplace, additionally filter on product code to
58
+ # avoid non-official AMIs. Can't use CentOS owner ID here, as the owner ID is that of aws marketplace.
59
+ # https://github.com/test-kitchen/kitchen-ec2/issues/456
60
+ PRODUCT_CODES.keys.each do |major_version|
61
+ search["product-code"] = PRODUCT_CODES[major_version] if version.start_with?(major_version)
62
+ end
63
+ end
41
64
  search["architecture"] = architecture if architecture
42
65
  search
43
66
  end
@@ -52,7 +75,7 @@ module Kitchen
52
75
  end
53
76
 
54
77
  def self.from_image(driver, image)
55
- if image.name =~ /centos/i
78
+ if /centos/i.match?(image.name)
56
79
  image.name =~ /\b(\d+(\.\d+)?)\b/i
57
80
  new(driver, "centos", (Regexp.last_match || [])[1], image.architecture)
58
81
  end
@@ -23,8 +23,8 @@ module Kitchen
23
23
  class Debian < StandardPlatform
24
24
  StandardPlatform.platforms["debian"] = self
25
25
 
26
- # 10/11 are listed last since we default to the first item in the hash
27
- # and 10/11 are not released yet. When they're released move them up
26
+ # 11/12 are listed last since we default to the first item in the hash
27
+ # and 11/12 are not released yet. When they're released move them up
28
28
  DEBIAN_CODENAMES = {
29
29
  10 => "buster",
30
30
  9 => "stretch",
@@ -60,7 +60,7 @@ module Kitchen
60
60
  end
61
61
 
62
62
  def self.from_image(driver, image)
63
- if image.name =~ /debian/i
63
+ if /debian/i.match?(image.name)
64
64
  image.name =~ /\b(\d+|#{DEBIAN_CODENAMES.values.join("|")})\b/i
65
65
  version = (Regexp.last_match || [])[1]
66
66
  if version && version.to_i == 0
@@ -39,7 +39,7 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if image.name =~ /fedora/i
42
+ if /fedora/i.match?(image.name)
43
43
  image.name =~ /\b(\d+(\.\d+)?)\b/i
44
44
  new(driver, "fedora", (Regexp.last_match || [])[1], image.architecture)
45
45
  end
@@ -41,7 +41,7 @@ module Kitchen
41
41
  end
42
42
 
43
43
  def self.from_image(driver, image)
44
- if image.name =~ /freebsd/i
44
+ if /freebsd/i.match?(image.name)
45
45
  image.name =~ /\b(\d+(\.\d+)?)\b/i
46
46
  new(driver, "freebsd", (Regexp.last_match || [])[1], image.architecture)
47
47
  end
@@ -45,11 +45,18 @@ module Kitchen
45
45
  end
46
46
 
47
47
  def self.from_image(driver, image)
48
- if image.name =~ /rhel/i
48
+ if /rhel/i.match?(image.name)
49
49
  image.name =~ /\b(\d+(\.\d+)?)/i
50
50
  new(driver, "rhel", (Regexp.last_match || [])[1], image.architecture)
51
51
  end
52
52
  end
53
+
54
+ def sort_by_version(images)
55
+ # First do a normal version sort
56
+ super(images)
57
+ # Now sort again, shunning Beta releases.
58
+ prefer(images) { |image| !image.name.match(/_Beta-/i) }
59
+ end
53
60
  end
54
61
  end
55
62
  end
@@ -39,7 +39,7 @@ module Kitchen
39
39
  end
40
40
 
41
41
  def self.from_image(driver, image)
42
- if image.name =~ /ubuntu/i
42
+ if /ubuntu/i.match?(image.name)
43
43
  image.name =~ /\b(\d+(\.\d+)?)\b/i
44
44
  new(driver, "ubuntu", (Regexp.last_match || [])[1], image.architecture)
45
45
  end
@@ -70,7 +70,7 @@ module Kitchen
70
70
  end
71
71
 
72
72
  def self.from_image(driver, image)
73
- if image.name =~ /Windows/i
73
+ if /Windows/i.match?(image.name)
74
74
  # 2008 R2 SP2
75
75
  if image.name =~ /(\b\d+)\W*(r\d+)?/i
76
76
  major, revision = (Regexp.last_match || [])[1], (Regexp.last_match || [])[2]
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  #
3
2
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
3
  #
@@ -17,8 +16,8 @@
17
16
  # See the License for the specific language governing permissions and
18
17
  # limitations under the License.
19
18
 
20
- require "benchmark"
21
- require "json"
19
+ require "benchmark" unless defined?(Benchmark)
20
+ require "json" unless defined?(JSON)
22
21
  require "kitchen"
23
22
  require_relative "ec2_version"
24
23
  require_relative "aws/client"
@@ -35,10 +34,10 @@ require_relative "aws/standard_platform/ubuntu"
35
34
  require_relative "aws/standard_platform/windows"
36
35
  require "aws-sdk-ec2"
37
36
  require "aws-sdk-core/waiters/errors"
38
- require "retryable"
39
- require "time"
40
- require "etc"
41
- require "socket"
37
+ require "retryable" unless defined?(Retryable)
38
+ require "time" unless defined?(Time)
39
+ require "etc" unless defined?(Etc)
40
+ require "socket" unless defined?(Socket)
42
41
 
43
42
  module Kitchen
44
43
 
@@ -57,7 +56,8 @@ module Kitchen
57
56
  default_config :shared_credentials_profile, ENV["AWS_PROFILE"]
58
57
  default_config :availability_zone, nil
59
58
  default_config :instance_type, &:default_instance_type
60
- default_config :ebs_optimized, false
59
+ default_config :ebs_optimized, false
60
+ default_config :delete_on_termination, true
61
61
  default_config :security_group_ids, nil
62
62
  default_config :security_group_filter, nil
63
63
  default_config :security_group_cidr_ip, "0.0.0.0/0"
@@ -227,7 +227,7 @@ module Kitchen
227
227
 
228
228
  if config[:spot_price]
229
229
  # Spot instance when a price is set
230
- server = with_request_limit_backoff(state) { submit_spots(state) }
230
+ server = with_request_limit_backoff(state) { submit_spots }
231
231
  else
232
232
  # On-demand instance
233
233
  server = with_request_limit_backoff(state) { submit_server }
@@ -238,32 +238,16 @@ module Kitchen
238
238
  server.wait_until_exists(before_attempt: logging_proc)
239
239
  end
240
240
 
241
+ state[:server_id] = server.id
242
+ info("EC2 instance <#{state[:server_id]}> created.")
243
+
241
244
  # See https://github.com/aws/aws-sdk-ruby/issues/859
242
- # Tagging can fail with a NotFound error even though we waited until the server exists
243
- # Waiting can also fail, so we have to also retry on that. If it means we re-tag the
244
- # instance, so be it.
245
- # Tagging an instance is possible before volumes are attached. Tagging the volumes after
246
- # instance creation is consistent.
245
+ # Waiting can fail, so we have to retry on that.
247
246
  Retryable.retryable(
248
247
  tries: 10,
249
248
  sleep: lambda { |n| [2**n, 30].min },
250
249
  on: ::Aws::EC2::Errors::InvalidInstanceIDNotFound
251
250
  ) do |r, _|
252
- info("Attempting to tag the instance, #{r} retries")
253
- tag_server(server)
254
-
255
- # Get information about the AMI (image) used to create the image.
256
- image_data = ec2.client.describe_images({ image_ids: [server.image_id] })[0][0]
257
-
258
- state[:server_id] = server.id
259
- info("EC2 instance <#{state[:server_id]}> created.")
260
-
261
- # instance-store backed images do not have attached volumes, so only
262
- # wait for the volumes to be ready if the instance EBS-backed.
263
- if image_data.root_device_type == "ebs"
264
- wait_until_volumes_ready(server, state)
265
- tag_volumes(server)
266
- end
267
251
  wait_until_ready(server, state)
268
252
  end
269
253
 
@@ -277,7 +261,7 @@ module Kitchen
277
261
 
278
262
  info("EC2 instance <#{state[:server_id]}> ready (hostname: #{state[:hostname]}).")
279
263
  instance.transport.connection(state).wait_until_ready
280
- create_ec2_json(state) if instance.provisioner.name =~ /chef/i
264
+ create_ec2_json(state) if /chef/i.match?(instance.provisioner.name)
281
265
  debug("ec2:create '#{state[:hostname]}'")
282
266
  rescue Exception
283
267
  # Clean up any auto-created security groups or keys on the way out.
@@ -297,13 +281,6 @@ module Kitchen
297
281
  warn("Received #{e}, instance was probably already destroyed. Ignoring")
298
282
  end
299
283
  end
300
- if state[:spot_request_id]
301
- debug("Deleting spot request <#{state[:server_id]}>")
302
- ec2.client.cancel_spot_instance_requests(
303
- spot_instance_request_ids: [state[:spot_request_id]]
304
- )
305
- state.delete(:spot_request_id)
306
- end
307
284
  # If we are going to clean up an automatic security group, we need
308
285
  # to wait for the instance to shut down. This slightly breaks the
309
286
  # subsystem encapsulation, sorry not sorry.
@@ -342,17 +319,14 @@ module Kitchen
342
319
  end
343
320
 
344
321
  def default_instance_type
345
- @instance_type ||= begin
346
- # We default to the free tier (t2.micro for hvm, t1.micro for paravirtual)
347
- if image && image.virtualization_type == "hvm"
348
- info("instance_type not specified. Using free tier t2.micro instance ...")
349
- "t2.micro"
350
- else
351
- info("instance_type not specified. Using free tier t1.micro instance since" \
352
- " image is paravirtual (pick an hvm image to use the superior t2.micro!) ...")
353
- "t1.micro"
354
- end
355
- end
322
+ @instance_type ||= if image && image.virtualization_type == "hvm"
323
+ info("instance_type not specified. Using free tier t2.micro instance ...")
324
+ "t2.micro"
325
+ else
326
+ info("instance_type not specified. Using free tier t1.micro instance since" \
327
+ " image is paravirtual (pick an hvm image to use the superior t2.micro!) ...")
328
+ "t1.micro"
329
+ end
356
330
  end
357
331
 
358
332
  # The actual platform is the platform detected from the image
@@ -409,15 +383,14 @@ module Kitchen
409
383
  @instance_generator = Aws::InstanceGenerator.new(config, ec2, instance.logger)
410
384
  end
411
385
 
412
- # Fog AWS helper for creating the instance
386
+ # AWS helper for creating the instance
413
387
  def submit_server
414
388
  instance_data = instance_generator.ec2_instance_data
415
389
  debug("Creating EC2 instance in region #{config[:region]} with properties:")
416
390
  instance_data.each do |key, value|
417
391
  debug("- #{key} = #{value.inspect}")
418
392
  end
419
- instance_data[:min_count] = 1
420
- instance_data[:max_count] = 1
393
+
421
394
  ec2.create_instance(instance_data)
422
395
  end
423
396
 
@@ -445,10 +418,34 @@ module Kitchen
445
418
  configs
446
419
  end
447
420
 
448
- def submit_spots(state)
421
+ def submit_spots
449
422
  configs = [config]
450
423
  expanded = []
451
- keys = %i{instance_type subnet_id}
424
+ keys = %i{instance_type}
425
+
426
+ unless config[:subnet_filter]
427
+ # => Use explicitly specified subnets
428
+ keys << :subnet_id
429
+ else
430
+ # => Enable cascading through matching subnets
431
+ client = ::Aws::EC2::Client.new(region: config[:region])
432
+ subnets = client.describe_subnets(
433
+ filters: [
434
+ {
435
+ name: "tag:#{config[:subnet_filter][:tag]}",
436
+ values: [config[:subnet_filter][:value]],
437
+ },
438
+ ]
439
+ ).subnets
440
+ raise "A subnet matching '#{config[:subnet_filter][:tag]}:#{config[:subnet_filter][:value]}' does not exist!" unless subnets.any?
441
+
442
+ configs = subnets.map do |subnet|
443
+ new_config = config.clone
444
+ new_config[:subnet_id] = subnet.subnet_id
445
+ new_config[:subnet_filter] = nil
446
+ new_config
447
+ end
448
+ end
452
449
 
453
450
  keys.each do |key|
454
451
  configs.each do |conf|
@@ -460,96 +457,55 @@ module Kitchen
460
457
 
461
458
  errs = []
462
459
  configs.each do |conf|
463
- begin
464
- @config = conf
465
- return submit_spot(state)
466
- rescue => e
467
- errs.append(e)
468
- end
460
+ @config = conf
461
+ return submit_spot
462
+ rescue => e
463
+ errs.append(e)
469
464
  end
470
465
  raise ["Could not create a spot instance:", errs].flatten.join("\n")
471
466
  end
472
467
 
473
- def submit_spot(state)
468
+ def submit_spot
474
469
  debug("Creating EC2 Spot Instance..")
470
+ instance_data = instance_generator.ec2_instance_data
475
471
 
476
- spot_request_id = create_spot_request
477
- # deleting the instance cancels the request, but deleting the request
478
- # does not affect the instance
479
- state[:spot_request_id] = spot_request_id
480
- ec2.client.wait_until(
481
- :spot_instance_request_fulfilled,
482
- spot_instance_request_ids: [spot_request_id]
483
- ) do |w|
484
- w.max_attempts = config[:spot_wait] / config[:retryable_sleep]
485
- w.delay = config[:retryable_sleep]
486
- w.before_attempt do |attempts|
487
- c = attempts * config[:retryable_sleep]
488
- t = config[:spot_wait]
489
- info "Waited #{c}/#{t}s for spot request <#{spot_request_id}> to become fulfilled."
490
- end
491
- end
492
- ec2.get_instance_from_spot_request(spot_request_id)
493
- end
494
-
495
- def create_spot_request
496
- request_duration = config[:spot_wait]
497
472
  config_spot_price = config[:spot_price].to_s
498
473
  if %w{ondemand on-demand}.include?(config_spot_price)
499
474
  spot_price = ""
500
475
  else
501
476
  spot_price = config_spot_price
502
477
  end
503
- request_data = {
504
- spot_price: spot_price,
505
- launch_specification: instance_generator.ec2_instance_data,
506
- valid_until: Time.now + request_duration,
478
+ spot_options = {
479
+ # Must use one-time in order to use instance_interruption_behavior=terminate
480
+ # spot_instance_type: "one-time", # default
481
+ # Must use instance_interruption_behavior=terminate in order to use block_duration_minutes
482
+ # instance_interruption_behavior: "terminate", # default
507
483
  }
508
484
  if config[:block_duration_minutes]
509
- request_data[:block_duration_minutes] = config[:block_duration_minutes]
485
+ spot_options[:block_duration_minutes] = config[:block_duration_minutes]
510
486
  end
511
-
512
- response = ec2.client.request_spot_instances(request_data)
513
- response[:spot_instance_requests][0][:spot_instance_request_id]
514
- end
515
-
516
- def tag_server(server)
517
- if config[:tags] && !config[:tags].empty?
518
- tags = config[:tags].map do |k, v|
519
- # we convert the value to a string because
520
- # nils should be passed as an empty String
521
- # and Integers need to be represented as Strings
522
- { key: k.to_s, value: v.to_s }
523
- end
524
- server.create_tags(tags: tags)
487
+ unless spot_price == "" # i.e. on-demand
488
+ spot_options[:max_price] = spot_price
525
489
  end
526
- end
527
490
 
528
- def tag_volumes(server)
529
- if config[:tags] && !config[:tags].empty?
530
- tags = config[:tags].map do |k, v|
531
- { key: k.to_s, value: v.to_s }
532
- end
533
- server.volumes.each do |volume|
534
- volume.create_tags(tags: tags)
535
- end
536
- end
537
- end
491
+ instance_data[:instance_market_options] = {
492
+ market_type: "spot",
493
+ spot_options: spot_options,
494
+ }
538
495
 
539
- # Compares the requested volume count vs what has actually been set to be
540
- # attached to the instance. The information requested through
541
- # ec2.client.described_volumes is updated before the instance volume
542
- # information.
543
- def wait_until_volumes_ready(server, state)
544
- wait_with_destroy(server, state, "volumes to be ready") do |aws_instance|
545
- described_volume_count = 0
546
- ready_volume_count = 0
547
- if aws_instance.exists?
548
- described_volume_count = ec2.client.describe_volumes(filters: [
549
- { name: "attachment.instance-id", values: ["#{state[:server_id]}"] }]).volumes.length
550
- aws_instance.volumes.each { ready_volume_count += 1 }
551
- end
552
- (described_volume_count > 0) && (described_volume_count == ready_volume_count)
496
+ # The preferred way to create a spot instance is via request_spot_instances()
497
+ # However, it does not allow for tagging to occur at creation time.
498
+ # create_instances() allows creation of tagged spot instances, but does
499
+ # not retry if the price could not be satisfied immediately.
500
+ Retryable.retryable(
501
+ tries: config[:spot_wait] / config[:retryable_sleep],
502
+ sleep: lambda { |_n| config[:retryable_sleep] },
503
+ on: ::Aws::EC2::Errors::SpotMaxPriceTooLow
504
+ ) do |retries|
505
+ c = retries * config[:retryable_sleep]
506
+ t = config[:spot_wait]
507
+ info "Waited #{c}/#{t}s for spot request to become fulfilled."
508
+ ec2.create_instance(instance_data)
553
509
  end
554
510
  end
555
511
 
@@ -825,7 +781,9 @@ module Kitchen
825
781
  ip_protocol: "tcp",
826
782
  from_port: port,
827
783
  to_port: port,
828
- ip_ranges: [{ cidr_ip: config[:security_group_cidr_ip] }],
784
+ ip_ranges: Array(config[:security_group_cidr_ip]).map do |cidr_ip|
785
+ { cidr_ip: cidr_ip }
786
+ end,
829
787
  }
830
788
  end
831
789
  )
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  #
3
2
  # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
3
  #
@@ -22,6 +21,6 @@ module Kitchen
22
21
  module Driver
23
22
 
24
23
  # Version string for EC2 Test Kitchen driver
25
- EC2_VERSION = "3.6.0".freeze
24
+ EC2_VERSION = "3.9.0".freeze
26
25
  end
27
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-ec2
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fletcher Nichol
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-17 00:00:00.000000000 Z
11
+ date: 2021-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: test-kitchen
@@ -30,34 +30,6 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '3'
33
- - !ruby/object:Gem::Dependency
34
- name: excon
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: '0'
40
- type: :runtime
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '0'
47
- - !ruby/object:Gem::Dependency
48
- name: multi_json
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '0'
61
33
  - !ruby/object:Gem::Dependency
62
34
  name: aws-sdk-ec2
63
35
  requirement: !ruby/object:Gem::Requirement
@@ -92,104 +64,6 @@ dependencies:
92
64
  - - "<"
93
65
  - !ruby/object:Gem::Version
94
66
  version: '4.0'
95
- - !ruby/object:Gem::Dependency
96
- name: rspec
97
- requirement: !ruby/object:Gem::Requirement
98
- requirements:
99
- - - "~>"
100
- - !ruby/object:Gem::Version
101
- version: '3.2'
102
- type: :development
103
- prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
105
- requirements:
106
- - - "~>"
107
- - !ruby/object:Gem::Version
108
- version: '3.2'
109
- - !ruby/object:Gem::Dependency
110
- name: countloc
111
- requirement: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - "~>"
114
- - !ruby/object:Gem::Version
115
- version: '0.4'
116
- type: :development
117
- prerelease: false
118
- version_requirements: !ruby/object:Gem::Requirement
119
- requirements:
120
- - - "~>"
121
- - !ruby/object:Gem::Version
122
- version: '0.4'
123
- - !ruby/object:Gem::Dependency
124
- name: maruku
125
- requirement: !ruby/object:Gem::Requirement
126
- requirements:
127
- - - "~>"
128
- - !ruby/object:Gem::Version
129
- version: '0.6'
130
- type: :development
131
- prerelease: false
132
- version_requirements: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - "~>"
135
- - !ruby/object:Gem::Version
136
- version: '0.6'
137
- - !ruby/object:Gem::Dependency
138
- name: simplecov
139
- requirement: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - "~>"
142
- - !ruby/object:Gem::Version
143
- version: '0.7'
144
- type: :development
145
- prerelease: false
146
- version_requirements: !ruby/object:Gem::Requirement
147
- requirements:
148
- - - "~>"
149
- - !ruby/object:Gem::Version
150
- version: '0.7'
151
- - !ruby/object:Gem::Dependency
152
- name: yard
153
- requirement: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - ">="
156
- - !ruby/object:Gem::Version
157
- version: 0.9.11
158
- type: :development
159
- prerelease: false
160
- version_requirements: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - ">="
163
- - !ruby/object:Gem::Version
164
- version: 0.9.11
165
- - !ruby/object:Gem::Dependency
166
- name: chefstyle
167
- requirement: !ruby/object:Gem::Requirement
168
- requirements:
169
- - - '='
170
- - !ruby/object:Gem::Version
171
- version: 1.0.5
172
- type: :development
173
- prerelease: false
174
- version_requirements: !ruby/object:Gem::Requirement
175
- requirements:
176
- - - '='
177
- - !ruby/object:Gem::Version
178
- version: 1.0.5
179
- - !ruby/object:Gem::Dependency
180
- name: climate_control
181
- requirement: !ruby/object:Gem::Requirement
182
- requirements:
183
- - - ">="
184
- - !ruby/object:Gem::Version
185
- version: '0'
186
- type: :development
187
- prerelease: false
188
- version_requirements: !ruby/object:Gem::Requirement
189
- requirements:
190
- - - ">="
191
- - !ruby/object:Gem::Version
192
- version: '0'
193
67
  description: A Test Kitchen Driver for Amazon EC2
194
68
  email:
195
69
  - fnichol@nichol.ca
@@ -216,7 +90,7 @@ homepage: https://github.com/test-kitchen/kitchen-ec2
216
90
  licenses:
217
91
  - Apache-2.0
218
92
  metadata: {}
219
- post_install_message:
93
+ post_install_message:
220
94
  rdoc_options: []
221
95
  require_paths:
222
96
  - lib
@@ -224,15 +98,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
98
  requirements:
225
99
  - - ">="
226
100
  - !ruby/object:Gem::Version
227
- version: '2.4'
101
+ version: '2.5'
228
102
  required_rubygems_version: !ruby/object:Gem::Requirement
229
103
  requirements:
230
104
  - - ">="
231
105
  - !ruby/object:Gem::Version
232
106
  version: '0'
233
107
  requirements: []
234
- rubygems_version: 3.1.2
235
- signing_key:
108
+ rubygems_version: 3.2.15
109
+ signing_key:
236
110
  specification_version: 4
237
111
  summary: A Test Kitchen Driver for Amazon EC2
238
112
  test_files: []