diff options
-rw-r--r-- | src/buildstream/_platform/linux.py | 27 | ||||
-rw-r--r-- | src/buildstream/sandbox/_sandboxbuildbox.py | 248 |
2 files changed, 0 insertions, 275 deletions
diff --git a/src/buildstream/_platform/linux.py b/src/buildstream/_platform/linux.py index a6215c90f..05e55e0f5 100644 --- a/src/buildstream/_platform/linux.py +++ b/src/buildstream/_platform/linux.py @@ -24,14 +24,12 @@ from .. import utils from ..sandbox import SandboxDummy from .platform import Platform -from .._exceptions import PlatformError class Linux(Platform): def _setup_sandbox(self, force_sandbox): sandbox_setups = { "bwrap": self._setup_bwrap_sandbox, - "buildbox": self._setup_buildbox_sandbox, "buildbox-run": self.setup_buildboxrun_sandbox, "chroot": self._setup_chroot_sandbox, "dummy": self._setup_dummy_sandbox, @@ -129,28 +127,3 @@ class Linux(Platform): self.check_sandbox_config = self._check_sandbox_config_chroot self.create_sandbox = Linux._create_chroot_sandbox return True - - # Buildbox sandbox methods - def _check_sandbox_config_buildbox(self, config): - from ..sandbox._sandboxbuildbox import SandboxBuildBox - - return SandboxBuildBox.check_sandbox_config(self, config) - - @staticmethod - def _create_buildbox_sandbox(*args, **kwargs): - from ..sandbox._sandboxbuildbox import SandboxBuildBox - - if kwargs.get("allow_real_directory"): - raise PlatformError( - "The BuildBox Sandbox does not support real directories.", - reason="You are using BuildBox sandbox because BST_FORCE_SANBOX=buildbox", - ) - return SandboxBuildBox(*args, **kwargs) - - def _setup_buildbox_sandbox(self): - from ..sandbox._sandboxbuildbox import SandboxBuildBox - - self._check_sandbox(SandboxBuildBox) - self.check_sandbox_config = self._check_sandbox_config_buildbox - self.create_sandbox = self._create_buildbox_sandbox - return True diff --git a/src/buildstream/sandbox/_sandboxbuildbox.py b/src/buildstream/sandbox/_sandboxbuildbox.py deleted file mode 100644 index c34d95223..000000000 --- a/src/buildstream/sandbox/_sandboxbuildbox.py +++ /dev/null @@ -1,248 +0,0 @@ -# -# Copyright (C) 2018 Bloomberg LP -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library. If not, see <http://www.gnu.org/licenses/>. - -import os -import sys -import signal -import subprocess -from contextlib import ExitStack - -import psutil - -from .. import utils, _signals, ProgramNotFoundError -from . import Sandbox, SandboxFlags, SandboxCommandError -from .._protos.build.bazel.remote.execution.v2 import remote_execution_pb2 -from ..storage._casbaseddirectory import CasBasedDirectory -from .._exceptions import SandboxError - - -# SandboxBuidBox() -# -# BuildBox-based sandbox implementation. -# -class SandboxBuildBox(Sandbox): - def __init__(self, context, project, directory, **kwargs): - if kwargs.get("allow_real_directory"): - raise SandboxError("BuildBox does not support real directories") - - kwargs["allow_real_directory"] = False - super().__init__(context, project, directory, **kwargs) - - @classmethod - def check_available(cls): - try: - utils.get_host_tool("buildbox") - except utils.ProgramNotFoundError as Error: - cls._dummy_reasons += ["buildbox not found"] - raise SandboxError(" and ".join(cls._dummy_reasons), reason="unavailable-local-sandbox") from Error - - @classmethod - def check_sandbox_config(cls, platform, config): - # Report error for elements requiring non-0 UID/GID - # TODO - if config.build_uid != 0 or config.build_gid != 0: - return False - - # Check host os and architecture match - if config.build_os != platform.get_host_os(): - raise SandboxError("Configured and host OS don't match.") - if config.build_arch != platform.get_host_arch(): - raise SandboxError("Configured and host architecture don't match.") - - return True - - def _run(self, command, flags, *, cwd, env): - stdout, stderr = self._get_output() - - root_directory = self.get_virtual_directory() - scratch_directory = self._get_scratch_directory() - - if not self._has_command(command[0], env): - raise SandboxCommandError( - "Staged artifacts do not provide command " "'{}'".format(command[0]), reason="missing-command" - ) - - # Grab the full path of the buildbox binary - try: - buildbox_command = [utils.get_host_tool("buildbox")] - except ProgramNotFoundError as Err: - raise SandboxError( - ("BuildBox not on path, you are using the BuildBox sandbox because " "BST_FORCE_SANDBOX=buildbox") - ) from Err - - for mark in self._get_marked_directories(): - path = mark["directory"] - assert path.startswith("/") and len(path) > 1 - root_directory.descend(*path[1:].split(os.path.sep), create=True) - - digest = root_directory._get_digest() - with open(os.path.join(scratch_directory, "in"), "wb") as input_digest_file: - input_digest_file.write(digest.SerializeToString()) - - buildbox_command += ["--local=" + root_directory.cas_cache.casdir] - buildbox_command += ["--input-digest=in"] - buildbox_command += ["--output-digest=out"] - - common_details = ( - "BuildBox is a experimental sandbox and does not support the requested feature.\n" - "You are using this feature because BST_FORCE_SANDBOX=buildbox." - ) - - if not flags & SandboxFlags.NETWORK_ENABLED: - # TODO - self._issue_warning("BuildBox sandbox does not have Networking yet", detail=common_details) - - if cwd is not None: - buildbox_command += ["--chdir=" + cwd] - - # In interactive mode, we want a complete devpts inside - # the container, so there is a /dev/console and such. In - # the regular non-interactive sandbox, we want to hand pick - # a minimal set of devices to expose to the sandbox. - # - if flags & SandboxFlags.INTERACTIVE: - # TODO - self._issue_warning( - "BuildBox sandbox does not fully support BuildStream shells yet", detail=common_details - ) - - if flags & SandboxFlags.ROOT_READ_ONLY: - # TODO - self._issue_warning( - "BuildBox sandbox does not fully support BuildStream `Read only Root`", detail=common_details - ) - - # Set UID and GID - if not flags & SandboxFlags.INHERIT_UID: - # TODO - self._issue_warning( - "BuildBox sandbox does not fully support BuildStream Inherit UID", detail=common_details - ) - - os.makedirs(os.path.join(scratch_directory, "mnt"), exist_ok=True) - buildbox_command += ["mnt"] - - # Add the command - buildbox_command += command - - # Use the MountMap context manager to ensure that any redirected - # mounts through fuse layers are in context and ready for buildbox - # to mount them from. - # - with ExitStack() as stack: - # Ensure the cwd exists - if cwd is not None and len(cwd) > 1: - assert cwd.startswith("/") - root_directory.descend(*cwd[1:].split(os.path.sep), create=True) - - # If we're interactive, we want to inherit our stdin, - # otherwise redirect to /dev/null, ensuring process - # disconnected from terminal. - if flags & SandboxFlags.INTERACTIVE: - stdin = sys.stdin - else: - stdin = stack.enter_context(open(os.devnull, "r")) - - # Run buildbox ! - exit_code = self.run_buildbox( - buildbox_command, - stdin, - stdout, - stderr, - env, - interactive=(flags & SandboxFlags.INTERACTIVE), - cwd=scratch_directory, - ) - - if exit_code == 0: - with open(os.path.join(scratch_directory, "out"), "rb") as output_digest_file: - output_digest = remote_execution_pb2.Digest() - output_digest.ParseFromString(output_digest_file.read()) - self._vdir = CasBasedDirectory(root_directory.cas_cache, digest=output_digest) - - return exit_code - - def run_buildbox(self, argv, stdin, stdout, stderr, env, *, interactive, cwd): - def kill_proc(): - if process: - # First attempt to gracefully terminate - proc = psutil.Process(process.pid) - proc.terminate() - - try: - proc.wait(20) - except psutil.TimeoutExpired: - utils._kill_process_tree(process.pid) - - def suspend_proc(): - group_id = os.getpgid(process.pid) - os.killpg(group_id, signal.SIGSTOP) - - def resume_proc(): - group_id = os.getpgid(process.pid) - os.killpg(group_id, signal.SIGCONT) - - with _signals.suspendable(suspend_proc, resume_proc), _signals.terminator(kill_proc): - process = subprocess.Popen( - argv, - close_fds=True, - env=env, - stdin=stdin, - stdout=stdout, - stderr=stderr, - cwd=cwd, - start_new_session=interactive, - ) - - # Wait for the child process to finish, ensuring that - # a SIGINT has exactly the effect the user probably - # expects (i.e. let the child process handle it). - try: - while True: - try: - _, status = os.waitpid(process.pid, 0) - # If the process exits due to a signal, we - # brutally murder it to avoid zombies - if not os.WIFEXITED(status): - utils._kill_process_tree(process.pid) - - # Unlike in the bwrap case, here only the main - # process seems to receive the SIGINT. We pass - # on the signal to the child and then continue - # to wait. - except KeyboardInterrupt: - process.send_signal(signal.SIGINT) - continue - - break - # If we can't find the process, it has already died of - # its own accord, and therefore we don't need to check - # or kill anything. - except psutil.NoSuchProcess: - pass - - # Return the exit code - see the documentation for - # os.WEXITSTATUS to see why this is required. - if os.WIFEXITED(status): - exit_code = os.WEXITSTATUS(status) - else: - exit_code = -1 - - return exit_code - - def _use_cas_based_directory(self): - # Always use CasBasedDirectory for BuildBox - return True |