kitchen-dokken 2.11.1 → 2.14.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: 98524d476ee9044dfb093ab3de13152ac564103fa663d8d50bdf0393d9bfcf32
4
- data.tar.gz: 653e8898c6d4cc114edc2dd4d1229744d0fea01745c4a45dfac1e49ada5127e5
3
+ metadata.gz: 11e0a7060becac934d969980957fa85ec1db9242263b9bfe1435102c98c5ba5b
4
+ data.tar.gz: 5090a5db7059ecb32e6807e012d1f49c7274265bb10c8e485cd2a54a9b84fb44
5
5
  SHA512:
6
- metadata.gz: 751bf6a56a04c4eaa040cdc0a527d9a0b9202534107d16563af52986b57285337be29cb89203828135f2bf373afad8e4413580a837398e0964c89eff9c6defed
7
- data.tar.gz: 3828997c2805eb7b2f1d433643c32ceef797a82a1a7d20649c6309c8cc760c965a4bc8c9222fc8fa5956c84657b53040cd26ed86bfdd065303dcdd4d3aa0bcb2
6
+ metadata.gz: 2e6d715f25d7cc4b324db6a31aab928d4fdf0c135d6f4fd6a5b9b9f67fb40f5360accf2136494178e1a696d7eb253805b2063f314769a580155ed7c675c0823f
7
+ data.tar.gz: 68970cd1101555b5556168f7f86cb20f69df80ee27465c41ee176c2946eb00dcaf454444641727a4a73cb223fa06b6d89d7565695346afc05829a4a68f7e9d80
@@ -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 'digest' unless defined?(Digest)
19
- require 'kitchen'
20
- require 'tmpdir' unless defined?(Dir.mktmpdir)
21
- require 'docker'
22
- require 'lockfile'
23
- require_relative '../helpers'
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,31 +37,35 @@ 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, 'chef/chef'
41
- default_config :chef_version, 'latest'
42
- default_config :data_image, 'dokken/kitchen-cache:latest'
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 :ports, nil
49
- default_config :docker_host_url, default_docker_host
50
- default_config :hostname, 'dokken'
50
+ default_config :hostname, "dokken"
51
+ default_config :hostname_aliases, nil
51
52
  default_config :image_prefix, nil
53
+ default_config :ipv6, false
54
+ default_config :ipv6_subnet, "2001:db8:1::/64" # "2001:db8::/32 Range reserved for documentation"
52
55
  default_config :links, nil
53
- default_config :network_mode, 'dokken'
56
+ default_config :memory_limit, 0
57
+ default_config :network_mode, "dokken"
54
58
  default_config :pid_one_command, 'sh -c "trap exit 0 SIGTERM; while :; do sleep 1; done"'
59
+ default_config :ports, nil
55
60
  default_config :privileged, false
61
+ default_config :pull_chef_image, true
62
+ default_config :pull_platform_image, true
56
63
  default_config :read_timeout, 3600
57
64
  default_config :security_opt, nil
58
65
  default_config :tmpfs, {}
66
+ default_config :userns_host, false
59
67
  default_config :volumes, nil
60
68
  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
- default_config :memory_limit, 0
65
69
 
66
70
  # (see Base#create)
67
71
  def create(state)
@@ -126,37 +130,38 @@ module Kitchen
126
130
 
127
131
  def delete_work_image
128
132
  return unless ::Docker::Image.exist?(work_image, {}, docker_connection)
133
+
129
134
  with_retries { @work_image = ::Docker::Image.get(work_image, {}, docker_connection) }
130
135
 
131
136
  with_retries do
132
- begin
133
- with_retries { @work_image.remove(force: true) }
134
- rescue ::Docker::Error::ConflictError
135
- debug "driver - #{work_image} cannot be removed"
136
- end
137
+
138
+ with_retries { @work_image.remove(force: true) }
139
+ rescue ::Docker::Error::ConflictError
140
+ debug "driver - #{work_image} cannot be removed"
141
+
137
142
  end
138
143
  end
139
144
 
140
145
  def build_work_image(state)
141
- info('Building work image..')
146
+ info("Building work image..")
142
147
  return if ::Docker::Image.exist?(work_image, {}, docker_connection)
143
148
 
144
149
  begin
145
150
  @intermediate_image = ::Docker::Image.build(
146
151
  work_image_dockerfile,
147
152
  {
148
- 't' => work_image,
153
+ "t" => work_image,
149
154
  },
150
155
  docker_connection
151
156
  )
152
157
  # credit to https://github.com/someara/kitchen-dokken/issues/95#issue-224697526
153
158
  rescue Docker::Error::UnexpectedResponseError => e
154
- msg = 'work_image build failed: '
155
- msg += JSON.parse(e.to_s.split("\r\n").last)['error'].to_s
156
- msg += '. The common scenerios are incorrect intermediate'
157
- msg += 'instructions such as not including `-y` on an `apt-get` '
158
- msg += 'or similar. The other common scenerio is a transient '
159
- msg += 'error such as an unresponsive mirror.'
159
+ msg = "work_image build failed: "
160
+ msg += JSON.parse(e.to_s.split("\r\n").last)["error"].to_s
161
+ msg += ". The common scenarios are incorrect intermediate "
162
+ msg += "instructions such as not including `-y` on an `apt-get` "
163
+ msg += "or similar. The other common scenario is a transient "
164
+ msg += "error such as an unresponsive mirror."
160
165
  raise msg
161
166
  # fallback rescue above should catch most of the errors
162
167
  rescue => e
@@ -167,8 +172,10 @@ module Kitchen
167
172
  end
168
173
 
169
174
  def work_image_dockerfile
175
+ from = registry_image_path(platform_image)
176
+ debug("driver - Building work image from #{from}")
170
177
  dockerfile_contents = [
171
- "FROM #{platform_image}",
178
+ "FROM #{from}",
172
179
  "LABEL X-Built-By=kitchen-dokken X-Built-From=#{platform_image}",
173
180
  ]
174
181
  Array(config[:intermediate_instructions]).each do |c|
@@ -219,6 +226,7 @@ module Kitchen
219
226
 
220
227
  def work_image
221
228
  return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
229
+
222
230
  instance_name
223
231
  end
224
232
 
@@ -244,7 +252,7 @@ module Kitchen
244
252
  v
245
253
  else
246
254
  Array(v).each_with_object({}) do |y, h|
247
- name, opts = y.split(':', 2)
255
+ name, opts = y.split(":", 2)
248
256
  h[name.to_s] = opts.to_s
249
257
  end
250
258
  end
@@ -260,12 +268,13 @@ module Kitchen
260
268
  b = []
261
269
  v = Array(v).to_a # in case v.is_A?(Chef::Node::ImmutableArray)
262
270
  v.delete_if do |x|
263
- parts = x.split(':')
271
+ parts = x.split(":")
264
272
  b << x if parts.length > 1
265
273
  end
266
274
  b = nil if b.empty?
267
275
  config[:binds].push(b) unless config[:binds].include?(b) || b.nil?
268
276
  return PartialHash.new if v.empty?
277
+
269
278
  v.each_with_object(PartialHash.new) { |volume, h| h[volume] = {} }
270
279
  end
271
280
  end
@@ -281,41 +290,42 @@ module Kitchen
281
290
  debug "driver - starting #{runner_container_name}"
282
291
 
283
292
  config = {
284
- 'name' => runner_container_name,
285
- 'Cmd' => Shellwords.shellwords(self[:pid_one_command]),
286
- 'Image' => "#{repo(work_image)}:#{tag(work_image)}",
287
- 'Hostname' => self[:hostname],
288
- 'Env' => self[:env],
289
- 'ExposedPorts' => exposed_ports,
290
- 'Volumes' => dokken_volumes,
291
- 'HostConfig' => {
292
- 'Privileged' => self[:privileged],
293
- 'VolumesFrom' => dokken_volumes_from,
294
- 'Binds' => dokken_binds,
295
- 'Dns' => self[:dns],
296
- 'DnsSearch' => self[:dns_search],
297
- 'Links' => Array(self[:links]),
298
- 'CapAdd' => Array(self[:cap_add]),
299
- 'CapDrop' => Array(self[:cap_drop]),
300
- 'SecurityOpt' => Array(self[:security_opt]),
301
- 'NetworkMode' => self[:network_mode],
302
- 'PortBindings' => port_bindings,
303
- 'Tmpfs' => dokken_tmpfs,
304
- 'Memory' => self[:memory_limit],
293
+ "name" => runner_container_name,
294
+ "Cmd" => Shellwords.shellwords(self[:pid_one_command]),
295
+ # locally built image, must use short-name
296
+ "Image" => short_image_path(work_image),
297
+ "Hostname" => self[:hostname],
298
+ "Env" => self[:env],
299
+ "ExposedPorts" => exposed_ports,
300
+ "Volumes" => dokken_volumes,
301
+ "HostConfig" => {
302
+ "Privileged" => self[:privileged],
303
+ "VolumesFrom" => dokken_volumes_from,
304
+ "Binds" => dokken_binds,
305
+ "Dns" => self[:dns],
306
+ "DnsSearch" => self[:dns_search],
307
+ "Links" => Array(self[:links]),
308
+ "CapAdd" => Array(self[:cap_add]),
309
+ "CapDrop" => Array(self[:cap_drop]),
310
+ "SecurityOpt" => Array(self[:security_opt]),
311
+ "NetworkMode" => self[:network_mode],
312
+ "PortBindings" => port_bindings,
313
+ "Tmpfs" => dokken_tmpfs,
314
+ "Memory" => self[:memory_limit],
305
315
  },
306
- 'NetworkingConfig' => {
307
- 'EndpointsConfig' => {
316
+ "NetworkingConfig" => {
317
+ "EndpointsConfig" => {
308
318
  self[:network_mode] => {
309
- 'Aliases' => Array(self[:hostname]),
319
+ "Aliases" => Array(self[:hostname]).concat(Array(self[:hostname_aliases])),
310
320
  },
311
321
  },
312
322
  },
313
323
  }
314
324
  unless self[:entrypoint].to_s.empty?
315
- config['Entrypoint'] = self[:entrypoint]
325
+ config["Entrypoint"] = self[:entrypoint]
316
326
  end
317
327
  if self[:userns_host]
318
- config['HostConfig']['UsernsMode'] = 'host'
328
+ config["HostConfig"]["UsernsMode"] = "host"
319
329
  end
320
330
  runner_container = run_container(config)
321
331
  state[:runner_container] = runner_container.json
@@ -324,17 +334,18 @@ module Kitchen
324
334
  def start_data_container(state)
325
335
  debug "driver - creating #{data_container_name}"
326
336
  config = {
327
- 'name' => data_container_name,
328
- 'Image' => "#{repo(data_image)}:#{tag(data_image)}",
329
- 'HostConfig' => {
330
- 'PortBindings' => port_bindings,
331
- 'PublishAllPorts' => true,
332
- 'NetworkMode' => 'bridge',
337
+ "name" => data_container_name,
338
+ # locally built image, must use short-name
339
+ "Image" => short_image_path(data_image),
340
+ "HostConfig" => {
341
+ "PortBindings" => port_bindings,
342
+ "PublishAllPorts" => true,
343
+ "NetworkMode" => "bridge",
333
344
  },
334
- 'NetworkingConfig' => {
335
- 'EndpointsConfig' => {
345
+ "NetworkingConfig" => {
346
+ "EndpointsConfig" => {
336
347
  self[:network_mode] => {
337
- 'Aliases' => Array(self[:hostname]),
348
+ "Aliases" => Array(self[:hostname]),
338
349
  },
339
350
  },
340
351
  },
@@ -347,10 +358,10 @@ module Kitchen
347
358
  lockfile = Lockfile.new "#{home_dir}/.dokken-network.lock"
348
359
  begin
349
360
  lockfile.lock
350
- with_retries { ::Docker::Network.get('dokken', {}, docker_connection) }
351
- rescue
361
+ with_retries { ::Docker::Network.get("dokken", {}, docker_connection) }
362
+ rescue ::Docker::Error::NotFoundError
352
363
  begin
353
- with_retries { ::Docker::Network.create('dokken', {}) }
364
+ with_retries { ::Docker::Network.create("dokken", network_settings) }
354
365
  rescue ::Docker::Error => e
355
366
  debug "driver - error :#{e}:"
356
367
  end
@@ -360,32 +371,39 @@ module Kitchen
360
371
  end
361
372
 
362
373
  def make_data_image
363
- debug 'driver - calling create_data_image'
364
- create_data_image
374
+ debug "driver - calling create_data_image"
375
+ create_data_image(config[:docker_registry])
365
376
  end
366
377
 
367
378
  def create_chef_container(state)
368
379
  lockfile = Lockfile.new "#{home_dir}/.dokken-#{chef_container_name}.lock"
369
380
  begin
370
381
  lockfile.lock
371
- with_retries { ::Docker::Container.get(chef_container_name, {}, docker_connection) }
382
+ with_retries {
383
+ # TEMPORARY FIX - docker-api 2.0.0 has a buggy Docker::Container.get - use .all instead
384
+ # https://github.com/swipely/docker-api/issues/566
385
+ # ::Docker::Container.get(chef_container_name, {}, docker_connection)
386
+ found = ::Docker::Container.all({ all: true }, docker_connection).select { |c| c.info["Names"].include?("/#{chef_container_name}") }
387
+ raise ::Docker::Error::NotFoundError.new(chef_container_name) if found.empty?
388
+
389
+ debug "Chef container already exists, continuing"
390
+ }
372
391
  rescue ::Docker::Error::NotFoundError
392
+ debug "Chef container does not exist, creating a new Chef container"
373
393
  with_retries do
374
- begin
375
- debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
376
- config = {
377
- 'name' => chef_container_name,
378
- 'Cmd' => 'true',
379
- 'Image' => "#{repo(chef_image)}:#{tag(chef_image)}",
380
- 'HostConfig' => {
381
- 'NetworkMode' => self[:network_mode],
382
- },
383
- }
384
- chef_container = create_container(config)
385
- state[:chef_container] = chef_container.json
386
- rescue ::Docker::Error => e
387
- raise "driver - #{chef_container_name} failed to create #{e}"
388
- end
394
+ debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
395
+ config = {
396
+ "name" => chef_container_name,
397
+ "Cmd" => "true",
398
+ "Image" => registry_image_path(chef_image),
399
+ "HostConfig" => {
400
+ "NetworkMode" => self[:network_mode],
401
+ },
402
+ }
403
+ chef_container = create_container(config)
404
+ state[:chef_container] = chef_container.json
405
+ rescue ::Docker::Error, StandardError => e
406
+ raise "driver - #{chef_container_name} failed to create #{e}"
389
407
  end
390
408
  ensure
391
409
  lockfile.unlock
@@ -393,12 +411,12 @@ module Kitchen
393
411
  end
394
412
 
395
413
  def pull_platform_image
396
- debug "driver - pulling #{chef_image} #{repo(platform_image)} #{tag(platform_image)}"
414
+ debug "driver - pulling #{short_image_path(platform_image)}"
397
415
  config[:pull_platform_image] ? pull_image(platform_image) : pull_if_missing(platform_image)
398
416
  end
399
417
 
400
418
  def pull_chef_image
401
- debug "driver - pulling #{chef_image} #{repo(chef_image)} #{tag(chef_image)}"
419
+ debug "driver - pulling #{short_image_path(chef_image)}"
402
420
  config[:pull_chef_image] ? pull_image(chef_image) : pull_if_missing(chef_image)
403
421
  end
404
422
 
@@ -411,50 +429,83 @@ module Kitchen
411
429
 
412
430
  def container_exist?(name)
413
431
  return true if ::Docker::Container.get(name, {}, docker_connection)
414
- rescue
432
+ rescue StandardError, ::Docker::Error::NotFoundError
415
433
  false
416
434
  end
417
435
 
418
436
  def parse_image_name(image)
419
- parts = image.split(':')
437
+ parts = image.split(":")
420
438
 
421
439
  if parts.size > 2
422
440
  tag = parts.pop
423
- repo = parts.join(':')
441
+ repo = parts.join(":")
424
442
  else
425
- tag = parts[1] || 'latest'
443
+ tag = parts[1] || "latest"
426
444
  repo = parts[0]
427
445
  end
428
446
 
429
447
  [repo, tag]
430
448
  end
431
449
 
450
+ # Return the 'repo' half of a docker image path. Agnostic about if a
451
+ # registry is included, this effectively is just "before the colon"
452
+ #
453
+ # @param image [String] the docker image path to parse
454
+ # @return [String] the repo portion of `image`
432
455
  def repo(image)
433
456
  parse_image_name(image)[0]
434
457
  end
435
458
 
459
+ # Return the 'tag' of a docker image path. Will be `latest` if there
460
+ # is no explicit tag in the image path.
461
+ #
462
+ # @param image [String] the docker image path to parse
463
+ # @return [String] the tag of `image`
464
+ def tag(image)
465
+ parse_image_name(image)[1]
466
+ end
467
+
468
+ # Ensures an explicit tag on an image path.
469
+ #
470
+ # @param image [String] the docker image path to parse
471
+ # @return [String] `repo`:`tag`
472
+ def short_image_path(image)
473
+ "#{repo(image)}:#{tag(image)}"
474
+ end
475
+
476
+ # Qualifies the results of `short_image_path` with any registry the
477
+ # user has requested
478
+ #
479
+ # @param image [String] the docker image path to parse
480
+ # @return [String] The most fully-qualified registry path we cn make
481
+ def registry_image_path(image)
482
+ if config[:docker_registry]
483
+ "#{config[:docker_registry]}/#{short_image_path(image)}"
484
+ else
485
+ short_image_path(image)
486
+ end
487
+ end
488
+
436
489
  def create_container(args)
437
- with_retries { @container = ::Docker::Container.get(args['name'], {}, docker_connection) }
490
+ with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
438
491
  rescue
439
492
  with_retries do
440
- begin
441
- args['Env'] = [] if args['Env'].nil?
442
- args['Env'] << 'TEST_KITCHEN=1'
443
- args['Env'] << "CI=#{ENV['CI']}" if ENV.include? 'CI'
444
- info "Creating container #{args['name']}"
445
- debug "driver - create_container args #{args}"
446
- with_retries do
447
- begin
448
- @container = ::Docker::Container.create(args.clone, docker_connection)
449
- rescue ::Docker::Error::ConflictError
450
- debug "driver - rescue ConflictError: #{args['name']}"
451
- with_retries { @container = ::Docker::Container.get(args['name'], {}, docker_connection) }
452
- end
453
- end
454
- rescue ::Docker::Error => e
455
- debug "driver - error :#{e}:"
456
- raise "driver - failed to create_container #{args['name']}"
493
+ args["Env"] = [] if args["Env"].nil?
494
+ args["Env"] << "TEST_KITCHEN=1"
495
+ args["Env"] << "CI=#{ENV["CI"]}" if ENV.include? "CI"
496
+ info "Creating container #{args["name"]}"
497
+ debug "driver - create_container args #{args}"
498
+ with_retries do
499
+
500
+ @container = ::Docker::Container.create(args.clone, docker_connection)
501
+ rescue ::Docker::Error::ConflictError
502
+ debug "driver - rescue ConflictError: #{args["name"]}"
503
+ with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
457
504
  end
505
+ rescue ::Docker::Error => e
506
+ debug "driver - error :#{e}:"
507
+ raise "driver - failed to create_container #{args["name"]}"
508
+
458
509
  end
459
510
  end
460
511
 
@@ -462,14 +513,14 @@ module Kitchen
462
513
  create_container(args)
463
514
  with_retries do
464
515
  @container.start
465
- @container = ::Docker::Container.get(args['name'], {}, docker_connection)
466
- wait_running_state(args['name'], true)
516
+ @container = ::Docker::Container.get(args["name"], {}, docker_connection)
517
+ wait_running_state(args["name"], true)
467
518
  end
468
519
  @container
469
520
  end
470
521
 
471
522
  def container_state
472
- @container ? @container.info['State'] : {}
523
+ @container ? @container.info["State"] : {}
473
524
  end
474
525
 
475
526
  def stop_container(name)
@@ -493,18 +544,15 @@ module Kitchen
493
544
  @container = ::Docker::Container.get(name, {}, docker_connection)
494
545
  i = 0
495
546
  tries = 20
496
- until container_state['Running'] == v || container_state['FinishedAt'] != '0001-01-01T00:00:00Z'
547
+ until container_state["Running"] == v || container_state["FinishedAt"] != "0001-01-01T00:00:00Z"
497
548
  i += 1
498
549
  break if i == tries
550
+
499
551
  sleep 0.1
500
552
  @container = ::Docker::Container.get(name, {}, docker_connection)
501
553
  end
502
554
  end
503
555
 
504
- def tag(image)
505
- parse_image_name(image)[1]
506
- end
507
-
508
556
  def chef_container_name
509
557
  "chef-#{chef_version}"
510
558
  end
@@ -514,7 +562,8 @@ module Kitchen
514
562
  end
515
563
 
516
564
  def chef_version
517
- return 'latest' if config[:chef_version] == 'stable'
565
+ return "latest" if config[:chef_version] == "stable"
566
+
518
567
  config[:chef_version]
519
568
  end
520
569
 
@@ -531,27 +580,29 @@ module Kitchen
531
580
  end
532
581
 
533
582
  def platform_image_from_name
534
- platform, release = instance.platform.name.split('-')
535
- release ? [platform, release].join(':') : platform
583
+ platform, release = instance.platform.name.split("-")
584
+ release ? [platform, release].join(":") : platform
536
585
  end
537
586
 
538
587
  def pull_if_missing(image)
539
- return if ::Docker::Image.exist?("#{repo(image)}:#{tag(image)}", {}, docker_connection)
588
+ return if ::Docker::Image.exist?(registry_image_path(image), {}, docker_connection)
589
+
540
590
  pull_image image
541
591
  end
542
592
 
543
593
  # https://github.com/docker/docker/blob/4fcb9ac40ce33c4d6e08d5669af6be5e076e2574/registry/auth.go#L231
544
594
  def parse_registry_host(val)
545
- val.sub(%r{https?://}, '').split('/').first
595
+ val.sub(%r{https?://}, "").split("/").first
546
596
  end
547
597
 
548
598
  def pull_image(image)
599
+ path = registry_image_path(image)
549
600
  with_retries do
550
- if Docker::Image.exist?("#{repo(image)}:#{tag(image)}", {}, docker_connection)
551
- original_image = Docker::Image.get("#{repo(image)}:#{tag(image)}", {}, docker_connection)
601
+ if Docker::Image.exist?(path, {}, docker_connection)
602
+ original_image = Docker::Image.get(path, {}, docker_connection)
552
603
  end
553
604
 
554
- new_image = Docker::Image.create({ 'fromImage' => "#{repo(image)}:#{tag(image)}" }, docker_connection)
605
+ new_image = Docker::Image.create({ "fromImage" => path }, {}, docker_connection)
555
606
 
556
607
  !(original_image && original_image.id.start_with?(new_image.id))
557
608
  end
@@ -18,6 +18,6 @@
18
18
  module Kitchen
19
19
  module Driver
20
20
  # Version string for Dokken Kitchen driver
21
- DOKKEN_VERSION = '2.11.1'.freeze
21
+ DOKKEN_VERSION = "2.14.0".freeze
22
22
  end
23
23
  end
@@ -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 'socket' unless defined?(Socket)
5
- require 'timeout' unless defined?(Timeout)
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
- begin
11
- s = TCPSocket.new(ip, port)
12
- s.close
13
- return true
14
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENETDOWN
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 centos:7
65
+ FROM #{from}
65
66
  MAINTAINER Sean OMeara \"sean@sean.io\"
66
67
  ENV LANG en_US.UTF-8
67
68
 
@@ -86,36 +87,38 @@ 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
- 'nocache' => true,
100
- 'rm' => true
100
+ "nocache" => true,
101
+ "rm" => true
101
102
  )
102
- i.tag('repo' => repo(data_image), 'tag' => tag(data_image), 'force' => true)
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['DOCKER_HOST']
107
- ENV['DOCKER_HOST']
108
- elsif File.exist?('/var/run/docker.sock')
109
- 'unix:///var/run/docker.sock'
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
- 'tcp://127.0.0.1:2375'
115
+ "tcp://127.0.0.1:2375"
115
116
  end
116
117
  end
117
118
 
118
119
  def docker_info
120
+ ::Docker.url = default_docker_host
121
+
119
122
  @docker_info ||= ::Docker.info
120
123
  rescue Excon::Error::Socket
121
124
  puts "kitchen-dokken could not connect to the docker host at #{default_docker_host}. Is docker running?"
@@ -152,7 +155,8 @@ VOLUME /opt/verifier
152
155
  # refs:
153
156
  # https://github.com/docker/machine/issues/1814
154
157
  # https://github.com/docker/toolbox/issues/607
155
- return Dir.home.sub 'C:/Users', '/c/Users' if Dir.home =~ /^C:/ && remote_docker_host?
158
+ return Dir.home.sub "C:/Users", "/c/Users" if Dir.home =~ /^C:/ && remote_docker_host?
159
+
156
160
  Dir.home
157
161
  end
158
162
 
@@ -173,6 +177,21 @@ VOLUME /opt/verifier
173
177
  coerce_exposed_ports(config[:ports])
174
178
  end
175
179
 
180
+ def network_settings
181
+ if self[:ipv6]
182
+ {
183
+ "EnableIPv6" => true,
184
+ "IPAM" => {
185
+ "Config" => [{
186
+ "Subnet" => self[:ipv6_subnet],
187
+ }],
188
+ },
189
+ }
190
+ else
191
+ {}
192
+ end
193
+ end
194
+
176
195
  def port_bindings
177
196
  coerce_port_bindings(config[:ports])
178
197
  end
@@ -185,7 +204,7 @@ VOLUME /opt/verifier
185
204
  x = Array(v).map { |a| parse_port(a) }
186
205
  x.flatten!
187
206
  x.each_with_object({}) do |y, h|
188
- h[y['container_port']] = {}
207
+ h[y["container_port"]] = {}
189
208
  end
190
209
  end
191
210
  end
@@ -198,52 +217,53 @@ VOLUME /opt/verifier
198
217
  x = Array(v).map { |a| parse_port(a) }
199
218
  x.flatten!
200
219
  x.each_with_object({}) do |y, h|
201
- h[y['container_port']] = [] unless h[y['container_port']]
202
- h[y['container_port']] << {
203
- 'HostIp' => y['host_ip'],
204
- 'HostPort' => y['host_port'],
220
+ h[y["container_port"]] = [] unless h[y["container_port"]]
221
+ h[y["container_port"]] << {
222
+ "HostIp" => y["host_ip"],
223
+ "HostPort" => y["host_port"],
205
224
  }
206
225
  end
207
226
  end
208
227
  end
209
228
 
210
229
  def parse_port(v)
211
- parts = v.split(':')
230
+ parts = v.split(":")
212
231
  case parts.length
213
232
  when 3
214
233
  host_ip = parts[0]
215
234
  host_port = parts[1]
216
235
  container_port = parts[2]
217
236
  when 2
218
- host_ip = '0.0.0.0'
237
+ host_ip = "0.0.0.0"
219
238
  host_port = parts[0]
220
239
  container_port = parts[1]
221
240
  when 1
222
- host_ip = ''
223
- host_port = ''
241
+ host_ip = ""
242
+ host_port = ""
224
243
  container_port = parts[0]
225
244
  end
226
- port_range, protocol = container_port.split('/')
227
- if port_range.include?('-')
228
- port_range = container_port.split('-')
245
+ port_range, protocol = container_port.split("/")
246
+ if port_range.include?("-")
247
+ port_range = container_port.split("-")
229
248
  port_range.map!(&:to_i)
230
249
  Chef::Log.fatal("FATAL: Invalid port range! #{container_port}") if port_range[0] > port_range[1]
231
250
  port_range = (port_range[0]..port_range[1]).to_a
232
251
  end
233
252
  # qualify the port-binding protocol even when it is implicitly tcp #427.
234
- protocol = 'tcp' if protocol.nil?
253
+ protocol = "tcp" if protocol.nil?
235
254
  Array(port_range).map do |port|
236
255
  {
237
- 'host_ip' => host_ip,
238
- 'host_port' => host_port,
239
- 'container_port' => "#{port}/#{protocol}",
256
+ "host_ip" => host_ip,
257
+ "host_port" => host_port,
258
+ "container_port" => "#{port}/#{protocol}",
240
259
  }
241
260
  end
242
261
  end
243
262
 
244
263
  def remote_docker_host?
245
- return false if config[:docker_info]['OperatingSystem'].include?('Boot2Docker')
264
+ return false if config[:docker_info]["OperatingSystem"].include?("Boot2Docker")
246
265
  return true if /^tcp:/.match?(config[:docker_host_url])
266
+
247
267
  false
248
268
  end
249
269
 
@@ -252,13 +272,13 @@ VOLUME /opt/verifier
252
272
  end
253
273
 
254
274
  def sandbox_dirs
255
- Dir.glob(File.join(sandbox_path, '*'))
275
+ Dir.glob(File.join(sandbox_path, "*"))
256
276
  end
257
277
 
258
278
  def create_sandbox
259
279
  info("Creating kitchen sandbox in #{sandbox_path}")
260
280
  unless ::Dir.exist?(sandbox_path)
261
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
281
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
262
282
  end
263
283
  end
264
284
  end
@@ -269,7 +289,7 @@ module Kitchen
269
289
  class Base
270
290
  def create_sandbox
271
291
  info("Creating kitchen sandbox in #{sandbox_path}")
272
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
292
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
273
293
  end
274
294
 
275
295
  # this MUST be named 'sandbox_path' because ruby.
@@ -291,7 +311,7 @@ module Kitchen
291
311
  def create_sandbox
292
312
  info("Creating kitchen sandbox in #{sandbox_path}")
293
313
  unless ::Dir.exist?(sandbox_path)
294
- FileUtils.mkdir_p(sandbox_path, :mode => 0o755)
314
+ FileUtils.mkdir_p(sandbox_path, mode: 0o755)
295
315
  end
296
316
  end
297
317
 
@@ -313,7 +333,7 @@ module Kitchen
313
333
  conn.execute(init_command)
314
334
  info("Transferring files to #{instance.to_str}")
315
335
  conn.upload(sandbox_dirs, config[:root_path])
316
- debug('Transfer complete')
336
+ debug("Transfer complete")
317
337
  end
318
338
 
319
339
  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 'kitchen'
19
- require 'kitchen/provisioner/chef_zero'
20
- require_relative '../helpers'
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, '/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'
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, 'chef'
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] = 'warn' if config[:chef_log_level].empty?
95
- config[:chef_output_format] = 'doc' if config[:chef_output_format].empty?
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,14 +106,16 @@ 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 << ' -c /opt/kitchen/client.rb'
108
- cmd << ' -j /opt/kitchen/dna.json'
109
+ cmd << " -c /opt/kitchen/client.rb"
110
+ cmd << " -j /opt/kitchen/dna.json"
111
+ cmd << "--profile-ruby" if config[:profile_ruby]
112
+ cmd << "--slow-report" if config[:slow_resource_report]
109
113
 
110
114
  chef_cmd(cmd)
111
115
  end
112
116
 
113
117
  def write_run_command(command)
114
- File.write("#{dokken_kitchen_sandbox}/run_command", command)
118
+ File.write("#{dokken_kitchen_sandbox}/run_command", command, mode: "wb")
115
119
  end
116
120
 
117
121
  def runner_container_name
@@ -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 'kitchen'
19
- require 'net/scp'
20
- require 'tmpdir' unless defined?(Dir.mktmpdir)
21
- require 'digest/sha1' unless defined?(Digest::SHA1)
22
- require_relative '../helpers'
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? ? 'localhost' : false
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], 'e' => { 'TERM' => 'xterm' }) { |_stream, chunk| print chunk.to_s }
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
 
@@ -83,9 +83,9 @@ module Kitchen
83
83
  ssh_port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
84
84
 
85
85
  elsif /unix:/.match?(options[:docker_host_url])
86
- if options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostIp] == '0.0.0.0'
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 = '22'
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]
@@ -96,12 +96,12 @@ module Kitchen
96
96
  name = options[:data_container][:Name]
97
97
 
98
98
  # DOCKER_HOST
99
- docker_host_url_ip = options[:docker_host_url].split('tcp://')[1].split(':')[0]
99
+ docker_host_url_ip = options[:docker_host_url].split("tcp://")[1].split(":")[0]
100
100
 
101
101
  # mapped IP of data container
102
102
  candidate_ip = ::Docker::Container.all.find do |x|
103
- x.info['Names'][0].eql?(name)
104
- end.info['NetworkSettings']['Networks']['dokken']['IPAddress']
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, '22')
117
+ elsif port_open?(candidate_ip, "22")
118
118
  ssh_ip = candidate_ip
119
- ssh_port = '22'
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, 'docker_host_url must be tcp:// or unix://'
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 + '/dokken/'
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 = '/usr/bin/rsync -a -e'
141
- rsync_cmd << ' \''
142
- rsync_cmd << 'ssh -2'
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 << ' -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'
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(' ')} root@#{ssh_ip}:#{remote}"
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 'Rsync is not installed. Falling back to SCP.'
156
+ debug "Rsync is not installed. Falling back to SCP."
157
157
  locals.each do |local|
158
158
  Net::SCP.upload!(ssh_ip,
159
- 'root',
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 = ['exec', '-e', "COLUMNS=#{cols}", '-e', "LINES=#{lines}", '-it', @runner, '/bin/bash', '-login', '-i']
173
- LoginCommand.new('docker', args)
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], {}))['Name'] == 'moby'
212
+ ::Docker.info(::Docker::Connection.new(config[:docker_host_url], {}))["Name"] == "moby"
212
213
  rescue
213
214
  false
214
215
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-dokken
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.1
4
+ version: 2.14.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: 2020-10-19 00:00:00.000000000 Z
11
+ date: 2021-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docker-api
@@ -53,7 +53,7 @@ dependencies:
53
53
  version: '1.15'
54
54
  - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: '3'
56
+ version: '4'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -63,7 +63,7 @@ dependencies:
63
63
  version: '1.15'
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: '3'
66
+ version: '4'
67
67
  description: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing
68
68
  using Chef Infra docker images
69
69
  email:
@@ -90,14 +90,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
90
  requirements:
91
91
  - - ">="
92
92
  - !ruby/object:Gem::Version
93
- version: '0'
93
+ version: '2.5'
94
94
  required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  requirements: []
100
- rubygems_version: 3.1.4
100
+ rubygems_version: 3.2.15
101
101
  signing_key:
102
102
  specification_version: 4
103
103
  summary: A Test Kitchen Driver for Docker & Chef Infra optimized for rapid testing