Module: Kitchen::Docker::Helpers::ImageHelper
- Includes:
- Configurable, CliHelper, ContainerHelper
- Included in:
- Container
- Defined in:
- lib/kitchen/docker/helpers/image_helper.rb
Overview
Building, inspecting, and removing Docker images.
Constant Summary
Constants included from ContainerHelper
ContainerHelper::COPIED_MARKER
Instance Method Summary collapse
-
#build_image(state, dockerfile) ⇒ String
Builds the image from the given Dockerfile.
-
#image_exists?(state) ⇒ Boolean
Whether the image named in state is present locally.
-
#image_in_use?(state) ⇒ Boolean
Whether any container was created from the image.
-
#parse_image_id(output) ⇒ String
Pulls the built image's id out of
docker buildoutput. -
#remove_image(state) ⇒ void
Removes the built image, unless a container is still using it.
Methods included from 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 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
#build_image(state, dockerfile) ⇒ String
Builds the image from the given Dockerfile.
The Dockerfile is written to a temp file and also passed on stdin, so the build works both with a build context and without one. The temp file is removed whether or not the build succeeded.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/kitchen/docker/helpers/image_helper.rb', line 105 def build_image(state, dockerfile) cmd = "build" cmd << " --no-cache" unless config[:use_cache] cmd << " --platform=#{config[:docker_platform]}" if config[:docker_platform] = (config[:build_options]) cmd << " #{}" unless .empty? dockerfile_contents = dockerfile file = Tempfile.new("Dockerfile-kitchen", Pathname.pwd + config[:build_tempdir]) cmd << " -f #{Shellwords.escape(dockerfile_path(file))}" if config[:build_context] build_context = config[:build_context] ? "." : "-" output = begin file.write(dockerfile) file.close docker_command("#{cmd} #{build_context}", input: dockerfile_contents, environment: { BUILDKIT_PROGRESS: "plain" }) ensure file.close unless file.closed? file.unlink end parse_image_id(output) end |
#image_exists?(state) ⇒ Boolean
Whether the image named in state is present locally.
The inspect is silenced, as every other predicate that shells out to
docker is. Left speaking, kitchen destroy on an instance with
remove_images set printed the image's entire docker inspect JSON
-- config, every layer digest, metadata -- into the middle of the
destroy output, between the container being removed and the image
being removed. Nothing read it: only whether the command succeeded is
used.
It is still printed under -l debug, where the rest of the driver's
docker traffic is.
144 145 146 147 148 149 150 151 |
# File 'lib/kitchen/docker/helpers/image_helper.rb', line 144 def image_exists?(state) return false unless state[:image_id] !!docker_command("inspect --type=image #{state[:image_id]}", suppress_output: !logger.debug?) rescue false end |
#image_in_use?(state) ⇒ Boolean
Whether any container was created from the image.
Asked with a filter rather than by searching docker ps -a output for
the id. That output abbreviates the IMAGE column to twelve characters,
while state carries the full sha256: digest, so the substring never
matched and the answer was always false -- which defeated the guard
entirely and let #remove_image run docker rmi against an image a
container was still using.
82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/kitchen/docker/helpers/image_helper.rb', line 82 def image_in_use?(state) return false unless state[:image_id] output = docker_command("ps -a -q --filter ancestor=#{state[:image_id]}", suppress_output: !logger.debug?) # Matched line by line rather than by emptiness, so a warning docker # writes to stderr is not mistaken for a container id. output.lines.map(&:strip).any? do |line| line.match?(/\A[0-9a-f]{12}(?:[0-9a-f]{52})?\z/) end end |
#parse_image_id(output) ⇒ String
Pulls the built image's id out of docker build output.
Scanned in reverse, and against several patterns, because the wording has changed across Docker and BuildKit versions.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/kitchen/docker/helpers/image_helper.rb', line 38 def parse_image_id(output) output.split("\n").reverse_each do |line| if line =~ /writing image (sha256:[[:xdigit:]]{64})(?: \d*\.\ds)? done/i img_id = line[/writing image (sha256:[[:xdigit:]]{64})(?: \d*\.\ds)? done/i, 1] return img_id end if line =~ /image id|build successful|successfully built/i img_id = line.split(/\s+/).last return img_id end # Docker ~v4.31 support if line =~ /naming to moby-dangling@(sha256:[[:xdigit:]]{64})(?: \d*\.\ds)? done/i img_id = line[/naming to moby-dangling@(sha256:[[:xdigit:]]{64})(?: \d*\.\ds)? done/i, 1] return img_id end end raise ActionFailed, "Could not parse Docker build output for image ID" end |
#remove_image(state) ⇒ void
This method returns an undefined value.
Removes the built image, unless a container is still using it.
61 62 63 64 65 66 67 68 69 |
# File 'lib/kitchen/docker/helpers/image_helper.rb', line 61 def remove_image(state) image_id = state[:image_id] if image_in_use?(state) info("[Docker] Image ID #{image_id} is in use. Skipping removal") else info("[Docker] Removing image with Image ID #{image_id}.") docker_command("rmi #{image_id}") end end |