File: images_build.py

package info (click to toggle)
python-podman 5.4.0.1-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,140 kB
  • sloc: python: 7,532; makefile: 82; sh: 75
file content (197 lines) | stat: -rw-r--r-- 8,625 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
"""Mixin for Image build support."""

import json
import logging
import pathlib
import random
import re
import shutil
import tempfile
from typing import Any
from collections.abc import Iterator

import itertools

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

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


class BuildMixin:
    """Class providing build method for ImagesManager."""

    # pylint: disable=too-many-locals,too-many-branches,too-few-public-methods,too-many-statements
    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()))