kitchen-dokken 2.23.1 → 2.23.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d17cea32434bc8b2b07ab6d5354b1f47462b369a75da4c5f590ea5f5584203f
4
- data.tar.gz: d35e888e3d94ec39baae9a00bffb3c2d6fb4ec9ca119cff17f0813925bfedc2e
3
+ metadata.gz: 9bbe79ca0ad5a4b71659c1359871a9e4c86f6e243d83d694ab76341f94a0e6c0
4
+ data.tar.gz: 2e6dd411ce0d408331cf312249d208c6b6345382cc94d2d83cba0cf7b407d0be
5
5
  SHA512:
6
- metadata.gz: 4b7537a361c7b640ba5387e7f2b96037f0ca1225e754743d7f15efcbfbb01ee7d437576d8aa739066e8a2b410d2f202c50ab375c50bd27c656399750681ff5ed
7
- data.tar.gz: 59ddda252eec17939ef63a3decaaf396fa733eb8021d480d76809033af1480b80eeb681c7f9936fe5b3c0089f78b7fc98cc4e3457c2f57a1303537a119c44c48
6
+ metadata.gz: a14d2a57ab4bdb76e6192107fd2f0c185df2f1fab90522c1fa45546420c8a508e0adfb9b893a56c2c9ef6e84c4b7fd5cf1428f578b614f788659b523e6219753
7
+ data.tar.gz: df6506e366a8379682f5bbbfc3e3b1574d841a3da048d7ab35d61b944f4b28996277cf30907fa1b1de3b21aa36c52fd01521844344c51c0ec976cd93b396ed1e
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require "digest" unless defined?(Digest)
19
+ require "json" unless defined?(JSON)
19
20
  require "kitchen"
20
21
  require "tmpdir" unless defined?(Dir.mktmpdir)
21
22
  require "docker"
@@ -80,7 +81,7 @@ module Kitchen
80
81
  default_config :write_timeout, 3600
81
82
  default_config :user_ns_mode, nil
82
83
  default_config :creds_file, nil
83
- default_config :docker_config_creds, false
84
+ default_config :docker_config_creds, true
84
85
 
85
86
  # (see Base#create)
86
87
  def create(state)
@@ -370,7 +371,7 @@ module Kitchen
370
371
  config["HostConfig"]["UsernsMode"] = "host"
371
372
  end
372
373
 
373
- runner_container = run_container(config)
374
+ runner_container = run_container(config, platform: self[:platform])
374
375
  state[:runner_container] = runner_container.json
375
376
  end
376
377
 
@@ -397,6 +398,12 @@ module Kitchen
397
398
  },
398
399
  }
399
400
  end
401
+ # Deliberately not pinned to config[:platform]: this container only
402
+ # serves kitchen's files over ssh, so its architecture is irrelevant to
403
+ # the system under test. The data image is built locally from
404
+ # almalinux:9 (see Helpers#create_data_image) and is therefore always
405
+ # host-architecture -- pinning it fails outright whenever the platform
406
+ # under test differs from the host.
400
407
  data_container = run_container(config)
401
408
  state[:data_container] = data_container.json
402
409
  end
@@ -443,7 +450,9 @@ module Kitchen
443
450
  "NetworkMode" => self[:network_mode],
444
451
  },
445
452
  }
446
- chef_container = create_container(config)
453
+ # /opt/chef from this container is mounted into the runner, so it
454
+ # has to be built for the same architecture as the runner.
455
+ chef_container = create_container(config, platform: self[:platform])
447
456
  state[:chef_container] = chef_container.json
448
457
  rescue ::Docker::Error, StandardError => e
449
458
  raise "driver - #{chef_container_name} failed to create #{e}"
@@ -504,20 +513,68 @@ module Kitchen
504
513
  @docker_config_creds
505
514
  end
506
515
 
516
+ # The `auths` key `docker login` itself writes for Docker Hub.
517
+ DOCKER_HUB_REGISTRY_KEY = "https://index.docker.io/v1/".freeze
518
+
519
+ # Registry hosts that all refer to Docker Hub. Docker writes the legacy
520
+ # v1 URL above, but hand-written and tool-generated configs use any of
521
+ # these. Listed in preference order.
522
+ DOCKER_HUB_HOSTS = %w{index.docker.io docker.io registry-1.docker.io}.freeze
523
+
524
+ # An explicit "no credentials" value. Returning nil here instead would let
525
+ # docker-api fall back to the process-global ::Docker.creds, which
526
+ # authenticate! populates from creds_file and never clears -- so another
527
+ # instance's private registry password would be sent to this registry,
528
+ # which is the very leak this scoping exists to prevent.
529
+ NO_DOCKER_CREDS = {}.freeze
530
+
531
+ # Return the registry host an image reference points at, or nil when the
532
+ # reference resolves to Docker Hub. The leading path component only names
533
+ # a registry when it contains a "." or a ":", or is exactly "localhost" --
534
+ # the same heuristic Docker uses to tell "quay.io/org/image" apart from
535
+ # the Hub shorthand "dokken/almalinux-8".
536
+ def image_registry_host(image)
537
+ first, remainder = image.split("/", 2)
538
+ return nil if remainder.nil?
539
+ return nil unless first.include?(".") || first.include?(":") || first == "localhost"
540
+
541
+ first
542
+ end
543
+
544
+ # Look up the ~/.docker/config.json entry that applies to an image's
545
+ # registry. Returns nil when that registry has no entry so the pull is
546
+ # attempted anonymously, rather than offering it another registry's
547
+ # credentials.
548
+ # Pick the config.json key that holds the Docker Hub credentials. A
549
+ # config may spell Hub several ways, so prefer the canonical key and then
550
+ # a fixed alias order rather than whichever happens to be listed first.
551
+ def docker_hub_creds_key
552
+ keys = docker_config_creds.keys.select { |k| DOCKER_HUB_HOSTS.include?(parse_registry_host(k)) }
553
+ return if keys.empty?
554
+
555
+ keys.find { |k| k == DOCKER_HUB_REGISTRY_KEY } ||
556
+ keys.min_by { |k| DOCKER_HUB_HOSTS.index(parse_registry_host(k)) }
557
+ end
558
+
559
+ def docker_config_creds_for_image(image)
560
+ host = image_registry_host(image)
561
+
562
+ key = if host.nil? || DOCKER_HUB_HOSTS.include?(host)
563
+ docker_hub_creds_key
564
+ else
565
+ docker_config_creds.keys.find { |k| parse_registry_host(k) == host }
566
+ end
567
+ return if key.nil?
568
+
569
+ c = docker_config_creds[key]
570
+ c.respond_to?(:call) ? c.call : c
571
+ end
572
+
507
573
  def docker_creds_for_image(image)
508
574
  return docker_creds if config[:creds_file]
575
+ return NO_DOCKER_CREDS unless config[:docker_config_creds]
509
576
 
510
- image_registry = image.split("/").first
511
-
512
- # NOTE: Try to use DockerHub auth if exact registry match isn't found
513
- default_registry = "https://index.docker.io/v1/"
514
- if docker_config_creds.key?(image_registry)
515
- c = docker_config_creds[image_registry]
516
- c.respond_to?(:call) ? c.call : c
517
- elsif docker_config_creds.key?(default_registry)
518
- c = docker_config_creds[default_registry]
519
- c.respond_to?(:call) ? c.call : c
520
- end
577
+ docker_config_creds_for_image(image) || NO_DOCKER_CREDS
521
578
  end
522
579
 
523
580
  def pull_platform_image
@@ -596,18 +653,17 @@ module Kitchen
596
653
  end
597
654
  end
598
655
 
599
- def create_container(args)
656
+ def create_container(args, platform: nil)
600
657
  with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
601
658
  rescue
602
659
  with_retries do
603
660
  args["Env"] = [] if args["Env"].nil?
604
661
  args["Env"] << "TEST_KITCHEN=1"
605
662
  args["Env"] << "CI=#{ENV["CI"]}" if ENV.include? "CI"
606
- args["Platform"] = config[:platform]
607
663
  info "Creating container #{args["name"]}"
608
664
  debug "driver - create_container args #{args}"
609
665
  with_retries do
610
- @container = ::Docker::Container.create(args.clone, docker_connection)
666
+ @container = create_container_for_platform(args.clone, platform)
611
667
  rescue ::Docker::Error::ConflictError
612
668
  debug "driver - rescue ConflictError: #{args["name"]}"
613
669
  with_retries { @container = ::Docker::Container.get(args["name"], {}, docker_connection) }
@@ -618,8 +674,25 @@ module Kitchen
618
674
  end
619
675
  end
620
676
 
621
- def run_container(args)
622
- create_container(args)
677
+ # Create a container, optionally pinned to an OCI platform.
678
+ #
679
+ # /containers/create takes `platform` as a *query* parameter, but
680
+ # Docker::Container.create only ever forwards `name` to the query string
681
+ # and dumps everything else into the request body. A "Platform" body key
682
+ # is therefore accepted and silently ignored by the daemon, which is why
683
+ # the platform config had no effect on container creation. Post directly
684
+ # when a platform is wanted; otherwise use the gem as before.
685
+ def create_container_for_platform(args, platform)
686
+ return ::Docker::Container.create(args, docker_connection) if platform.to_s.empty?
687
+
688
+ query = { "name" => args["name"], "platform" => platform }
689
+ body = args.reject { |k, _| k == "name" }
690
+ docker_connection.post("/containers/create", query, body: JSON.dump(body))
691
+ ::Docker::Container.get(args["name"], {}, docker_connection)
692
+ end
693
+
694
+ def run_container(args, platform: nil)
695
+ create_container(args, platform: platform)
623
696
  with_retries do
624
697
  @container.start
625
698
  @container = ::Docker::Container.get(args["name"], {}, docker_connection)
@@ -664,7 +737,7 @@ module Kitchen
664
737
 
665
738
  def chef_container_name
666
739
  prefix = instance.provisioner[:product_name] == "cinc" ? "cinc" : "chef"
667
- config[:platform] != "" ? "#{prefix}-#{chef_version}-" + config[:platform].sub("/", "-") : "#{prefix}-#{chef_version}"
740
+ config[:platform] != "" ? "#{prefix}-#{chef_version}-" + config[:platform].tr("/", "-") : "#{prefix}-#{chef_version}"
668
741
  end
669
742
 
670
743
  def chef_image
@@ -733,18 +806,24 @@ module Kitchen
733
806
  original_image = Docker::Image.get(path, { "platform" => oci_platform(config[:platform]) }, docker_connection)
734
807
  end
735
808
 
736
- new_image = Docker::Image.create({ "fromImage" => path, "platform" => config[:platform] }, docker_creds_for_image(image), docker_connection)
809
+ new_image = Docker::Image.create({ "fromImage" => path, "platform" => config[:platform] }, docker_creds_for_image(path), docker_connection)
737
810
 
738
811
  !(original_image&.id&.start_with?(new_image.id))
739
812
  end
740
813
  end
741
814
 
815
+ # Convert an "os/arch[/variant]" platform string into the JSON OCI
816
+ # platform spec the Docker API expects as an image filter. The variant
817
+ # matters: the daemon will not match a filter of
818
+ # {"os":"linux","architecture":"amd64"} against a linux/amd64/v2 image,
819
+ # so dropping it makes every image lookup for a variant image miss.
742
820
  def oci_platform(platform)
743
- if !platform.nil? && platform.include?("/")
744
- os, arch = platform.split("/")
745
- platform = { os: os, architecture: arch }.to_json
746
- end
747
- platform
821
+ return platform if platform.nil? || !platform.include?("/")
822
+
823
+ os, architecture, variant = platform.split("/")
824
+ spec = { os: os, architecture: architecture }
825
+ spec[:variant] = variant unless variant.nil? || variant.empty?
826
+ spec.to_json
748
827
  end
749
828
 
750
829
  def runner_container_name
@@ -18,6 +18,6 @@
18
18
  module Kitchen
19
19
  module Driver
20
20
  # Version string for Dokken Kitchen driver
21
- DOKKEN_VERSION = "2.23.1".freeze
21
+ DOKKEN_VERSION = "2.23.3".freeze
22
22
  end
23
23
  end
@@ -204,9 +204,7 @@ module Dokken
204
204
  else
205
205
  x = Array(v).map { |a| parse_port(a) }
206
206
  x.flatten!
207
- x.each_with_object({}) do |y, h|
208
- h[y["container_port"]] = {}
209
- end
207
+ x.to_h { |y| [y["container_port"], {}] }
210
208
  end
211
209
  end
212
210
 
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.23.1
4
+ version: 2.23.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean OMeara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-21 00:00:00.000000000 Z
11
+ date: 2026-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docker-api