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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +0 -1
- data/.yardopts +11 -0
- data/CHANGELOG.md +381 -21
- data/CONTRIBUTING.md +203 -0
- data/Gemfile +4 -0
- data/README.md +417 -533
- data/Rakefile +18 -0
- data/lib/kitchen/docker/container/linux.rb +50 -0
- data/lib/kitchen/docker/container/windows.rb +27 -0
- data/lib/kitchen/docker/container.rb +34 -0
- data/lib/kitchen/docker/docker_version.rb +4 -2
- data/lib/kitchen/docker/erb_context.rb +9 -0
- data/lib/kitchen/docker/helpers/cli_helper.rb +56 -3
- data/lib/kitchen/docker/helpers/container_helper.rb +91 -19
- data/lib/kitchen/docker/helpers/dockerfile_helper.rb +54 -0
- data/lib/kitchen/docker/helpers/file_helper.rb +8 -0
- data/lib/kitchen/docker/helpers/image_helper.rb +28 -0
- data/lib/kitchen/docker/helpers/inspec_helper.rb +1 -0
- data/lib/kitchen/driver/docker.rb +27 -0
- data/lib/kitchen/transport/docker.rb +43 -2
- data/spec/cli_helper_spec.rb +312 -0
- data/spec/container_helper_spec.rb +178 -0
- data/spec/dockerfile_helper_spec.rb +97 -61
- data/spec/erb_context_spec.rb +60 -0
- data/spec/image_helper_spec.rb +142 -0
- data/spec/linux_container_spec.rb +194 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/argv.rb +55 -0
- data/spec/support/docker_output.rb +117 -0
- data/spec/support/harness.rb +70 -0
- data/spec/transport_docker_spec.rb +7 -0
- data/spec/windows_container_spec.rb +101 -0
- metadata +13 -2
|
@@ -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
|
-
|
|
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
|
-
#
|
|
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
|