kitchen-ec2 3.6.0 → 3.7.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.
- checksums.yaml +4 -4
- data/lib/kitchen/driver/aws/instance_generator.rb +14 -0
- data/lib/kitchen/driver/ec2.rb +34 -97
- data/lib/kitchen/driver/ec2_version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d6429e51c08d43c0e1084b9acc55b76feacae05bbe42b7cf3a1e35e0de70f87
|
|
4
|
+
data.tar.gz: 9f2a0adbe2866ad6a267ab8adc14d24a890b22b71b0abe272808a2a52def3933
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa308480b37d109ec173851aefe0098523d2a59c06481e88004bf76ccb8d87dfbd865300d2383157af13b38af47588ec2d1adf0541734f7279669a8dbcb0b673
|
|
7
|
+
data.tar.gz: 48f6f1a3dc22caf5d04079ef1579c19850e7e72558708e633537adb752969c3aac1b79702804864fac3fad9140e8d9053347c3ecde47e578e0545366d0b0b619
|
|
@@ -115,8 +115,22 @@ module Kitchen
|
|
|
115
115
|
key_name: config[:aws_ssh_key_id],
|
|
116
116
|
subnet_id: config[:subnet_id],
|
|
117
117
|
private_ip_address: config[:private_ip_address],
|
|
118
|
+
min_count: 1,
|
|
119
|
+
max_count: 1,
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
if config[:tags] && !config[:tags].empty?
|
|
123
|
+
tags = config[:tags].map do |k, v|
|
|
124
|
+
# we convert the value to a string because
|
|
125
|
+
# nils should be passed as an empty String
|
|
126
|
+
# and Integers need to be represented as Strings
|
|
127
|
+
{ key: k, value: v.to_s }
|
|
128
|
+
end
|
|
129
|
+
instance_tag_spec = { resource_type: "instance", tags: tags }
|
|
130
|
+
volume_tag_spec = { resource_type: "volume", tags: tags }
|
|
131
|
+
i[:tag_specifications] = [instance_tag_spec, volume_tag_spec]
|
|
132
|
+
end
|
|
133
|
+
|
|
120
134
|
availability_zone = config[:availability_zone]
|
|
121
135
|
if availability_zone
|
|
122
136
|
if availability_zone =~ /^[a-z]$/i
|
data/lib/kitchen/driver/ec2.rb
CHANGED
|
@@ -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
|
|
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
|
-
#
|
|
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
|
|
|
@@ -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.
|
|
@@ -409,15 +386,14 @@ module Kitchen
|
|
|
409
386
|
@instance_generator = Aws::InstanceGenerator.new(config, ec2, instance.logger)
|
|
410
387
|
end
|
|
411
388
|
|
|
412
|
-
#
|
|
389
|
+
# AWS helper for creating the instance
|
|
413
390
|
def submit_server
|
|
414
391
|
instance_data = instance_generator.ec2_instance_data
|
|
415
392
|
debug("Creating EC2 instance in region #{config[:region]} with properties:")
|
|
416
393
|
instance_data.each do |key, value|
|
|
417
394
|
debug("- #{key} = #{value.inspect}")
|
|
418
395
|
end
|
|
419
|
-
|
|
420
|
-
instance_data[:max_count] = 1
|
|
396
|
+
|
|
421
397
|
ec2.create_instance(instance_data)
|
|
422
398
|
end
|
|
423
399
|
|
|
@@ -445,7 +421,7 @@ module Kitchen
|
|
|
445
421
|
configs
|
|
446
422
|
end
|
|
447
423
|
|
|
448
|
-
def submit_spots
|
|
424
|
+
def submit_spots
|
|
449
425
|
configs = [config]
|
|
450
426
|
expanded = []
|
|
451
427
|
keys = %i{instance_type subnet_id}
|
|
@@ -462,7 +438,7 @@ module Kitchen
|
|
|
462
438
|
configs.each do |conf|
|
|
463
439
|
begin
|
|
464
440
|
@config = conf
|
|
465
|
-
return submit_spot
|
|
441
|
+
return submit_spot
|
|
466
442
|
rescue => e
|
|
467
443
|
errs.append(e)
|
|
468
444
|
end
|
|
@@ -470,29 +446,10 @@ module Kitchen
|
|
|
470
446
|
raise ["Could not create a spot instance:", errs].flatten.join("\n")
|
|
471
447
|
end
|
|
472
448
|
|
|
473
|
-
def submit_spot
|
|
449
|
+
def submit_spot
|
|
474
450
|
debug("Creating EC2 Spot Instance..")
|
|
451
|
+
instance_data = instance_generator.ec2_instance_data
|
|
475
452
|
|
|
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
453
|
request_duration = config[:spot_wait]
|
|
497
454
|
config_spot_price = config[:spot_price].to_s
|
|
498
455
|
if %w{ondemand on-demand}.include?(config_spot_price)
|
|
@@ -500,56 +457,36 @@ module Kitchen
|
|
|
500
457
|
else
|
|
501
458
|
spot_price = config_spot_price
|
|
502
459
|
end
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
launch_specification: instance_generator.ec2_instance_data,
|
|
460
|
+
spot_options = {
|
|
461
|
+
spot_instance_type: "persistent", # Cannot use one-time with valid_until
|
|
506
462
|
valid_until: Time.now + request_duration,
|
|
463
|
+
instance_interruption_behavior: "stop",
|
|
507
464
|
}
|
|
508
465
|
if config[:block_duration_minutes]
|
|
509
|
-
|
|
466
|
+
spot_options[:block_duration_minutes] = config[:block_duration_minutes]
|
|
510
467
|
end
|
|
511
|
-
|
|
512
|
-
|
|
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)
|
|
468
|
+
unless spot_price == "" # i.e. on-demand
|
|
469
|
+
spot_options[:max_price] = spot_price
|
|
525
470
|
end
|
|
526
|
-
end
|
|
527
471
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
end
|
|
533
|
-
server.volumes.each do |volume|
|
|
534
|
-
volume.create_tags(tags: tags)
|
|
535
|
-
end
|
|
536
|
-
end
|
|
537
|
-
end
|
|
472
|
+
instance_data[:instance_market_options] = {
|
|
473
|
+
market_type: "spot",
|
|
474
|
+
spot_options: spot_options,
|
|
475
|
+
}
|
|
538
476
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
(described_volume_count > 0) && (described_volume_count == ready_volume_count)
|
|
477
|
+
# The preferred way to create a spot instance is via request_spot_instances()
|
|
478
|
+
# However, it does not allow for tagging to occur at creation time.
|
|
479
|
+
# create_instances() allows creation of tagged spot instances, but does
|
|
480
|
+
# not retry if the price could not be satisfied immediately.
|
|
481
|
+
Retryable.retryable(
|
|
482
|
+
tries: config[:spot_wait] / config[:retryable_sleep],
|
|
483
|
+
sleep: lambda { |_n| config[:retryable_sleep] },
|
|
484
|
+
on: ::Aws::EC2::Errors::SpotMaxPriceTooLow
|
|
485
|
+
) do |retries|
|
|
486
|
+
c = retries * config[:retryable_sleep]
|
|
487
|
+
t = config[:spot_wait]
|
|
488
|
+
info "Waited #{c}/#{t}s for spot request to become fulfilled."
|
|
489
|
+
ec2.create_instance(instance_data)
|
|
553
490
|
end
|
|
554
491
|
end
|
|
555
492
|
|
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.
|
|
4
|
+
version: 3.7.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-
|
|
11
|
+
date: 2020-07-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: test-kitchen
|
|
@@ -168,14 +168,14 @@ dependencies:
|
|
|
168
168
|
requirements:
|
|
169
169
|
- - '='
|
|
170
170
|
- !ruby/object:Gem::Version
|
|
171
|
-
version: 1.
|
|
171
|
+
version: 1.1.2
|
|
172
172
|
type: :development
|
|
173
173
|
prerelease: false
|
|
174
174
|
version_requirements: !ruby/object:Gem::Requirement
|
|
175
175
|
requirements:
|
|
176
176
|
- - '='
|
|
177
177
|
- !ruby/object:Gem::Version
|
|
178
|
-
version: 1.
|
|
178
|
+
version: 1.1.2
|
|
179
179
|
- !ruby/object:Gem::Dependency
|
|
180
180
|
name: climate_control
|
|
181
181
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,7 +216,7 @@ homepage: https://github.com/test-kitchen/kitchen-ec2
|
|
|
216
216
|
licenses:
|
|
217
217
|
- Apache-2.0
|
|
218
218
|
metadata: {}
|
|
219
|
-
post_install_message:
|
|
219
|
+
post_install_message:
|
|
220
220
|
rdoc_options: []
|
|
221
221
|
require_paths:
|
|
222
222
|
- lib
|
|
@@ -232,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
232
232
|
version: '0'
|
|
233
233
|
requirements: []
|
|
234
234
|
rubygems_version: 3.1.2
|
|
235
|
-
signing_key:
|
|
235
|
+
signing_key:
|
|
236
236
|
specification_version: 4
|
|
237
237
|
summary: A Test Kitchen Driver for Amazon EC2
|
|
238
238
|
test_files: []
|