kitchen-docker 3.3.0 → 3.3.1

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.
@@ -16,10 +16,20 @@ require "kitchen/configurable"
16
16
 
17
17
  module Kitchen
18
18
  module Docker
19
+ # Mixins shared by the driver, transport, and container classes.
19
20
  module Helpers
21
+ # Per-distribution Dockerfile fragments that prepare a Linux image for SSH.
20
22
  module DockerfileHelper
21
23
  include Configurable
22
24
 
25
+ # Dockerfile lines that prepare the configured platform for SSH.
26
+ #
27
+ # Each distribution needs its own package manager invocation to install an
28
+ # SSH server and sudo, and its own host-key generation, which is why there
29
+ # is one method per family rather than a shared one.
30
+ #
31
+ # @return [String] the RUN lines for this platform
32
+ # @raise [Kitchen::ActionFailed] if the platform is not recognised
23
33
  def dockerfile_platform
24
34
  case config[:platform]
25
35
  when "arch"
@@ -51,6 +61,9 @@ module Kitchen
51
61
  end
52
62
  end
53
63
 
64
+ # Dockerfile lines installing an SSH server and sudo on Arch Linux.
65
+ #
66
+ # @return [String] the RUN lines
54
67
  def arch_platform
55
68
  <<-CODE
56
69
  RUN pacman --noconfirm -Sy archlinux-keyring
@@ -60,6 +73,9 @@ module Kitchen
60
73
  CODE
61
74
  end
62
75
 
76
+ # Dockerfile lines installing an SSH server and sudo on Debian and Ubuntu.
77
+ #
78
+ # @return [String] the RUN lines
63
79
  def debian_platform
64
80
  disable_upstart = <<-CODE
65
81
  RUN [ ! -f "/sbin/initctl" ] || dpkg-divert --local --rename --add /sbin/initctl \
@@ -74,6 +90,9 @@ module Kitchen
74
90
  config[:disable_upstart] ? disable_upstart + packages : packages
75
91
  end
76
92
 
93
+ # Dockerfile lines installing an SSH server and sudo on Fedora.
94
+ #
95
+ # @return [String] the RUN lines
77
96
  def fedora_platform
78
97
  <<-CODE
79
98
  ENV container=docker
@@ -83,6 +102,9 @@ module Kitchen
83
102
  CODE
84
103
  end
85
104
 
105
+ # Dockerfile lines installing an SSH server and sudo on Gentoo with Portage.
106
+ #
107
+ # @return [String] the RUN lines
86
108
  def gentoo_platform
87
109
  <<-CODE
88
110
  RUN emerge-webrsync
@@ -91,6 +113,9 @@ module Kitchen
91
113
  CODE
92
114
  end
93
115
 
116
+ # Dockerfile lines installing an SSH server and sudo on Gentoo with Paludis.
117
+ #
118
+ # @return [String] the RUN lines
94
119
  def gentoo_paludis_platform
95
120
  <<-CODE
96
121
  RUN cave sync
@@ -99,6 +124,9 @@ module Kitchen
99
124
  CODE
100
125
  end
101
126
 
127
+ # Dockerfile lines installing an SSH server and sudo on openSUSE and SLES.
128
+ #
129
+ # @return [String] the RUN lines
102
130
  def opensuse_platform
103
131
  <<-CODE
104
132
  ENV container=docker
@@ -107,6 +135,9 @@ module Kitchen
107
135
  CODE
108
136
  end
109
137
 
138
+ # Dockerfile lines installing an SSH server and sudo on RHEL, CentOS, and Oracle Linux.
139
+ #
140
+ # @return [String] the RUN lines
110
141
  def rhel_platform
111
142
  <<-CODE
112
143
  ENV container=docker
@@ -117,6 +148,9 @@ module Kitchen
117
148
  CODE
118
149
  end
119
150
 
151
+ # Dockerfile lines installing an SSH server and sudo on Amazon Linux.
152
+ #
153
+ # @return [String] the RUN lines
120
154
  def amazonlinux_platform
121
155
  <<-CODE
122
156
  ENV container=docker
@@ -126,6 +160,9 @@ module Kitchen
126
160
  CODE
127
161
  end
128
162
 
163
+ # Dockerfile lines installing an SSH server and sudo on CentOS Stream.
164
+ #
165
+ # @return [String] the RUN lines
129
166
  def centosstream_platform
130
167
  <<-CODE
131
168
  ENV container=docker
@@ -135,6 +172,9 @@ module Kitchen
135
172
  CODE
136
173
  end
137
174
 
175
+ # Dockerfile lines installing an SSH server and sudo on AlmaLinux.
176
+ #
177
+ # @return [String] the RUN lines
138
178
  def almalinux_platform
139
179
  <<-CODE
140
180
  ENV container=docker
@@ -144,6 +184,9 @@ module Kitchen
144
184
  CODE
145
185
  end
146
186
 
187
+ # Dockerfile lines installing an SSH server and sudo on Rocky Linux.
188
+ #
189
+ # @return [String] the RUN lines
147
190
  def rockylinux_platform
148
191
  <<-CODE
149
192
  ENV container=docker
@@ -153,6 +196,9 @@ module Kitchen
153
196
  CODE
154
197
  end
155
198
 
199
+ # Dockerfile lines installing an SSH server and sudo on Photon OS.
200
+ #
201
+ # @return [String] the RUN lines
156
202
  def photonos_platform
157
203
  <<-CODE
158
204
  ENV container=docker
@@ -163,6 +209,14 @@ module Kitchen
163
209
  CODE
164
210
  end
165
211
 
212
+ # Dockerfile lines creating the login user and its SSH directory.
213
+ #
214
+ # The user gets passwordless sudo and +Defaults !requiretty+, because Test
215
+ # Kitchen runs commands non-interactively and sudo would otherwise refuse.
216
+ #
217
+ # @param username [String] the login user to create
218
+ # @param homedir [String] that user's home directory
219
+ # @return [String] the RUN lines
166
220
  def dockerfile_base_linux(username, homedir)
167
221
  <<-CODE
168
222
  RUN if ! getent passwd #{username}; then \
@@ -15,8 +15,16 @@ require "fileutils" unless defined?(FileUtils)
15
15
 
16
16
  module Kitchen
17
17
  module Docker
18
+ # Mixins shared by the driver, transport, and container classes.
18
19
  module Helpers
20
+ # Local temp-file handling.
19
21
  module FileHelper
22
+ # Writes a temp file, creating its parent directory if needed.
23
+ #
24
+ # @param file [String] path to write
25
+ # @param contents [String] what to write
26
+ # @return [void]
27
+ # @raise [RuntimeError] if the write fails
20
28
  def create_temp_file(file, contents)
21
29
  debug("[Docker] Creating temp file #{file}")
22
30
  debug("[Docker] --- Start Temp File Contents ---")
@@ -19,12 +19,22 @@ require_relative "container_helper"
19
19
 
20
20
  module Kitchen
21
21
  module Docker
22
+ # Mixins shared by the driver, transport, and container classes.
22
23
  module Helpers
24
+ # Building, inspecting, and removing Docker images.
23
25
  module ImageHelper
24
26
  include Configurable
25
27
  include Kitchen::Docker::Helpers::CliHelper
26
28
  include Kitchen::Docker::Helpers::ContainerHelper
27
29
 
30
+ # Pulls the built image's id out of `docker build` output.
31
+ #
32
+ # Scanned in reverse, and against several patterns, because the wording has
33
+ # changed across Docker and BuildKit versions.
34
+ #
35
+ # @param output [String] the build output
36
+ # @return [String] the image id
37
+ # @raise [Kitchen::ActionFailed] if no id could be found
28
38
  def parse_image_id(output)
29
39
  output.split("\n").reverse_each do |line|
30
40
  if line =~ /writing image (sha256:[[:xdigit:]]{64})(?: \d*\.\ds)? done/i
@@ -44,6 +54,10 @@ module Kitchen
44
54
  raise ActionFailed, "Could not parse Docker build output for image ID"
45
55
  end
46
56
 
57
+ # Removes the built image, unless a container is still using it.
58
+ #
59
+ # @param state [Hash] instance state naming the image
60
+ # @return [void]
47
61
  def remove_image(state)
48
62
  image_id = state[:image_id]
49
63
  if image_in_use?(state)
@@ -54,10 +68,22 @@ module Kitchen
54
68
  end
55
69
  end
56
70
 
71
+ # @param state [Hash] instance state naming the image
72
+ # @return [Boolean] whether any container references it
57
73
  def image_in_use?(state)
58
74
  docker_command("ps -a", suppress_output: !logger.debug?).include?(state[:image_id])
59
75
  end
60
76
 
77
+ # Builds the image from the given Dockerfile.
78
+ #
79
+ # The Dockerfile is written to a temp file and also passed on stdin, so the
80
+ # build works both with a build context and without one. The temp file is
81
+ # removed whether or not the build succeeded.
82
+ #
83
+ # @param state [Hash] instance state
84
+ # @param dockerfile [String] the Dockerfile contents
85
+ # @return [String] the new image's id
86
+ # @raise [Kitchen::ActionFailed] if the id cannot be parsed from the output
61
87
  def build_image(state, dockerfile)
62
88
  cmd = "build"
63
89
  cmd << " --no-cache" unless config[:use_cache]
@@ -82,6 +108,8 @@ module Kitchen
82
108
  parse_image_id(output)
83
109
  end
84
110
 
111
+ # @param state [Hash] instance state naming the image
112
+ # @return [Boolean] whether the image is present locally
85
113
  def image_exists?(state)
86
114
  state[:image_id] && !!docker_command("inspect --type=image #{state[:image_id]}") rescue false
87
115
  end
@@ -52,6 +52,7 @@ end
52
52
 
53
53
  module Kitchen
54
54
  module Docker
55
+ # Mixins shared by the driver, transport, and container classes.
55
56
  module Helpers
56
57
  # Marker module included by the Docker transport Connection class.
57
58
  # Actual verifier patches are applied directly to verifier classes above.
@@ -26,6 +26,7 @@ require_relative "../docker/helpers/cli_helper"
26
26
  require_relative "../docker/helpers/container_helper"
27
27
 
28
28
  module Kitchen
29
+ # Test Kitchen's driver plugins.
29
30
  module Driver
30
31
  # Docker driver for Kitchen.
31
32
  #
@@ -111,28 +112,50 @@ module Kitchen
111
112
  end
112
113
  end
113
114
 
115
+ # Checks that the Docker CLI is installed and runnable.
116
+ #
117
+ # @return [void]
118
+ # @raise [Kitchen::UserError] if the binary cannot be run
114
119
  def verify_dependencies
115
120
  run_command("#{config[:binary]} >> #{dev_null} 2>&1", quiet: true, use_sudo: config[:use_sudo])
116
121
  rescue
117
122
  raise UserError, "You must first install the Docker CLI tool https://www.docker.com/get-started"
118
123
  end
119
124
 
125
+ # Builds the image and starts the container.
126
+ #
127
+ # @param state [Hash] mutable instance state
128
+ # @return [void]
120
129
  def create(state)
121
130
  container.create(state)
122
131
 
123
132
  wait_for_transport(state)
124
133
  end
125
134
 
135
+ # Removes the container, and its image when +remove_images+ is set.
136
+ #
137
+ # @param state [Hash] instance state naming the container
138
+ # @return [void]
126
139
  def destroy(state)
127
140
  container.destroy(state)
128
141
  end
129
142
 
143
+ # Waits for the transport to accept a connection, unless disabled.
144
+ #
145
+ # @param state [Hash] instance state describing how to connect
146
+ # @return [void]
130
147
  def wait_for_transport(state)
131
148
  if config[:wait_for_transport]
132
149
  instance.transport.connection(state, &:wait_until_ready)
133
150
  end
134
151
  end
135
152
 
153
+ # The Docker image implied by the platform name.
154
+ #
155
+ # +ubuntu-22.04+ becomes +ubuntu:22.04+. CentOS is special-cased, since its
156
+ # images are tagged +centos7+ rather than +centos:7+.
157
+ #
158
+ # @return [String] an image reference
136
159
  def default_image
137
160
  platform, release = instance.platform.name.split("-")
138
161
  if platform == "centos" && release
@@ -141,12 +164,16 @@ module Kitchen
141
164
  release ? [platform, release].join(":") : platform
142
165
  end
143
166
 
167
+ # @return [String] the platform family, e.g. +ubuntu+ from +ubuntu-22.04+
144
168
  def default_platform
145
169
  instance.platform.name.split("-").first
146
170
  end
147
171
 
148
172
  protected
149
173
 
174
+ # The container implementation for this platform.
175
+ #
176
+ # @return [Kitchen::Docker::Container] a Windows or Linux container
150
177
  def container
151
178
  @container ||= if windows_os?
152
179
  Kitchen::Docker::Container::Windows.new(config)
@@ -21,8 +21,14 @@ require_relative "../docker/helpers/inspec_helper"
21
21
  # require_relative "../../docker/version"
22
22
 
23
23
  module Kitchen
24
+ # Test Kitchen's transport plugins.
24
25
  module Transport
26
+ # Test Kitchen transport that runs commands inside a Docker container.
27
+ #
28
+ # Commands go through `docker exec` rather than a network protocol, so no
29
+ # SSH or WinRM server is needed inside the image.
25
30
  class Docker < Kitchen::Transport::Base
31
+ # Raised when a docker command against the container fails.
26
32
  class DockerFailed < TransportFailed; end
27
33
 
28
34
  # kitchen_transport_api_version 1
@@ -64,6 +70,15 @@ module Kitchen
64
70
  end
65
71
  end
66
72
 
73
+ # Builds a connection to the container.
74
+ #
75
+ # +DOCKER_HOST+ is exported here because the docker-api gem, used by the
76
+ # InSpec verifier, reads the daemon address from the environment rather
77
+ # than from Test Kitchen's configuration.
78
+ #
79
+ # @param state [Hash] instance state naming the container
80
+ # @yieldparam connection [Connection] if a block is given
81
+ # @return [Connection]
67
82
  def connection(state, &block)
68
83
  options = config.to_hash.merge(state)
69
84
  options[:platform] = instance.platform.name
@@ -77,10 +92,19 @@ module Kitchen
77
92
  Kitchen::Transport::Docker::Connection.new(options, &block)
78
93
  end
79
94
 
80
- class Connection < Kitchen::Transport::Docker::Connection
95
+ # A connection to one container.
96
+ #
97
+ # The superclass is named in full rather than relying on Ruby resolving
98
+ # the bare `Connection` constant through this class's ancestors.
99
+ class Connection < Kitchen::Transport::Base::Connection
81
100
  # Include the InSpec patches to be able to execute tests on Windows containers
82
101
  include Kitchen::Docker::Helpers::InspecHelper
83
102
 
103
+ # Runs a command inside the container.
104
+ #
105
+ # @param command [String] the command to run; nil is a no-op
106
+ # @return [void]
107
+ # @raise [DockerFailed] if the command fails
84
108
  def execute(command)
85
109
  return if command.nil?
86
110
 
@@ -92,10 +116,18 @@ module Kitchen
92
116
  raise DockerFailed, "Docker failed to execute command on container. Error Details: #{e}"
93
117
  end
94
118
 
119
+ # Copies local files into the container.
120
+ #
121
+ # @param locals [String, Array<String>] one path or several
122
+ # @param remote [String] destination path inside the container
123
+ # @return [Array<String>] the files copied
95
124
  def upload(locals, remote)
96
125
  container.upload(locals, remote)
97
126
  end
98
127
 
128
+ # The container implementation for this platform.
129
+ #
130
+ # @return [Kitchen::Docker::Container] a Windows or Linux container
99
131
  def container
100
132
  @container ||= if windows_container?
101
133
  Kitchen::Docker::Container::Windows.new(@options)
@@ -105,7 +137,13 @@ module Kitchen
105
137
  @container
106
138
  end
107
139
 
108
- # (see Base::Connection#login_command)
140
+ # The command `kitchen login` execs to open a shell in the container.
141
+ #
142
+ # Documented here rather than inherited via `(see ...)`, because the
143
+ # superclass lives in the test-kitchen gem and YARD cannot resolve a
144
+ # reference into it from this project's docs.
145
+ #
146
+ # @return [Kitchen::LoginCommand] an interactive `docker exec` session
109
147
  def login_command
110
148
  argv = build_login_command
111
149
  LoginCommand.new(argv.first, argv.drop(1))
@@ -113,6 +151,7 @@ module Kitchen
113
151
 
114
152
  private
115
153
 
154
+ # @return [Boolean] whether the platform under test is Windows
116
155
  def windows_container?
117
156
  @options[:platform].to_s.include?("windows")
118
157
  end
@@ -150,6 +189,8 @@ module Kitchen
150
189
  docker + cmd
151
190
  end
152
191
 
192
+ # @return [Array<String>] the shell to drop the user into, PowerShell on
193
+ # Windows and an interactive login bash elsewhere
153
194
  def login_shell
154
195
  windows_container? ? ["powershell"] : ["/bin/bash", "--login", "-i"]
155
196
  end