Class: Kitchen::Driver::Docker
- Inherits:
-
Base
- Object
- Base
- Kitchen::Driver::Docker
- Includes:
- Kitchen::Docker::Helpers::CliHelper, Kitchen::Docker::Helpers::ContainerHelper, ShellOut
- Defined in:
- lib/kitchen/driver/docker.rb
Overview
Docker driver for Kitchen.
Constant Summary
Constants included from Kitchen::Docker::Helpers::ContainerHelper
Kitchen::Docker::Helpers::ContainerHelper::COPIED_MARKER
Instance Method Summary collapse
-
#container ⇒ Kitchen::Docker::Container
protected
The container implementation for this platform.
-
#create(state) ⇒ void
Builds the image and starts the container.
-
#default_image ⇒ String
The Docker image implied by the platform name.
-
#default_platform ⇒ String
The platform family, e.g.
-
#destroy(state) ⇒ void
Removes the container, and its image when
remove_imagesis set. -
#doctor(state) ⇒ Boolean
Checks the configuration and the daemon it points at.
-
#doctor_container(state) ⇒ Boolean
protected
Whether state names a container that is gone.
-
#doctor_daemon ⇒ Boolean
protected
Whether the daemon could not be reached.
-
#doctor_files ⇒ Boolean
protected
Checks paths the configuration names.
-
#package(state) ⇒ void
Commits the container to a Docker image.
-
#status(state) ⇒ Hash
Reports whether the container backing this instance is up.
-
#verify_dependencies ⇒ void
Checks that the Docker CLI is installed and runnable.
-
#wait_for_transport(state) ⇒ void
Waits for the transport to accept a connection, unless disabled.
Methods included from Kitchen::Docker::Helpers::ContainerHelper
#container_env_variables, #container_exec, #container_exists?, #container_ip_address, #container_running?, #copy_file_to_container, #create_dir_on_container, #dockerfile_path, #dockerfile_proxy_config, #dockerfile_template, #file_on_container?, #ip_address?, #parse_container_id, #proxy_env_vars, #remote_socket?, #remove_container, #replace_env_variables, #run_container, #socket_uri, #verify_file_copied
Methods included from Kitchen::Docker::Helpers::CliHelper
#build_copy_command, #build_env_variable_args, #build_exec_command, #build_powershell_command, #build_run_command, #config_to_options, #dev_null, #docker_command, #docker_shell_opts, #docker_sudo_opts, #run_command, #shell_escape
Instance Method Details
#container ⇒ Kitchen::Docker::Container (protected)
The container implementation for this platform.
308 309 310 311 312 313 314 315 |
# File 'lib/kitchen/driver/docker.rb', line 308 def container @container ||= if windows_os? Kitchen::Docker::Container::Windows.new(config) else Kitchen::Docker::Container::Linux.new(config) end @container end |
#create(state) ⇒ void
This method returns an undefined value.
Builds the image and starts the container.
146 147 148 149 150 |
# File 'lib/kitchen/driver/docker.rb', line 146 def create(state) container.create(state) wait_for_transport(state) end |
#default_image ⇒ String
The Docker image implied by the platform name.
ubuntu-22.04 becomes ubuntu:22.04. CentOS is special-cased, since its
images are tagged centos7 rather than centos:7.
252 253 254 255 256 257 258 |
# File 'lib/kitchen/driver/docker.rb', line 252 def default_image platform, release = instance.platform.name.split("-") if platform == "centos" && release release = "centos" + release.split(".").first end release ? [platform, release].join(":") : platform end |
#default_platform ⇒ String
Returns the platform family, e.g. ubuntu from ubuntu-22.04.
261 262 263 |
# File 'lib/kitchen/driver/docker.rb', line 261 def default_platform instance.platform.name.split("-").first end |
#destroy(state) ⇒ void
This method returns an undefined value.
Removes the container, and its image when remove_images is set.
156 157 158 |
# File 'lib/kitchen/driver/docker.rb', line 156 def destroy(state) container.destroy(state) end |
#doctor(state) ⇒ Boolean
Checks the configuration and the daemon it points at.
Run by kitchen doctor. A true return is how Test Kitchen decides to
exit non-zero, so every check runs and the results are OR-ed together
rather than returning at the first problem -- somebody running doctor
wants the whole list, not the first item on it.
202 203 204 205 206 207 208 |
# File 'lib/kitchen/driver/docker.rb', line 202 def doctor(state) [ doctor_daemon, doctor_files, doctor_container(state), ].any? end |
#doctor_container(state) ⇒ Boolean (protected)
Returns whether state names a container that is gone.
296 297 298 299 300 301 302 303 |
# File 'lib/kitchen/driver/docker.rb', line 296 def doctor_container(state) return false unless state[:container_id] return false if container_exists?(state) error("The state file names container #{state[:container_id]}, which the daemon does " \ "not have. Run `kitchen destroy` to clear it.") true end |
#doctor_daemon ⇒ Boolean (protected)
Returns whether the daemon could not be reached.
268 269 270 271 272 273 274 275 |
# File 'lib/kitchen/driver/docker.rb', line 268 def doctor_daemon version = docker_command("version --format '{{.Server.Version}}'", suppress_output: true).strip info("Docker daemon at #{config[:socket]} is reachable, running #{version}.") false rescue => e error("Cannot reach the Docker daemon at #{config[:socket]}. #{e}") true end |
#doctor_files ⇒ Boolean (protected)
Checks paths the configuration names.
A missing TLS file or Dockerfile is worth catching here because docker reports it far from the cause -- a missing client certificate surfaces as a connection error rather than as a missing file.
284 285 286 287 288 289 290 291 292 |
# File 'lib/kitchen/driver/docker.rb', line 284 def doctor_files %i{tls_cacert tls_cert tls_key dockerfile}.map do |key| path = config[key] next false if path.nil? || ::File.exist?(::File.(path)) error("#{key} is set to #{path}, which does not exist.") true end.any? end |
#package(state) ⇒ void
This method returns an undefined value.
Commits the container to a Docker image.
kitchen package asks a driver to turn a converged instance into
something reusable. For Docker that is an image: docker commit on the
running container, which is the artifact every other docker tool
already takes. Run docker save against the result for a tarball.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/kitchen/driver/docker.rb', line 170 def package(state) unless state[:container_id] raise ActionFailed, "Cannot package #{instance.name}: it has not been created." end # Asked here rather than left to `docker commit`, which reports a # container that is gone as a bare "Error response from daemon: No such # container: <64 hex characters>" with nothing about the instance or # what to do next. unless container_exists?(state) raise ActionFailed, "Cannot package #{instance.name}: the state file names container " \ "#{state[:container_id]}, which the daemon does not have. " \ "Run `kitchen destroy` to clear it." end name = config[:package_name] info("[Docker] Committing container #{state[:container_id]} to #{name}") output = docker_command("commit #{shell_escape(state[:container_id])} #{shell_escape(name)}", suppress_output: !logger.debug?) image_id = output.lines.map(&:strip).find { |line| line.match?(/\Asha256:[[:xdigit:]]{64}\z/) } info("[Docker] Packaged #{instance.name} as #{name}#{" (#{image_id})" if image_id}") end |
#status(state) ⇒ Hash
Reports whether the container backing this instance is up.
Read by kitchen list --live, which showed "unknown" for every
instance: Base cannot know, and this driver never
said. Docker can answer directly, and these are the same two questions
create and destroy already ask.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/kitchen/driver/docker.rb', line 219 def status(state) common = { source: "driver", checked_at: Time.now.utc.iso8601, resource_id: state[:container_id] } if !state[:container_id] common.merge(live: false, state: "not created", message: "No container is recorded in the state file") elsif !container_exists?(state) common.merge(live: false, state: "gone", message: "The state file names a container the daemon does not have") elsif container_running?(state) common.merge(live: true, state: "running") else common.merge(live: false, state: "stopped", message: "The container exists but is not running") end end |
#verify_dependencies ⇒ void
This method returns an undefined value.
Checks that the Docker CLI is installed and runnable.
136 137 138 139 140 |
# File 'lib/kitchen/driver/docker.rb', line 136 def verify_dependencies run_command("#{config[:binary]} >> #{dev_null} 2>&1", quiet: true, use_sudo: config[:use_sudo]) rescue raise UserError, "You must first install the Docker CLI tool https://www.docker.com/get-started" end |
#wait_for_transport(state) ⇒ void
This method returns an undefined value.
Waits for the transport to accept a connection, unless disabled.
240 241 242 243 244 |
# File 'lib/kitchen/driver/docker.rb', line 240 def wait_for_transport(state) if config[:wait_for_transport] instance.transport.connection(state, &:wait_until_ready) end end |