kitchen-dokken 2.9.1 → 2.12.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/dokken.rb +139 -117
- data/lib/kitchen/driver/dokken_version.rb +1 -1
- data/lib/kitchen/helpers.rb +49 -46
- data/lib/kitchen/provisioner/dokken.rb +19 -16
- data/lib/kitchen/transport/dokken.rb +36 -35
- metadata +16 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e00103fefe2a49fd2630337f2a8d0bb0932484b770704b69c1fb31d9ddd0b42
|
|
4
|
+
data.tar.gz: 0acea78f249845712be9d1f97094f190369a1e15707ac559de27be5fc6c29e31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d84bd393b040ef4bc771a5d5c4448dff1860a9244d0818341f06c834e8133cbd9a77b0b7a4f697c8873be24b567083556f4feb47a57723782fe6575ad7b2a1a
|
|
7
|
+
data.tar.gz: b628d6bce7009efa94713c5c6032ec81e3258686c84f22ebd430e1710a1817cfaf3cb82420a8109e21f1506ff0e661c08da1e6fb2d5baedd6adbb216a0dedaa8
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
-
require
|
|
19
|
-
require
|
|
20
|
-
require
|
|
21
|
-
require
|
|
22
|
-
require
|
|
23
|
-
require_relative
|
|
18
|
+
require "digest" unless defined?(Digest)
|
|
19
|
+
require "kitchen"
|
|
20
|
+
require "tmpdir" unless defined?(Dir.mktmpdir)
|
|
21
|
+
require "docker"
|
|
22
|
+
require "lockfile"
|
|
23
|
+
require_relative "../helpers"
|
|
24
24
|
|
|
25
25
|
include Dokken::Helpers
|
|
26
26
|
|
|
@@ -37,30 +37,32 @@ module Kitchen
|
|
|
37
37
|
default_config :binds, []
|
|
38
38
|
default_config :cap_add, nil
|
|
39
39
|
default_config :cap_drop, nil
|
|
40
|
-
default_config :chef_image,
|
|
41
|
-
default_config :chef_version,
|
|
42
|
-
default_config :data_image,
|
|
40
|
+
default_config :chef_image, "chef/chef"
|
|
41
|
+
default_config :chef_version, "latest"
|
|
42
|
+
default_config :data_image, "dokken/kitchen-cache:latest"
|
|
43
43
|
default_config :dns, nil
|
|
44
44
|
default_config :dns_search, nil
|
|
45
|
+
default_config :docker_host_url, default_docker_host
|
|
45
46
|
default_config :docker_info, docker_info
|
|
47
|
+
default_config :docker_registry, nil
|
|
46
48
|
default_config :entrypoint, nil
|
|
47
49
|
default_config :env, nil
|
|
48
|
-
default_config :
|
|
49
|
-
default_config :docker_host_url, default_docker_host
|
|
50
|
-
default_config :hostname, 'dokken'
|
|
50
|
+
default_config :hostname, "dokken"
|
|
51
51
|
default_config :image_prefix, nil
|
|
52
52
|
default_config :links, nil
|
|
53
|
-
default_config :
|
|
53
|
+
default_config :memory_limit, 0
|
|
54
|
+
default_config :network_mode, "dokken"
|
|
54
55
|
default_config :pid_one_command, 'sh -c "trap exit 0 SIGTERM; while :; do sleep 1; done"'
|
|
56
|
+
default_config :ports, nil
|
|
55
57
|
default_config :privileged, false
|
|
58
|
+
default_config :pull_chef_image, true
|
|
59
|
+
default_config :pull_platform_image, true
|
|
56
60
|
default_config :read_timeout, 3600
|
|
57
61
|
default_config :security_opt, nil
|
|
58
62
|
default_config :tmpfs, {}
|
|
63
|
+
default_config :userns_host, false
|
|
59
64
|
default_config :volumes, nil
|
|
60
65
|
default_config :write_timeout, 3600
|
|
61
|
-
default_config :userns_host, false
|
|
62
|
-
default_config :pull_platform_image, true
|
|
63
|
-
default_config :pull_chef_image, true
|
|
64
66
|
|
|
65
67
|
# (see Base#create)
|
|
66
68
|
def create(state)
|
|
@@ -125,37 +127,38 @@ module Kitchen
|
|
|
125
127
|
|
|
126
128
|
def delete_work_image
|
|
127
129
|
return unless ::Docker::Image.exist?(work_image, {}, docker_connection)
|
|
130
|
+
|
|
128
131
|
with_retries { @work_image = ::Docker::Image.get(work_image, {}, docker_connection) }
|
|
129
132
|
|
|
130
133
|
with_retries do
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
|
|
135
|
+
with_retries { @work_image.remove(force: true) }
|
|
136
|
+
rescue ::Docker::Error::ConflictError
|
|
137
|
+
debug "driver - #{work_image} cannot be removed"
|
|
138
|
+
|
|
136
139
|
end
|
|
137
140
|
end
|
|
138
141
|
|
|
139
142
|
def build_work_image(state)
|
|
140
|
-
info(
|
|
143
|
+
info("Building work image..")
|
|
141
144
|
return if ::Docker::Image.exist?(work_image, {}, docker_connection)
|
|
142
145
|
|
|
143
146
|
begin
|
|
144
147
|
@intermediate_image = ::Docker::Image.build(
|
|
145
148
|
work_image_dockerfile,
|
|
146
149
|
{
|
|
147
|
-
|
|
150
|
+
"t" => work_image,
|
|
148
151
|
},
|
|
149
152
|
docker_connection
|
|
150
153
|
)
|
|
151
154
|
# credit to https://github.com/someara/kitchen-dokken/issues/95#issue-224697526
|
|
152
155
|
rescue Docker::Error::UnexpectedResponseError => e
|
|
153
|
-
msg =
|
|
154
|
-
msg += JSON.parse(e.to_s.split("\r\n").last)[
|
|
155
|
-
msg +=
|
|
156
|
-
msg +=
|
|
157
|
-
msg +=
|
|
158
|
-
msg +=
|
|
156
|
+
msg = "work_image build failed: "
|
|
157
|
+
msg += JSON.parse(e.to_s.split("\r\n").last)["error"].to_s
|
|
158
|
+
msg += ". The common scenarios are incorrect intermediate "
|
|
159
|
+
msg += "instructions such as not including `-y` on an `apt-get` "
|
|
160
|
+
msg += "or similar. The other common scenario is a transient "
|
|
161
|
+
msg += "error such as an unresponsive mirror."
|
|
159
162
|
raise msg
|
|
160
163
|
# fallback rescue above should catch most of the errors
|
|
161
164
|
rescue => e
|
|
@@ -218,6 +221,7 @@ module Kitchen
|
|
|
218
221
|
|
|
219
222
|
def work_image
|
|
220
223
|
return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
|
|
224
|
+
|
|
221
225
|
instance_name
|
|
222
226
|
end
|
|
223
227
|
|
|
@@ -243,7 +247,7 @@ module Kitchen
|
|
|
243
247
|
v
|
|
244
248
|
else
|
|
245
249
|
Array(v).each_with_object({}) do |y, h|
|
|
246
|
-
name, opts = y.split(
|
|
250
|
+
name, opts = y.split(":", 2)
|
|
247
251
|
h[name.to_s] = opts.to_s
|
|
248
252
|
end
|
|
249
253
|
end
|
|
@@ -259,12 +263,13 @@ module Kitchen
|
|
|
259
263
|
b = []
|
|
260
264
|
v = Array(v).to_a # in case v.is_A?(Chef::Node::ImmutableArray)
|
|
261
265
|
v.delete_if do |x|
|
|
262
|
-
parts = x.split(
|
|
266
|
+
parts = x.split(":")
|
|
263
267
|
b << x if parts.length > 1
|
|
264
268
|
end
|
|
265
269
|
b = nil if b.empty?
|
|
266
270
|
config[:binds].push(b) unless config[:binds].include?(b) || b.nil?
|
|
267
271
|
return PartialHash.new if v.empty?
|
|
272
|
+
|
|
268
273
|
v.each_with_object(PartialHash.new) { |volume, h| h[volume] = {} }
|
|
269
274
|
end
|
|
270
275
|
end
|
|
@@ -280,40 +285,41 @@ module Kitchen
|
|
|
280
285
|
debug "driver - starting #{runner_container_name}"
|
|
281
286
|
|
|
282
287
|
config = {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
288
|
+
"name" => runner_container_name,
|
|
289
|
+
"Cmd" => Shellwords.shellwords(self[:pid_one_command]),
|
|
290
|
+
"Image" => "#{repo(work_image)}:#{tag(work_image)}",
|
|
291
|
+
"Hostname" => self[:hostname],
|
|
292
|
+
"Env" => self[:env],
|
|
293
|
+
"ExposedPorts" => exposed_ports,
|
|
294
|
+
"Volumes" => dokken_volumes,
|
|
295
|
+
"HostConfig" => {
|
|
296
|
+
"Privileged" => self[:privileged],
|
|
297
|
+
"VolumesFrom" => dokken_volumes_from,
|
|
298
|
+
"Binds" => dokken_binds,
|
|
299
|
+
"Dns" => self[:dns],
|
|
300
|
+
"DnsSearch" => self[:dns_search],
|
|
301
|
+
"Links" => Array(self[:links]),
|
|
302
|
+
"CapAdd" => Array(self[:cap_add]),
|
|
303
|
+
"CapDrop" => Array(self[:cap_drop]),
|
|
304
|
+
"SecurityOpt" => Array(self[:security_opt]),
|
|
305
|
+
"NetworkMode" => self[:network_mode],
|
|
306
|
+
"PortBindings" => port_bindings,
|
|
307
|
+
"Tmpfs" => dokken_tmpfs,
|
|
308
|
+
"Memory" => self[:memory_limit],
|
|
303
309
|
},
|
|
304
|
-
|
|
305
|
-
|
|
310
|
+
"NetworkingConfig" => {
|
|
311
|
+
"EndpointsConfig" => {
|
|
306
312
|
self[:network_mode] => {
|
|
307
|
-
|
|
313
|
+
"Aliases" => Array(self[:hostname]),
|
|
308
314
|
},
|
|
309
315
|
},
|
|
310
316
|
},
|
|
311
317
|
}
|
|
312
318
|
unless self[:entrypoint].to_s.empty?
|
|
313
|
-
config[
|
|
319
|
+
config["Entrypoint"] = self[:entrypoint]
|
|
314
320
|
end
|
|
315
321
|
if self[:userns_host]
|
|
316
|
-
config[
|
|
322
|
+
config["HostConfig"]["UsernsMode"] = "host"
|
|
317
323
|
end
|
|
318
324
|
runner_container = run_container(config)
|
|
319
325
|
state[:runner_container] = runner_container.json
|
|
@@ -322,17 +328,17 @@ module Kitchen
|
|
|
322
328
|
def start_data_container(state)
|
|
323
329
|
debug "driver - creating #{data_container_name}"
|
|
324
330
|
config = {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
"name" => data_container_name,
|
|
332
|
+
"Image" => "#{repo(data_image)}:#{tag(data_image)}",
|
|
333
|
+
"HostConfig" => {
|
|
334
|
+
"PortBindings" => port_bindings,
|
|
335
|
+
"PublishAllPorts" => true,
|
|
336
|
+
"NetworkMode" => "bridge",
|
|
331
337
|
},
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
"NetworkingConfig" => {
|
|
339
|
+
"EndpointsConfig" => {
|
|
334
340
|
self[:network_mode] => {
|
|
335
|
-
|
|
341
|
+
"Aliases" => Array(self[:hostname]),
|
|
336
342
|
},
|
|
337
343
|
},
|
|
338
344
|
},
|
|
@@ -345,10 +351,10 @@ module Kitchen
|
|
|
345
351
|
lockfile = Lockfile.new "#{home_dir}/.dokken-network.lock"
|
|
346
352
|
begin
|
|
347
353
|
lockfile.lock
|
|
348
|
-
with_retries { ::Docker::Network.get(
|
|
354
|
+
with_retries { ::Docker::Network.get("dokken", {}, docker_connection) }
|
|
349
355
|
rescue
|
|
350
356
|
begin
|
|
351
|
-
with_retries { ::Docker::Network.create(
|
|
357
|
+
with_retries { ::Docker::Network.create("dokken", {}) }
|
|
352
358
|
rescue ::Docker::Error => e
|
|
353
359
|
debug "driver - error :#{e}:"
|
|
354
360
|
end
|
|
@@ -358,32 +364,39 @@ module Kitchen
|
|
|
358
364
|
end
|
|
359
365
|
|
|
360
366
|
def make_data_image
|
|
361
|
-
debug
|
|
362
|
-
create_data_image
|
|
367
|
+
debug "driver - calling create_data_image"
|
|
368
|
+
create_data_image(config[:docker_registry])
|
|
363
369
|
end
|
|
364
370
|
|
|
365
371
|
def create_chef_container(state)
|
|
366
372
|
lockfile = Lockfile.new "#{home_dir}/.dokken-#{chef_container_name}.lock"
|
|
367
373
|
begin
|
|
368
374
|
lockfile.lock
|
|
369
|
-
with_retries {
|
|
375
|
+
with_retries {
|
|
376
|
+
# TEMPORARY FIX - docker-api 2.0.0 has a buggy Docker::Container.get - use .all instead
|
|
377
|
+
# https://github.com/swipely/docker-api/issues/566
|
|
378
|
+
# ::Docker::Container.get(chef_container_name, {}, docker_connection)
|
|
379
|
+
found = ::Docker::Container.all({ all: true }, docker_connection).select { |c| c.info["Names"].include?("/#{chef_container_name}") }
|
|
380
|
+
raise ::Docker::Error::NotFoundError.new(chef_container_name) if found.empty?
|
|
381
|
+
|
|
382
|
+
debug "Chef container already exists, continuing"
|
|
383
|
+
}
|
|
370
384
|
rescue ::Docker::Error::NotFoundError
|
|
385
|
+
debug "Chef container does not exist, creating a new Chef container"
|
|
371
386
|
with_retries do
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
raise "driver - #{chef_container_name} failed to create #{e}"
|
|
386
|
-
end
|
|
387
|
+
debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
|
|
388
|
+
config = {
|
|
389
|
+
"name" => chef_container_name,
|
|
390
|
+
"Cmd" => "true",
|
|
391
|
+
"Image" => "#{repo(chef_image)}:#{tag(chef_image)}",
|
|
392
|
+
"HostConfig" => {
|
|
393
|
+
"NetworkMode" => self[:network_mode],
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
chef_container = create_container(config)
|
|
397
|
+
state[:chef_container] = chef_container.json
|
|
398
|
+
rescue ::Docker::Error => e
|
|
399
|
+
raise "driver - #{chef_container_name} failed to create #{e}"
|
|
387
400
|
end
|
|
388
401
|
ensure
|
|
389
402
|
lockfile.unlock
|
|
@@ -414,13 +427,13 @@ module Kitchen
|
|
|
414
427
|
end
|
|
415
428
|
|
|
416
429
|
def parse_image_name(image)
|
|
417
|
-
parts = image.split(
|
|
430
|
+
parts = image.split(":")
|
|
418
431
|
|
|
419
432
|
if parts.size > 2
|
|
420
433
|
tag = parts.pop
|
|
421
|
-
repo = parts.join(
|
|
434
|
+
repo = parts.join(":")
|
|
422
435
|
else
|
|
423
|
-
tag = parts[1] ||
|
|
436
|
+
tag = parts[1] || "latest"
|
|
424
437
|
repo = parts[0]
|
|
425
438
|
end
|
|
426
439
|
|
|
@@ -432,27 +445,25 @@ module Kitchen
|
|
|
432
445
|
end
|
|
433
446
|
|
|
434
447
|
def create_container(args)
|
|
435
|
-
with_retries { @container = ::Docker::Container.get(args[
|
|
448
|
+
with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
|
|
436
449
|
rescue
|
|
437
450
|
with_retries do
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
with_retries { @container = ::Docker::Container.get(args['name'], {}, docker_connection) }
|
|
450
|
-
end
|
|
451
|
-
end
|
|
452
|
-
rescue ::Docker::Error => e
|
|
453
|
-
debug "driver - error :#{e}:"
|
|
454
|
-
raise "driver - failed to create_container #{args['name']}"
|
|
451
|
+
args["Env"] = [] if args["Env"].nil?
|
|
452
|
+
args["Env"] << "TEST_KITCHEN=1"
|
|
453
|
+
args["Env"] << "CI=#{ENV["CI"]}" if ENV.include? "CI"
|
|
454
|
+
info "Creating container #{args["name"]}"
|
|
455
|
+
debug "driver - create_container args #{args}"
|
|
456
|
+
with_retries do
|
|
457
|
+
|
|
458
|
+
@container = ::Docker::Container.create(args.clone, docker_connection)
|
|
459
|
+
rescue ::Docker::Error::ConflictError
|
|
460
|
+
debug "driver - rescue ConflictError: #{args["name"]}"
|
|
461
|
+
with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
|
|
455
462
|
end
|
|
463
|
+
rescue ::Docker::Error => e
|
|
464
|
+
debug "driver - error :#{e}:"
|
|
465
|
+
raise "driver - failed to create_container #{args["name"]}"
|
|
466
|
+
|
|
456
467
|
end
|
|
457
468
|
end
|
|
458
469
|
|
|
@@ -460,14 +471,14 @@ module Kitchen
|
|
|
460
471
|
create_container(args)
|
|
461
472
|
with_retries do
|
|
462
473
|
@container.start
|
|
463
|
-
@container = ::Docker::Container.get(args[
|
|
464
|
-
wait_running_state(args[
|
|
474
|
+
@container = ::Docker::Container.get(args["name"], {}, docker_connection)
|
|
475
|
+
wait_running_state(args["name"], true)
|
|
465
476
|
end
|
|
466
477
|
@container
|
|
467
478
|
end
|
|
468
479
|
|
|
469
480
|
def container_state
|
|
470
|
-
@container ? @container.info[
|
|
481
|
+
@container ? @container.info["State"] : {}
|
|
471
482
|
end
|
|
472
483
|
|
|
473
484
|
def stop_container(name)
|
|
@@ -491,9 +502,10 @@ module Kitchen
|
|
|
491
502
|
@container = ::Docker::Container.get(name, {}, docker_connection)
|
|
492
503
|
i = 0
|
|
493
504
|
tries = 20
|
|
494
|
-
until container_state[
|
|
505
|
+
until container_state["Running"] == v || container_state["FinishedAt"] != "0001-01-01T00:00:00Z"
|
|
495
506
|
i += 1
|
|
496
507
|
break if i == tries
|
|
508
|
+
|
|
497
509
|
sleep 0.1
|
|
498
510
|
@container = ::Docker::Container.get(name, {}, docker_connection)
|
|
499
511
|
end
|
|
@@ -512,7 +524,8 @@ module Kitchen
|
|
|
512
524
|
end
|
|
513
525
|
|
|
514
526
|
def chef_version
|
|
515
|
-
return
|
|
527
|
+
return "latest" if config[:chef_version] == "stable"
|
|
528
|
+
|
|
516
529
|
config[:chef_version]
|
|
517
530
|
end
|
|
518
531
|
|
|
@@ -529,27 +542,36 @@ module Kitchen
|
|
|
529
542
|
end
|
|
530
543
|
|
|
531
544
|
def platform_image_from_name
|
|
532
|
-
platform, release = instance.platform.name.split(
|
|
533
|
-
release ? [platform, release].join(
|
|
545
|
+
platform, release = instance.platform.name.split("-")
|
|
546
|
+
release ? [platform, release].join(":") : platform
|
|
534
547
|
end
|
|
535
548
|
|
|
536
549
|
def pull_if_missing(image)
|
|
537
550
|
return if ::Docker::Image.exist?("#{repo(image)}:#{tag(image)}", {}, docker_connection)
|
|
551
|
+
|
|
538
552
|
pull_image image
|
|
539
553
|
end
|
|
540
554
|
|
|
541
555
|
# https://github.com/docker/docker/blob/4fcb9ac40ce33c4d6e08d5669af6be5e076e2574/registry/auth.go#L231
|
|
542
556
|
def parse_registry_host(val)
|
|
543
|
-
val.sub(%r{https?://},
|
|
557
|
+
val.sub(%r{https?://}, "").split("/").first
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def image_path(image)
|
|
561
|
+
fqimage = "#{repo(image)}:#{tag(image)}"
|
|
562
|
+
if config[:docker_registry]
|
|
563
|
+
fqimage = "#{config[:docker_registry]}/#{fqimage}"
|
|
564
|
+
end
|
|
565
|
+
fqimage
|
|
544
566
|
end
|
|
545
567
|
|
|
546
568
|
def pull_image(image)
|
|
547
569
|
with_retries do
|
|
548
|
-
if Docker::Image.exist?(
|
|
549
|
-
original_image = Docker::Image.get(
|
|
570
|
+
if Docker::Image.exist?(image_path(image), {}, docker_connection)
|
|
571
|
+
original_image = Docker::Image.get(image_path(image), {}, docker_connection)
|
|
550
572
|
end
|
|
551
573
|
|
|
552
|
-
new_image = Docker::Image.create({
|
|
574
|
+
new_image = Docker::Image.create({ "fromImage" => "#{repo(image)}:#{tag(image)}" }, docker_connection)
|
|
553
575
|
|
|
554
576
|
!(original_image && original_image.id.start_with?(new_image.id))
|
|
555
577
|
end
|
data/lib/kitchen/helpers.rb
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
module Dokken
|
|
2
2
|
module Helpers
|
|
3
3
|
# https://stackoverflow.com/questions/517219/ruby-see-if-a-port-is-open
|
|
4
|
-
require
|
|
5
|
-
require
|
|
4
|
+
require "socket" unless defined?(Socket)
|
|
5
|
+
require "timeout" unless defined?(Timeout)
|
|
6
6
|
|
|
7
7
|
def port_open?(ip, port)
|
|
8
8
|
begin
|
|
9
9
|
Timeout.timeout(1) do
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return false
|
|
16
|
-
end
|
|
10
|
+
s = TCPSocket.new(ip, port)
|
|
11
|
+
s.close
|
|
12
|
+
return true
|
|
13
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN
|
|
14
|
+
return false
|
|
17
15
|
end
|
|
18
16
|
rescue Timeout::Error
|
|
19
17
|
end
|
|
20
|
-
|
|
21
18
|
false
|
|
22
19
|
end
|
|
23
20
|
|
|
@@ -59,9 +56,13 @@ X8N2N9ZNnORJqK374yGj1jWUU66mQhPvn49QpG8P2HEoh2RQjNvyHA==
|
|
|
59
56
|
EOF
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
def data_dockerfile
|
|
59
|
+
def data_dockerfile(registry)
|
|
60
|
+
from = "centos:7"
|
|
61
|
+
if registry
|
|
62
|
+
from = "#{registry}/#{from}"
|
|
63
|
+
end
|
|
63
64
|
<<-EOF
|
|
64
|
-
FROM
|
|
65
|
+
FROM #{from}
|
|
65
66
|
MAINTAINER Sean OMeara \"sean@sean.io\"
|
|
66
67
|
ENV LANG en_US.UTF-8
|
|
67
68
|
|
|
@@ -86,32 +87,32 @@ VOLUME /opt/verifier
|
|
|
86
87
|
EOF
|
|
87
88
|
end
|
|
88
89
|
|
|
89
|
-
def create_data_image
|
|
90
|
+
def create_data_image(registry)
|
|
90
91
|
return if ::Docker::Image.exist?(data_image)
|
|
91
92
|
|
|
92
93
|
tmpdir = Dir.tmpdir
|
|
93
94
|
FileUtils.mkdir_p "#{tmpdir}/dokken"
|
|
94
|
-
File.write("#{tmpdir}/dokken/Dockerfile", data_dockerfile)
|
|
95
|
+
File.write("#{tmpdir}/dokken/Dockerfile", data_dockerfile(registry))
|
|
95
96
|
File.write("#{tmpdir}/dokken/authorized_keys", insecure_ssh_public_key)
|
|
96
97
|
|
|
97
98
|
i = ::Docker::Image.build_from_dir(
|
|
98
99
|
"#{tmpdir}/dokken",
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
"nocache" => true,
|
|
101
|
+
"rm" => true
|
|
101
102
|
)
|
|
102
|
-
i.tag(
|
|
103
|
+
i.tag("repo" => repo(data_image), "tag" => tag(data_image), "force" => true)
|
|
103
104
|
end
|
|
104
105
|
|
|
105
106
|
def default_docker_host
|
|
106
|
-
if ENV[
|
|
107
|
-
ENV[
|
|
108
|
-
elsif File.exist?(
|
|
109
|
-
|
|
107
|
+
if ENV["DOCKER_HOST"]
|
|
108
|
+
ENV["DOCKER_HOST"]
|
|
109
|
+
elsif File.exist?("/var/run/docker.sock")
|
|
110
|
+
"unix:///var/run/docker.sock"
|
|
110
111
|
# TODO: Docker for Windows also operates over a named pipe at
|
|
111
112
|
# //./pipe/docker_engine that can be used if named pipe support is
|
|
112
113
|
# added to the docker-api gem.
|
|
113
114
|
else
|
|
114
|
-
|
|
115
|
+
"tcp://127.0.0.1:2375"
|
|
115
116
|
end
|
|
116
117
|
end
|
|
117
118
|
|
|
@@ -152,7 +153,8 @@ VOLUME /opt/verifier
|
|
|
152
153
|
# refs:
|
|
153
154
|
# https://github.com/docker/machine/issues/1814
|
|
154
155
|
# https://github.com/docker/toolbox/issues/607
|
|
155
|
-
return Dir.home.sub
|
|
156
|
+
return Dir.home.sub "C:/Users", "/c/Users" if Dir.home =~ /^C:/ && remote_docker_host?
|
|
157
|
+
|
|
156
158
|
Dir.home
|
|
157
159
|
end
|
|
158
160
|
|
|
@@ -185,7 +187,7 @@ VOLUME /opt/verifier
|
|
|
185
187
|
x = Array(v).map { |a| parse_port(a) }
|
|
186
188
|
x.flatten!
|
|
187
189
|
x.each_with_object({}) do |y, h|
|
|
188
|
-
h[y[
|
|
190
|
+
h[y["container_port"]] = {}
|
|
189
191
|
end
|
|
190
192
|
end
|
|
191
193
|
end
|
|
@@ -198,52 +200,53 @@ VOLUME /opt/verifier
|
|
|
198
200
|
x = Array(v).map { |a| parse_port(a) }
|
|
199
201
|
x.flatten!
|
|
200
202
|
x.each_with_object({}) do |y, h|
|
|
201
|
-
h[y[
|
|
202
|
-
h[y[
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
h[y["container_port"]] = [] unless h[y["container_port"]]
|
|
204
|
+
h[y["container_port"]] << {
|
|
205
|
+
"HostIp" => y["host_ip"],
|
|
206
|
+
"HostPort" => y["host_port"],
|
|
205
207
|
}
|
|
206
208
|
end
|
|
207
209
|
end
|
|
208
210
|
end
|
|
209
211
|
|
|
210
212
|
def parse_port(v)
|
|
211
|
-
parts = v.split(
|
|
213
|
+
parts = v.split(":")
|
|
212
214
|
case parts.length
|
|
213
215
|
when 3
|
|
214
216
|
host_ip = parts[0]
|
|
215
217
|
host_port = parts[1]
|
|
216
218
|
container_port = parts[2]
|
|
217
219
|
when 2
|
|
218
|
-
host_ip =
|
|
220
|
+
host_ip = "0.0.0.0"
|
|
219
221
|
host_port = parts[0]
|
|
220
222
|
container_port = parts[1]
|
|
221
223
|
when 1
|
|
222
|
-
host_ip =
|
|
223
|
-
host_port =
|
|
224
|
+
host_ip = ""
|
|
225
|
+
host_port = ""
|
|
224
226
|
container_port = parts[0]
|
|
225
227
|
end
|
|
226
|
-
port_range, protocol = container_port.split(
|
|
227
|
-
if port_range.include?(
|
|
228
|
-
port_range = container_port.split(
|
|
228
|
+
port_range, protocol = container_port.split("/")
|
|
229
|
+
if port_range.include?("-")
|
|
230
|
+
port_range = container_port.split("-")
|
|
229
231
|
port_range.map!(&:to_i)
|
|
230
232
|
Chef::Log.fatal("FATAL: Invalid port range! #{container_port}") if port_range[0] > port_range[1]
|
|
231
233
|
port_range = (port_range[0]..port_range[1]).to_a
|
|
232
234
|
end
|
|
233
235
|
# qualify the port-binding protocol even when it is implicitly tcp #427.
|
|
234
|
-
protocol =
|
|
236
|
+
protocol = "tcp" if protocol.nil?
|
|
235
237
|
Array(port_range).map do |port|
|
|
236
238
|
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
"host_ip" => host_ip,
|
|
240
|
+
"host_port" => host_port,
|
|
241
|
+
"container_port" => "#{port}/#{protocol}",
|
|
240
242
|
}
|
|
241
243
|
end
|
|
242
244
|
end
|
|
243
245
|
|
|
244
246
|
def remote_docker_host?
|
|
245
|
-
return false if config[:docker_info][
|
|
246
|
-
return true if config[:docker_host_url]
|
|
247
|
+
return false if config[:docker_info]["OperatingSystem"].include?("Boot2Docker")
|
|
248
|
+
return true if /^tcp:/.match?(config[:docker_host_url])
|
|
249
|
+
|
|
247
250
|
false
|
|
248
251
|
end
|
|
249
252
|
|
|
@@ -252,13 +255,13 @@ VOLUME /opt/verifier
|
|
|
252
255
|
end
|
|
253
256
|
|
|
254
257
|
def sandbox_dirs
|
|
255
|
-
Dir.glob(File.join(sandbox_path,
|
|
258
|
+
Dir.glob(File.join(sandbox_path, "*"))
|
|
256
259
|
end
|
|
257
260
|
|
|
258
261
|
def create_sandbox
|
|
259
262
|
info("Creating kitchen sandbox in #{sandbox_path}")
|
|
260
263
|
unless ::Dir.exist?(sandbox_path)
|
|
261
|
-
FileUtils.mkdir_p(sandbox_path, :
|
|
264
|
+
FileUtils.mkdir_p(sandbox_path, mode: 0o755)
|
|
262
265
|
end
|
|
263
266
|
end
|
|
264
267
|
end
|
|
@@ -269,7 +272,7 @@ module Kitchen
|
|
|
269
272
|
class Base
|
|
270
273
|
def create_sandbox
|
|
271
274
|
info("Creating kitchen sandbox in #{sandbox_path}")
|
|
272
|
-
FileUtils.mkdir_p(sandbox_path, :
|
|
275
|
+
FileUtils.mkdir_p(sandbox_path, mode: 0o755)
|
|
273
276
|
end
|
|
274
277
|
|
|
275
278
|
# this MUST be named 'sandbox_path' because ruby.
|
|
@@ -291,7 +294,7 @@ module Kitchen
|
|
|
291
294
|
def create_sandbox
|
|
292
295
|
info("Creating kitchen sandbox in #{sandbox_path}")
|
|
293
296
|
unless ::Dir.exist?(sandbox_path)
|
|
294
|
-
FileUtils.mkdir_p(sandbox_path, :
|
|
297
|
+
FileUtils.mkdir_p(sandbox_path, mode: 0o755)
|
|
295
298
|
end
|
|
296
299
|
end
|
|
297
300
|
|
|
@@ -313,7 +316,7 @@ module Kitchen
|
|
|
313
316
|
conn.execute(init_command)
|
|
314
317
|
info("Transferring files to #{instance.to_str}")
|
|
315
318
|
conn.upload(sandbox_dirs, config[:root_path])
|
|
316
|
-
debug(
|
|
319
|
+
debug("Transfer complete")
|
|
317
320
|
end
|
|
318
321
|
|
|
319
322
|
conn.execute(prepare_command)
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
-
require
|
|
19
|
-
require
|
|
20
|
-
require_relative
|
|
18
|
+
require "kitchen"
|
|
19
|
+
require "kitchen/provisioner/chef_zero"
|
|
20
|
+
require_relative "../helpers"
|
|
21
21
|
|
|
22
22
|
include Dokken::Helpers
|
|
23
23
|
|
|
@@ -29,11 +29,12 @@ module Kitchen
|
|
|
29
29
|
|
|
30
30
|
plugin_version Kitchen::VERSION
|
|
31
31
|
|
|
32
|
-
default_config :root_path,
|
|
33
|
-
default_config :chef_binary,
|
|
34
|
-
default_config :chef_options,
|
|
35
|
-
default_config :chef_log_level,
|
|
36
|
-
default_config :chef_output_format,
|
|
32
|
+
default_config :root_path, "/opt/kitchen"
|
|
33
|
+
default_config :chef_binary, "/opt/chef/bin/chef-client"
|
|
34
|
+
default_config :chef_options, " -z"
|
|
35
|
+
default_config :chef_log_level, "warn"
|
|
36
|
+
default_config :chef_output_format, "doc"
|
|
37
|
+
default_config :profile_ruby, false
|
|
37
38
|
default_config :docker_info, docker_info
|
|
38
39
|
default_config :docker_host_url, default_docker_host
|
|
39
40
|
|
|
@@ -45,7 +46,7 @@ module Kitchen
|
|
|
45
46
|
# driver and set it here. If we remove this, users will set their chef_version
|
|
46
47
|
# to 14 in the driver and still get prompted for license acceptance because
|
|
47
48
|
# the ChefZero provisioner defaults product_version to 'latest'.
|
|
48
|
-
default_config :product_name,
|
|
49
|
+
default_config :product_name, "chef"
|
|
49
50
|
default_config :product_version do |provisioner|
|
|
50
51
|
driver = provisioner.instance.driver
|
|
51
52
|
driver[:chef_version]
|
|
@@ -73,15 +74,16 @@ module Kitchen
|
|
|
73
74
|
rescue Kitchen::Transport::TransportFailed => ex
|
|
74
75
|
raise ActionFailed, ex.message
|
|
75
76
|
ensure
|
|
76
|
-
return unless config[:clean_dokken_sandbox]
|
|
77
|
+
return unless config[:clean_dokken_sandbox] # rubocop: disable Lint/EnsureReturn
|
|
78
|
+
|
|
77
79
|
cleanup_dokken_sandbox
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def validate_config
|
|
81
83
|
# check if we have an space for the user provided options
|
|
82
84
|
# or add it if not to avoid issues
|
|
83
|
-
unless config[:chef_options].start_with?
|
|
84
|
-
config[:chef_options].prepend(
|
|
85
|
+
unless config[:chef_options].start_with? " "
|
|
86
|
+
config[:chef_options].prepend(" ")
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
# strip spaces from all other options
|
|
@@ -91,8 +93,8 @@ module Kitchen
|
|
|
91
93
|
|
|
92
94
|
# if the user wants to be funny and pass empty strings
|
|
93
95
|
# just use the defaults
|
|
94
|
-
config[:chef_log_level] =
|
|
95
|
-
config[:chef_output_format] =
|
|
96
|
+
config[:chef_log_level] = "warn" if config[:chef_log_level].empty?
|
|
97
|
+
config[:chef_output_format] = "doc" if config[:chef_output_format].empty?
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
private
|
|
@@ -104,8 +106,9 @@ module Kitchen
|
|
|
104
106
|
cmd << config[:chef_options].to_s
|
|
105
107
|
cmd << " -l #{config[:chef_log_level]}"
|
|
106
108
|
cmd << " -F #{config[:chef_output_format]}"
|
|
107
|
-
cmd <<
|
|
108
|
-
cmd <<
|
|
109
|
+
cmd << " -c /opt/kitchen/client.rb"
|
|
110
|
+
cmd << " -j /opt/kitchen/dna.json"
|
|
111
|
+
cmd << "--profile-ruby" if config[:profile_ruby]
|
|
109
112
|
|
|
110
113
|
chef_cmd(cmd)
|
|
111
114
|
end
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
-
require
|
|
19
|
-
require
|
|
20
|
-
require
|
|
21
|
-
require
|
|
22
|
-
require_relative
|
|
18
|
+
require "kitchen"
|
|
19
|
+
require "net/scp"
|
|
20
|
+
require "tmpdir" unless defined?(Dir.mktmpdir)
|
|
21
|
+
require "digest/sha1" unless defined?(Digest::SHA1)
|
|
22
|
+
require_relative "../helpers"
|
|
23
23
|
|
|
24
24
|
include Dokken::Helpers
|
|
25
25
|
|
|
@@ -44,7 +44,7 @@ module Kitchen
|
|
|
44
44
|
default_config :read_timeout, 3600
|
|
45
45
|
default_config :write_timeout, 3600
|
|
46
46
|
default_config :host_ip_override do |transport|
|
|
47
|
-
transport.docker_for_mac_or_win? ?
|
|
47
|
+
transport.docker_for_mac_or_win? ? "localhost" : false
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# (see Base#connection)
|
|
@@ -69,7 +69,7 @@ module Kitchen
|
|
|
69
69
|
|
|
70
70
|
with_retries { @runner = ::Docker::Container.get(instance_name, {}, docker_connection) }
|
|
71
71
|
with_retries do
|
|
72
|
-
o = @runner.exec(Shellwords.shellwords(command), wait: options[:timeout],
|
|
72
|
+
o = @runner.exec(Shellwords.shellwords(command), wait: options[:timeout], "e" => { "TERM" => "xterm" }) { |_stream, chunk| print chunk.to_s }
|
|
73
73
|
@exit_code = o[2]
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -82,26 +82,26 @@ module Kitchen
|
|
|
82
82
|
ssh_ip = options[:host_ip_override]
|
|
83
83
|
ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
|
|
84
84
|
|
|
85
|
-
elsif options[:docker_host_url]
|
|
86
|
-
if options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp] ==
|
|
85
|
+
elsif /unix:/.match?(options[:docker_host_url])
|
|
86
|
+
if options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp] == "0.0.0.0"
|
|
87
87
|
ssh_ip = options[:data_container][:NetworkSettings][:IPAddress]
|
|
88
|
-
ssh_port =
|
|
88
|
+
ssh_port = "22"
|
|
89
89
|
else
|
|
90
90
|
# we should read the proper mapped ip, since this allows us to upload the files
|
|
91
91
|
ssh_ip = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp]
|
|
92
92
|
ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
elsif options[:docker_host_url]
|
|
95
|
+
elsif /tcp:/.match?(options[:docker_host_url])
|
|
96
96
|
name = options[:data_container][:Name]
|
|
97
97
|
|
|
98
98
|
# DOCKER_HOST
|
|
99
|
-
docker_host_url_ip = options[:docker_host_url].split(
|
|
99
|
+
docker_host_url_ip = options[:docker_host_url].split("tcp://")[1].split(":")[0]
|
|
100
100
|
|
|
101
101
|
# mapped IP of data container
|
|
102
|
-
candidate_ip = ::Docker::Container.all.
|
|
103
|
-
x.info[
|
|
104
|
-
end.
|
|
102
|
+
candidate_ip = ::Docker::Container.all.find do |x|
|
|
103
|
+
x.info["Names"][0].eql?(name)
|
|
104
|
+
end.info["NetworkSettings"]["Networks"]["dokken"]["IPAddress"]
|
|
105
105
|
|
|
106
106
|
# mapped port
|
|
107
107
|
candidate_ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
|
|
@@ -114,22 +114,22 @@ module Kitchen
|
|
|
114
114
|
ssh_ip = candidate_ip
|
|
115
115
|
ssh_port = candidate_ssh_port
|
|
116
116
|
|
|
117
|
-
elsif port_open?(candidate_ip,
|
|
117
|
+
elsif port_open?(candidate_ip, "22")
|
|
118
118
|
ssh_ip = candidate_ip
|
|
119
|
-
ssh_port =
|
|
119
|
+
ssh_port = "22"
|
|
120
120
|
debug "candidate_ip - #{candidate_ip}/22 open"
|
|
121
121
|
else
|
|
122
122
|
ssh_ip = docker_host_url_ip
|
|
123
123
|
ssh_port = candidate_ssh_port
|
|
124
124
|
end
|
|
125
125
|
else
|
|
126
|
-
raise Kitchen::UserError,
|
|
126
|
+
raise Kitchen::UserError, "docker_host_url must be tcp:// or unix://"
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
debug "ssh_ip : #{ssh_ip}"
|
|
130
130
|
debug "ssh_port : #{ssh_port}"
|
|
131
131
|
|
|
132
|
-
tmpdir = Dir.tmpdir +
|
|
132
|
+
tmpdir = Dir.tmpdir + "/dokken/"
|
|
133
133
|
FileUtils.mkdir_p tmpdir.to_s, mode: 0o777
|
|
134
134
|
tmpdir += Process.uid.to_s
|
|
135
135
|
FileUtils.mkdir_p tmpdir.to_s
|
|
@@ -137,26 +137,26 @@ module Kitchen
|
|
|
137
137
|
FileUtils.chmod(0o600, "#{tmpdir}/id_rsa")
|
|
138
138
|
|
|
139
139
|
begin
|
|
140
|
-
rsync_cmd =
|
|
141
|
-
rsync_cmd <<
|
|
142
|
-
rsync_cmd <<
|
|
140
|
+
rsync_cmd = "/usr/bin/rsync -a -e"
|
|
141
|
+
rsync_cmd << " '"
|
|
142
|
+
rsync_cmd << "ssh -2"
|
|
143
143
|
rsync_cmd << " -i #{tmpdir}/id_rsa"
|
|
144
|
-
rsync_cmd <<
|
|
145
|
-
rsync_cmd <<
|
|
146
|
-
rsync_cmd <<
|
|
147
|
-
rsync_cmd <<
|
|
148
|
-
rsync_cmd <<
|
|
149
|
-
rsync_cmd <<
|
|
144
|
+
rsync_cmd << " -o CheckHostIP=no"
|
|
145
|
+
rsync_cmd << " -o Compression=no"
|
|
146
|
+
rsync_cmd << " -o PasswordAuthentication=no"
|
|
147
|
+
rsync_cmd << " -o StrictHostKeyChecking=no"
|
|
148
|
+
rsync_cmd << " -o UserKnownHostsFile=/dev/null"
|
|
149
|
+
rsync_cmd << " -o LogLevel=ERROR"
|
|
150
150
|
rsync_cmd << " -p #{ssh_port}"
|
|
151
|
-
rsync_cmd << '
|
|
152
|
-
rsync_cmd << " #{locals.join(
|
|
151
|
+
rsync_cmd << "'"
|
|
152
|
+
rsync_cmd << " #{locals.join(" ")} root@#{ssh_ip}:#{remote}"
|
|
153
153
|
debug "rsync_cmd :#{rsync_cmd}:"
|
|
154
154
|
`#{rsync_cmd}`
|
|
155
155
|
rescue Errno::ENOENT
|
|
156
|
-
debug
|
|
156
|
+
debug "Rsync is not installed. Falling back to SCP."
|
|
157
157
|
locals.each do |local|
|
|
158
158
|
Net::SCP.upload!(ssh_ip,
|
|
159
|
-
|
|
159
|
+
"root",
|
|
160
160
|
local,
|
|
161
161
|
remote,
|
|
162
162
|
recursive: true,
|
|
@@ -169,8 +169,8 @@ module Kitchen
|
|
|
169
169
|
@runner = options[:instance_name].to_s
|
|
170
170
|
cols = `tput cols`
|
|
171
171
|
lines = `tput lines`
|
|
172
|
-
args = [
|
|
173
|
-
LoginCommand.new(
|
|
172
|
+
args = ["exec", "-e", "COLUMNS=#{cols}", "-e", "LINES=#{lines}", "-it", @runner, "/bin/bash", "-login", "-i"]
|
|
173
|
+
LoginCommand.new("docker", args)
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
private
|
|
@@ -181,6 +181,7 @@ module Kitchen
|
|
|
181
181
|
|
|
182
182
|
def work_image
|
|
183
183
|
return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
|
|
184
|
+
|
|
184
185
|
instance_name
|
|
185
186
|
end
|
|
186
187
|
|
|
@@ -208,7 +209,7 @@ module Kitchen
|
|
|
208
209
|
#
|
|
209
210
|
# @return [TrueClass,FalseClass]
|
|
210
211
|
def docker_for_mac_or_win?
|
|
211
|
-
::Docker.info(::Docker::Connection.new(config[:docker_host_url], {}))[
|
|
212
|
+
::Docker.info(::Docker::Connection.new(config[:docker_host_url], {}))["Name"] == "moby"
|
|
212
213
|
rescue
|
|
213
214
|
false
|
|
214
215
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-dokken
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean OMeara
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-02-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: docker-api
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '1.33'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '3'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
|
-
- - "
|
|
27
|
+
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
29
|
version: '1.33'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: lockfile
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,7 +64,8 @@ dependencies:
|
|
|
58
64
|
- - "<"
|
|
59
65
|
- !ruby/object:Gem::Version
|
|
60
66
|
version: '3'
|
|
61
|
-
description: A Test Kitchen Driver for
|
|
67
|
+
description: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing
|
|
68
|
+
using Chef Infra docker images
|
|
62
69
|
email:
|
|
63
70
|
- sean@sean.io
|
|
64
71
|
executables: []
|
|
@@ -83,16 +90,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
83
90
|
requirements:
|
|
84
91
|
- - ">="
|
|
85
92
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: '
|
|
93
|
+
version: '2.5'
|
|
87
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
95
|
requirements:
|
|
89
96
|
- - ">="
|
|
90
97
|
- !ruby/object:Gem::Version
|
|
91
98
|
version: '0'
|
|
92
99
|
requirements: []
|
|
93
|
-
rubygems_version: 3.1.
|
|
100
|
+
rubygems_version: 3.1.4
|
|
94
101
|
signing_key:
|
|
95
102
|
specification_version: 4
|
|
96
|
-
summary: A Test Kitchen Driver
|
|
97
|
-
|
|
103
|
+
summary: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing
|
|
104
|
+
using Chef Infra docker images
|
|
98
105
|
test_files: []
|