Source code for podman.domain.images_build

"""Mixin for Image build support."""

import json
import logging
import pathlib
import random
import re
import shutil
import tempfile
from typing import Any, Dict, Iterator, List, Tuple

import itertools

from podman import api
from podman.domain.images import Image
from podman.errors import BuildError, PodmanError, ImageNotFound

logger = logging.getLogger("podman.images")


[docs] class BuildMixin: """Class providing build method for ImagesManager.""" # pylint: disable=too-many-locals,too-many-branches,too-few-public-methods,too-many-statements
[docs] def build(self, **kwargs) -> Tuple[Image, Iterator[bytes]]: """Returns built image. Keyword Args: path (str) – Path to the directory containing the Dockerfile fileobj – A file object to use as the Dockerfile. (Or an IO object) tag (str) – A tag to add to the final image quiet (bool) – Whether to return the status nocache (bool) – Don’t use the cache when set to True rm (bool) – Remove intermediate containers. Default True timeout (int) – HTTP timeout custom_context (bool) – Optional if using fileobj (ignored) encoding (str) – The encoding for a stream. Set to gzip for compressing (ignored) pull (bool) – Downloads any updates to the FROM image in Dockerfile forcerm (bool) – Always remove intermediate containers, even after unsuccessful builds dockerfile (str) – full path to the Dockerfile / Containerfile buildargs (Mapping[str,str) – A dictionary of build arguments container_limits (Dict[str, Union[int,str]]) – A dictionary of limits applied to each container created by the build process. Valid keys: - memory (int): set memory limit for build - memswap (int): Total memory (memory + swap), -1 to disable swap - cpushares (int): CPU shares (relative weight) - cpusetcpus (str): CPUs in which to allow execution, For example, "0-3", "0,1" - cpuperiod (int): CPU CFS (Completely Fair Scheduler) period (Podman only) - cpuquota (int): CPU CFS (Completely Fair Scheduler) quota (Podman only) shmsize (int) – Size of /dev/shm in bytes. The size must be greater than 0. If omitted the system uses 64MB labels (Mapping[str,str]) – A dictionary of labels to set on the image cache_from (List[str]) – A list of image's identifier used for build cache resolution target (str) – Name of the build-stage to build in a multi-stage Dockerfile network_mode (str) – networking mode for the run commands during build squash (bool) – Squash the resulting images layers into a single layer. extra_hosts (Dict[str,str]) – Extra hosts to add to /etc/hosts in building containers, as a mapping of hostname to IP address. platform (str) – Platform in the format os[/arch[/variant]]. isolation (str) – Isolation technology used during build. (ignored) use_config_proxy (bool) – (ignored) http_proxy (bool) - Inject http proxy environment variables into container (Podman only) layers (bool) - Cache intermediate layers during build. output (str) - specifies if any custom build output is selected for following build. outputformat (str) - The format of the output image's manifest and configuration data. Returns: first item is the podman.domain.images.Image built second item is the build logs Raises: BuildError: when there is an error during the build APIError: when service returns an error TypeError: when neither path nor fileobj is not specified """ params = self._render_params(kwargs) body = None path = None if "fileobj" in kwargs: path = tempfile.TemporaryDirectory() # pylint: disable=consider-using-with filename = pathlib.Path(path.name) / params["dockerfile"] with open(filename, "w", encoding='utf-8') as file: shutil.copyfileobj(kwargs["fileobj"], file) body = api.create_tar(anchor=path.name, gzip=kwargs.get("gzip", False)) elif "path" in kwargs: filename = pathlib.Path(kwargs["path"]) / params["dockerfile"] # The Dockerfile will be copied into the context_dir if needed params["dockerfile"] = api.prepare_containerfile(kwargs["path"], str(filename)) excludes = api.prepare_containerignore(kwargs["path"]) body = api.create_tar( anchor=kwargs["path"], exclude=excludes, gzip=kwargs.get("gzip", False) ) post_kwargs = {} if kwargs.get("timeout"): post_kwargs["timeout"] = float(kwargs.get("timeout")) response = self.client.post( "/build", params=params, data=body, headers={ "Content-type": "application/x-tar", # "X-Registry-Config": "TODO", }, stream=True, **post_kwargs, ) if hasattr(body, "close"): body.close() if hasattr(path, "cleanup"): path.cleanup() response.raise_for_status(not_found=ImageNotFound) image_id = unknown = None marker = re.compile(r"(^[0-9a-f]+)\n$") report_stream, stream = itertools.tee(response.iter_lines()) for line in stream: result = json.loads(line) if "error" in result: raise BuildError(result["error"], report_stream) if "stream" in result: match = marker.match(result["stream"]) if match: image_id = match.group(1) unknown = line if image_id: return self.get(image_id), report_stream raise BuildError(unknown or "Unknown", report_stream)
@staticmethod def _render_params(kwargs) -> Dict[str, List[Any]]: """Map kwargs to query parameters. All unsupported kwargs are silently ignored. """ if "path" not in kwargs and "fileobj" not in kwargs: raise TypeError("Either path or fileobj must be provided.") if "gzip" in kwargs and "encoding" in kwargs: raise PodmanError("Custom encoding not supported when gzip enabled.") params = { "dockerfile": kwargs.get("dockerfile"), "forcerm": kwargs.get("forcerm"), "httpproxy": kwargs.get("http_proxy"), "networkmode": kwargs.get("network_mode"), "nocache": kwargs.get("nocache"), "platform": kwargs.get("platform"), "pull": kwargs.get("pull"), "q": kwargs.get("quiet"), "remote": kwargs.get("remote"), "rm": kwargs.get("rm"), "shmsize": kwargs.get("shmsize"), "squash": kwargs.get("squash"), "t": kwargs.get("tag"), "target": kwargs.get("target"), "layers": kwargs.get("layers"), "output": kwargs.get("output"), "outputformat": kwargs.get("outputformat"), } if "buildargs" in kwargs: params["buildargs"] = json.dumps(kwargs.get("buildargs")) if "cache_from" in kwargs: params["cachefrom"] = json.dumps(kwargs.get("cache_from")) if "container_limits" in kwargs: params["cpuperiod"] = kwargs["container_limits"].get("cpuperiod") params["cpuquota"] = kwargs["container_limits"].get("cpuquota") params["cpusetcpus"] = kwargs["container_limits"].get("cpusetcpus") params["cpushares"] = kwargs["container_limits"].get("cpushares") params["memory"] = kwargs["container_limits"].get("memory") params["memswap"] = kwargs["container_limits"].get("memswap") if "extra_hosts" in kwargs: params["extrahosts"] = json.dumps(kwargs.get("extra_hosts")) if "labels" in kwargs: params["labels"] = json.dumps(kwargs.get("labels")) if params["dockerfile"] is None: params["dockerfile"] = f".containerfile.{random.getrandbits(160):x}" # Remove any unset parameters return dict(filter(lambda i: i[1] is not None, params.items()))