kitchen-dokken 2.23.4 → 2.23.6

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: 2d9ccb56c8974df70436872b28c19a5450c2c05bf932455bcfa58aafce70864c
4
- data.tar.gz: 8e07df1d7dba596d084ad7a4a102c585052a746cc89bb2e4e67684ac4860711e
3
+ metadata.gz: 2c7f5b5652cdde267df1ead2dad29ea1c7e3f642cec4b86f3c5ef4c13612a3f3
4
+ data.tar.gz: 8b94c596b93899da42069d35faed37d4be565c9af5617839ee84f9e44fcc9a86
5
5
  SHA512:
6
- metadata.gz: c1e01f4007692ced6bf2072031e2ab0ccb64540baac5c8442b52241e9202c744a9392382712b69cf02cf62716410ea645c9c2967f13a8fb8c72ae2cd3a94209e
7
- data.tar.gz: b5213a8c3f73612734cc5db99a607c9d9eb4253a5814d563122f7556d14df100aeec431eb459a673b3f083408a7581a778d89e0c5ddc8dfb50cfa7b62952b56c
6
+ metadata.gz: d48c19a4bdbed503978b68451cded18f2437d2aa0c048d8285344447c4a27503b48937eaf29d93d2fd429316f9c8db240942f09a3333616c939a5cd58555f26d
7
+ data.tar.gz: 305e81615cc269fdfc685436bbd19b47de69c465006c8ac7b55b42eb8911e4dbfd3e2b36759417347e109c56418176dfa69b5f5136f33669ff2b3e5ffbed4f62
@@ -543,7 +543,8 @@ module Kitchen
543
543
  return unless self[:network_mode] == "dokken"
544
544
 
545
545
  with_file_lock("#{home_dir}/.dokken-network.lock") do
546
- with_retries { ::Docker::Network.get("dokken", {}, docker_connection) }
546
+ network = with_retries { ::Docker::Network.get("dokken", {}, docker_connection) }
547
+ warn_on_ipv6_mismatch(network)
547
548
  rescue ::Docker::Error::NotFoundError
548
549
  begin
549
550
  with_retries { ::Docker::Network.create("dokken", network_settings) }
@@ -553,6 +554,34 @@ module Kitchen
553
554
  end
554
555
  end
555
556
 
557
+ # Say something when the shared network cannot honour `ipv6: true`.
558
+ #
559
+ # The dokken network is created once and shared by every instance, and
560
+ # `destroy` deliberately leaves it behind -- so on any workstation that
561
+ # has run a non-ipv6 suite, it already exists without ipv6. Since the
562
+ # network is only created when absent, a later `ipv6: true` suite
563
+ # quietly reuses it, the container gets no ipv6 address, and nothing
564
+ # anywhere says why.
565
+ #
566
+ # Deliberately a warning rather than an error: the network may be in use
567
+ # by other instances right now, and removing it is the user's call.
568
+ #
569
+ # @param network [::Docker::Network] the existing dokken network
570
+ # @return [void]
571
+ def warn_on_ipv6_mismatch(network)
572
+ return unless self[:ipv6]
573
+
574
+ # A daemon that does not report the flag must not produce a warning on
575
+ # every converge.
576
+ enabled = network.info["EnableIPv6"] if network.respond_to?(:info)
577
+ return if enabled.nil? || enabled
578
+
579
+ warn "The existing dokken network was created without IPv6, so `ipv6: true` " \
580
+ "will have no effect and this instance will get no IPv6 address. The " \
581
+ "network is shared and `kitchen destroy` leaves it in place; recreate it " \
582
+ "with `docker network rm dokken` once no containers are using it."
583
+ end
584
+
556
585
  # Build the data image, unless it is already present.
557
586
  #
558
587
  # @return [void]
@@ -907,6 +936,7 @@ module Kitchen
907
936
  # @param args [Hash] the `/containers/create` body
908
937
  # @param platform [String, nil] an OCI platform to pin the container to
909
938
  # @return [::Docker::Container] the running container
939
+ # @raise [Kitchen::ActionFailed] if the container will not stay running
910
940
  def run_container(args, platform: nil)
911
941
  @container = create_container(args, platform: platform)
912
942
  with_retries do
@@ -914,9 +944,34 @@ module Kitchen
914
944
  @container = ::Docker::Container.get(args["name"], {}, docker_connection)
915
945
  wait_running_state(args["name"], true)
916
946
  end
947
+ assert_running!(args["name"])
917
948
  @container
918
949
  end
919
950
 
951
+ # Fail the action when a container that has to run is not running.
952
+ #
953
+ # wait_running_state stops polling as soon as `FinishedAt` is set, which
954
+ # is precisely the case for a container whose pid 1 exited -- so without
955
+ # this check `kitchen create` reported success and exited 0, leaving the
956
+ # user to discover the problem at converge as a bare docker API message:
957
+ # "container dd55d579... is not running". A container id, no instance
958
+ # name, and no clue that pid 1 was the cause.
959
+ #
960
+ # Only containers that must actually run reach here; the chef container
961
+ # is a volume and is never started.
962
+ #
963
+ # @param name [String] the container name
964
+ # @return [void]
965
+ # @raise [Kitchen::ActionFailed] if the container is not running
966
+ def assert_running!(name)
967
+ return if container_state["Running"]
968
+
969
+ raise ActionFailed,
970
+ "The #{name} container exited immediately after being started. " \
971
+ "Its pid 1 did not stay up: check `pid_one_command`, `entrypoint`, " \
972
+ "and that the image can boot (`docker logs #{name}` shows why)."
973
+ end
974
+
920
975
  # The current container's `State` payload.
921
976
  #
922
977
  # @return [Hash] the state, or an empty hash if there is no container
@@ -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.4".freeze
21
+ DOKKEN_VERSION = "2.23.6".freeze
22
22
  end
23
23
  end
@@ -211,6 +211,31 @@ module Kitchen
211
211
  "The data container has no address on any docker network: #{settings[:Networks].inspect}"
212
212
  end
213
213
 
214
+ # Every address the data container has, best candidate first.
215
+ #
216
+ # A user-defined network comes before the default bridge. That order
217
+ # matters when kitchen is itself a container: a sibling on the dokken
218
+ # network can reach the container there but has no route to its
219
+ # bridge address, and preferring the bridge would send every upload
220
+ # to the docker host instead.
221
+ #
222
+ # The legacy top-level `IPAddress` is the default bridge's, so it
223
+ # sorts with the bridge and is deduplicated against it.
224
+ #
225
+ # @return [Array<String>] addresses, most specific network first
226
+ # @api private
227
+ def data_container_addresses
228
+ settings = options[:data_container][:NetworkSettings]
229
+ networks = settings[:Networks] || {}
230
+
231
+ user_defined, default_bridge = networks.partition { |name, _| name.to_s != "bridge" }
232
+
233
+ addresses = (user_defined + default_bridge).map { |_, network| network[:IPAddress].to_s }
234
+ addresses << settings[:IPAddress].to_s
235
+
236
+ addresses.reject(&:empty?).uniq
237
+ end
238
+
214
239
  # Pick an endpoint for a daemon reached over tcp.
215
240
  #
216
241
  # The container's address on the dokken network is preferred, since it
@@ -221,31 +246,42 @@ module Kitchen
221
246
  # @return [Array(String, String)] the address and port to ssh to
222
247
  # @api private
223
248
  def tcp_ssh_endpoint
224
- name = options[:data_container][:Name]
225
-
226
249
  # DOCKER_HOST
227
250
  docker_host_url_ip = options[:docker_host_url].split("tcp://")[1].split(":")[0]
228
251
 
229
- # mapped IP of data container
230
- candidate_ip = ::Docker::Container.all.find do |x|
231
- x.info["Names"][0].eql?(name)
232
- end.info["NetworkSettings"]["Networks"]["dokken"]["IPAddress"]
233
-
234
252
  # mapped port
235
253
  candidate_ssh_port = published_ssh_port
236
254
 
237
- debug "candidate_ip - #{candidate_ip}"
238
- debug "candidate_ssh_port - #{candidate_ssh_port}"
239
-
240
- if port_open?(candidate_ip, candidate_ssh_port)
241
- debug "candidate_ip - #{candidate_ip}/#{candidate_ssh_port} open"
242
- [candidate_ip, candidate_ssh_port]
243
- elsif port_open?(candidate_ip, "22")
244
- debug "candidate_ip - #{candidate_ip}/22 open"
245
- [candidate_ip, "22"]
246
- else
247
- [docker_host_url_ip, candidate_ssh_port]
255
+ # The addresses come from the state the driver recorded after it
256
+ # started the container. This used to ask the daemon instead --
257
+ # Docker::Container.all, find ours by name, then read
258
+ # Networks["dokken"]["IPAddress"] -- which was wrong twice over.
259
+ #
260
+ # Container.all lists only *running* containers, so a data container
261
+ # that had exited made `find` return nil and the chained `.info`
262
+ # raise `undefined method 'info' for nil`. And the dokken network is
263
+ # only attached when network_mode is left at its default:
264
+ # start_data_container adds the endpoint
265
+ # "unless %w{host bridge}.include?" and names it after network_mode,
266
+ # so `bridge`, `host` and every custom network name produced
267
+ # `undefined method '[]' for nil` on a remote daemon.
268
+ data_container_addresses.each do |candidate_ip|
269
+ debug "candidate_ip - #{candidate_ip}"
270
+ debug "candidate_ssh_port - #{candidate_ssh_port}"
271
+
272
+ if port_open?(candidate_ip, candidate_ssh_port)
273
+ debug "candidate_ip - #{candidate_ip}/#{candidate_ssh_port} open"
274
+ return [candidate_ip, candidate_ssh_port]
275
+ elsif port_open?(candidate_ip, "22")
276
+ debug "candidate_ip - #{candidate_ip}/22 open"
277
+ return [candidate_ip, "22"]
278
+ end
248
279
  end
280
+
281
+ # Nothing answered, or -- with host networking -- the container has
282
+ # no address of its own to try. The docker host is the endpoint.
283
+ debug "no data container address answered; falling back to the docker host"
284
+ [docker_host_url_ip, candidate_ssh_port]
249
285
  end
250
286
 
251
287
  # Write the built-in insecure private key somewhere ssh will accept it.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kitchen-dokken
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.23.4
4
+ version: 2.23.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean OMeara