summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Schubert <ben.c.schubert@gmail.com>2018-11-20 17:54:07 +0000
committerBenjamin Schubert <ben.c.schubert@gmail.com>2018-12-07 14:33:06 +0000
commit58b38f32f304623b6ed5544fedca4b4d56fdbad8 (patch)
treea0c1a6ad74f444840efab59354a4c14a3a3cb2bd
parent2a0676c3bf607a95a41bd802839eaf677588bf79 (diff)
downloadbuildstream-58b38f32f304623b6ed5544fedca4b4d56fdbad8.tar.gz
First draft for docker sandbox
-rw-r--r--buildstream/_platform/linux.py4
-rwxr-xr-xbuildstream/sandbox/_sandboxdocker.py87
2 files changed, 91 insertions, 0 deletions
diff --git a/buildstream/_platform/linux.py b/buildstream/_platform/linux.py
index 702059a5d..ac2488095 100644
--- a/buildstream/_platform/linux.py
+++ b/buildstream/_platform/linux.py
@@ -63,6 +63,10 @@ class Linux(Platform):
self._linux32 = False
def create_sandbox(self, *args, **kwargs):
+ # FIXME: we need a better way rather than hijacking
+ # the normal setup process
+ from ..sandbox._sandboxdocker import SandboxDocker
+ return SandboxDocker(*args, **kwargs)
if not self._local_sandbox_available:
return self._create_dummy_sandbox(*args, **kwargs)
else:
diff --git a/buildstream/sandbox/_sandboxdocker.py b/buildstream/sandbox/_sandboxdocker.py
new file mode 100755
index 000000000..d7342a9f5
--- /dev/null
+++ b/buildstream/sandbox/_sandboxdocker.py
@@ -0,0 +1,87 @@
+import os
+import sys
+import stat
+import signal
+import subprocess
+from contextlib import contextmanager, ExitStack
+import psutil
+import tempfile
+
+import docker
+
+from .._exceptions import SandboxError
+from .. import utils
+from .. import _signals
+from ._mounter import Mounter
+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()
+ stdout, stderr = self._get_output()
+
+ # 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:
+ 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"]