summaryrefslogtreecommitdiff
path: root/buildstream/sandbox/_sandboxdocker.py
blob: 26d261ed46dc5f21365e5f26074b592d20901eed (plain)
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
import os

import docker

from .._exceptions import SandboxError
from ._mount import MountMap
from . import Sandbox, SandboxFlags


DOCKERFILE = """
FROM scratch

ADD . /
""".strip()


class SandboxDocker(Sandbox):

    def run(self, command, flags, *, cwd=None, env=None):
        client = docker.from_env()

        # Fallback to the sandbox default settings for
        # the cwd and env.
        #
        cwd = self._get_work_directory(cwd=cwd)
        env = self._get_environment(cwd=cwd, env=env)

        # Convert single-string argument to a list
        if isinstance(command, str):
            command = [command]

        if not self._has_command(command[0], env):
            raise SandboxError("Staged artifacts do not provide command "
                               "'{}'".format(command[0]),
                               reason='missing-command')

        # Create the mount map, this will tell us where
        # each mount point needs to be mounted from and to
        mount_map = MountMap(self, flags & SandboxFlags.ROOT_READ_ONLY)
        root_mount_source = mount_map.get_mount_source("/")

        with open(os.path.join(root_mount_source, "Dockerfile"), "w") as fp:
            fp.write(DOCKERFILE)

        image, _ = client.images.build(path=root_mount_source)

        volumes = {}

        mount_source_overrides = self._get_mount_sources()
        for mark in self._get_marked_directories():
            mount_point = mark["directory"]
            if mount_point in mount_source_overrides:  # pylint: disable=consider-using-get
                mount_source = mount_source_overrides[mount_point]
            else:
                mount_source = mount_map.get_mount_source(mount_point)

            volumes[mount_source] = {"bind": mount_point}

        # TODO: we need to handle root that is RO
        # TODO: we need to handle cwd
        # TODO: we need to handle env
        # TODO: we need to support specific user uid/gid
        # TODO: we need to support interactive mode
        args = {
            "image": image,
            "command": command,
            "detach": True,
            "volumes": volumes,
        }

        container = client.containers.run(**args)
        # TODO: we need to handle signals and react accordingly
        status = container.wait()

        self._vdir._mark_changed()
        return status["StatusCode"]