kitchen-ec2 3.6.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|