diff options
author | Jim MacArthur <jim.macarthur@codethink.co.uk> | 2018-03-13 15:17:22 +0000 |
---|---|---|
committer | Tristan Van Berkom <tristan.van.berkom@gmail.com> | 2018-03-23 14:37:38 +0000 |
commit | f4d3892e4b7276f25599ce6e9fbae840ab10c833 (patch) | |
tree | 5f300f0851a2b28bd4e569cc2b6d2fa4a4964d7c /buildstream | |
parent | 7d92ef0f39f64eb7dfddb969d88487287733e5b2 (diff) | |
download | buildstream-f4d3892e4b7276f25599ce6e9fbae840ab10c833.tar.gz |
Add 'sandbox' configuration key and build-uid/build-gid elements
This only affects SandboxBWrap at the moment.
buildstream/_loader.py: Add Symbol.SANDBOX and allow it in validation
buildstream/_metaelement.py: Add 'sandbox' variable and store it in the object
buildstream/_project.py: Add 'sandbox' configuration key and load it from
project.conf.
buildstream/data/projectconfig.yaml: Default build-uid/build-gid values of 0
for 'sandbox'.
buildstream/element.py: Add __extract_sandbox_config to find the final sandbox
configuration. Pass this to the sandbox constructor.
buildstream/sandbox/_sandboxbwrap.py: If sandbox configuration was supplied,
use it for uid and gid instead of the default 0.
buildstream/sandbox/_sandboxchroot.py: Throw exception if non-0 uid/gid were
supplied.
buildstream/sandbox/__init__.py: Import SandboxConfig.
buildstream/sandbox/_private.py: New file, containing SandboxConfig. Made private
to avoid documentation for this class.
Diffstat (limited to 'buildstream')
-rw-r--r-- | buildstream/_loader.py | 6 | ||||
-rw-r--r-- | buildstream/_metaelement.py | 6 | ||||
-rw-r--r-- | buildstream/_project.py | 7 | ||||
-rw-r--r-- | buildstream/data/projectconfig.yaml | 7 | ||||
-rw-r--r-- | buildstream/element.py | 37 | ||||
-rw-r--r-- | buildstream/sandbox/_config.py | 30 | ||||
-rw-r--r-- | buildstream/sandbox/_sandboxbwrap.py | 4 | ||||
-rw-r--r-- | buildstream/sandbox/_sandboxchroot.py | 7 | ||||
-rw-r--r-- | buildstream/sandbox/sandbox.py | 16 |
9 files changed, 105 insertions, 15 deletions
diff --git a/buildstream/_loader.py b/buildstream/_loader.py index dc092dd0e..24dea7c97 100644 --- a/buildstream/_loader.py +++ b/buildstream/_loader.py @@ -57,6 +57,7 @@ class Symbol(): ALL = "all" DIRECTORY = "directory" JUNCTION = "junction" + SANDBOX = "sandbox" # A simple dependency object @@ -91,7 +92,7 @@ class LoadElement(): # Ensure the root node is valid _yaml.node_validate(self.data, [ - 'kind', 'depends', 'sources', + 'kind', 'depends', 'sources', 'sandbox', 'variables', 'environment', 'environment-nocache', 'config', 'public', 'description', ]) @@ -582,7 +583,8 @@ class Loader(): _yaml.node_get(data, Mapping, Symbol.VARIABLES, default_value={}), _yaml.node_get(data, Mapping, Symbol.ENVIRONMENT, default_value={}), _yaml.node_get(data, list, Symbol.ENV_NOCACHE, default_value=[]), - _yaml.node_get(data, Mapping, Symbol.PUBLIC, default_value={})) + _yaml.node_get(data, Mapping, Symbol.PUBLIC, default_value={}), + _yaml.node_get(data, Mapping, Symbol.SANDBOX, default_value={})) # Cache it now, make sure it's already there before recursing self.meta_elements[element_name] = meta_element diff --git a/buildstream/_metaelement.py b/buildstream/_metaelement.py index f9d95a3ce..7ba6ed0ed 100644 --- a/buildstream/_metaelement.py +++ b/buildstream/_metaelement.py @@ -36,8 +36,10 @@ class MetaElement(): # environment: The environment variables declared or overridden on this element # env_nocache: List of environment vars which should not be considered in cache keys # public: Public domain data dictionary + # sandbox: Configuration specific to the sandbox environment # - def __init__(self, project, name, kind, provenance, sources, config, variables, environment, env_nocache, public): + def __init__(self, project, name, kind, provenance, sources, config, + variables, environment, env_nocache, public, sandbox): self.project = project self.name = name self.kind = kind @@ -48,6 +50,6 @@ class MetaElement(): self.environment = environment self.env_nocache = env_nocache self.public = public - + self.sandbox = sandbox self.build_dependencies = [] self.dependencies = [] diff --git a/buildstream/_project.py b/buildstream/_project.py index 85319b414..aeacf6bdf 100644 --- a/buildstream/_project.py +++ b/buildstream/_project.py @@ -173,7 +173,7 @@ class Project(): 'aliases', 'name', 'artifacts', 'options', 'fail-on-overlap', 'shell', - 'ref-storage' + 'ref-storage', 'sandbox' ]) # The project name, element path and option declarations @@ -292,10 +292,13 @@ class Project(): if option.variable: self._variables[option.variable] = option.get_value() - # Load sandbox configuration + # Load sandbox environment variables self._environment = _yaml.node_get(config, Mapping, 'environment') self._env_nocache = _yaml.node_get(config, list, 'environment-nocache') + # Load sandbox configuration + self._sandbox = _yaml.node_get(config, Mapping, 'sandbox') + # Load project split rules self._splits = _yaml.node_get(config, Mapping, 'split-rules') diff --git a/buildstream/data/projectconfig.yaml b/buildstream/data/projectconfig.yaml index d8fefc5e1..408304544 100644 --- a/buildstream/data/projectconfig.yaml +++ b/buildstream/data/projectconfig.yaml @@ -110,6 +110,13 @@ environment: # environment-nocache: [] +# Configuration for the sandbox other than environment variables +# should go in 'sandbox'. This just contains the UID and GID that +# the user in the sandbox will have. Not all sandboxes will support +# changing the values. +sandbox: + build-uid: 0 + build-gid: 0 # Defaults for the 'split-rules' public data found on elements # in the 'bst' domain. diff --git a/buildstream/element.py b/buildstream/element.py index b62945f4b..57d3732e7 100644 --- a/buildstream/element.py +++ b/buildstream/element.py @@ -37,12 +37,13 @@ from . import _yaml from ._variables import Variables from ._exceptions import BstError, LoadError, LoadErrorReason, ImplError, ErrorDomain from . import Plugin, Consistency -from . import SandboxFlags +from . import Sandbox, SandboxFlags from . import utils from . import _cachekey from . import _signals from . import _site from ._platform import Platform +from .sandbox._config import SandboxConfig # The base BuildStream artifact version @@ -191,6 +192,9 @@ class Element(Plugin): self.__config = self.__extract_config(meta) self.configure(self.__config) + # Extract Sandbox config + self.__sandbox_config = self.__extract_sandbox_config(meta) + self.__tainted = None self.__workspaced_artifact = None self.__workspaced_dependencies_artifact = None @@ -1072,7 +1076,7 @@ class Element(Plugin): utils._force_rmtree(rootdir) with _signals.terminator(cleanup_rootdir), \ - self.__sandbox(rootdir, output_file, output_file) as sandbox: # nopep8 + self.__sandbox(rootdir, output_file, output_file, self.__sandbox_config) as sandbox: # nopep8 sandbox_root = sandbox.get_directory() @@ -1376,7 +1380,7 @@ class Element(Plugin): @contextmanager def _prepare_sandbox(self, scope, directory, integrate=True): - with self.__sandbox(directory) as sandbox: + with self.__sandbox(directory, config=self.__sandbox_config) as sandbox: # Configure always comes first, and we need it. self.configure_sandbox(sandbox) @@ -1694,7 +1698,7 @@ class Element(Plugin): # Private Local Methods # ############################################################# @contextmanager - def __sandbox(self, directory, stdout=None, stderr=None): + def __sandbox(self, directory, stdout=None, stderr=None, config=None): context = self._get_context() project = self._get_project() platform = Platform.get_platform() @@ -1703,7 +1707,8 @@ class Element(Plugin): sandbox = platform.create_sandbox(context, project, directory, stdout=stdout, - stderr=stderr) + stderr=stderr, + config=config) yield sandbox else: @@ -1711,7 +1716,7 @@ class Element(Plugin): rootdir = tempfile.mkdtemp(prefix="{}-".format(self.normal_name), dir=context.builddir) # Recursive contextmanager... - with self.__sandbox(rootdir, stdout=stdout, stderr=stderr) as sandbox: + with self.__sandbox(rootdir, stdout=stdout, stderr=stderr, config=config) as sandbox: yield sandbox # Cleanup the build dir @@ -1821,6 +1826,26 @@ class Element(Plugin): return config + # Sandbox-specific configuration data, to be passed to the sandbox's constructor. + # + def __extract_sandbox_config(self, meta): + project = self._get_project() + + # The default config is already composited with the project overrides + sandbox_defaults = _yaml.node_get(self.__defaults, Mapping, 'sandbox', default_value={}) + sandbox_defaults = _yaml.node_chain_copy(sandbox_defaults) + + sandbox_config = _yaml.node_chain_copy(project._sandbox) + _yaml.composite(sandbox_config, sandbox_defaults) + _yaml.composite(sandbox_config, meta.sandbox) + _yaml.node_final_assertions(sandbox_config) + + # Sandbox config, unlike others, has fixed members so we should validate them + _yaml.node_validate(sandbox_config, ['build-uid', 'build-gid']) + + return SandboxConfig(self.node_get_member(sandbox_config, int, 'build-uid'), + self.node_get_member(sandbox_config, int, 'build-gid')) + # This makes a special exception for the split rules, which # elements may extend but whos defaults are defined in the project. # diff --git a/buildstream/sandbox/_config.py b/buildstream/sandbox/_config.py new file mode 100644 index 000000000..56282ca1f --- /dev/null +++ b/buildstream/sandbox/_config.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 Codethink Limited +# +# 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/>. +# +# Authors: +# Jim MacArthur <jim.macarthur@codethink.co.uk> + + +"""A container for sandbox configuration data. We want the internals +of this to be opaque, hence putting it in its own private file. +""" + + +class SandboxConfig(): + def __init__(self, build_uid, build_gid): + self.build_uid = build_uid + self.build_gid = build_gid diff --git a/buildstream/sandbox/_sandboxbwrap.py b/buildstream/sandbox/_sandboxbwrap.py index f955a5e44..d18cb9ec0 100644 --- a/buildstream/sandbox/_sandboxbwrap.py +++ b/buildstream/sandbox/_sandboxbwrap.py @@ -149,7 +149,9 @@ class SandboxBwrap(Sandbox): if self.user_ns_available: bwrap_command += ['--unshare-user'] if not flags & SandboxFlags.INHERIT_UID: - bwrap_command += ['--uid', '0', '--gid', '0'] + uid = self._get_config().build_uid + gid = self._get_config().build_gid + bwrap_command += ['--uid', str(uid), '--gid', str(gid)] # Add the command bwrap_command += command diff --git a/buildstream/sandbox/_sandboxchroot.py b/buildstream/sandbox/_sandboxchroot.py index c5e14e485..7f27f50d0 100644 --- a/buildstream/sandbox/_sandboxchroot.py +++ b/buildstream/sandbox/_sandboxchroot.py @@ -38,6 +38,13 @@ from . import Sandbox, SandboxFlags class SandboxChroot(Sandbox): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + + uid = self._get_config().build_uid + gid = self._get_config().build_gid + if uid != 0 or gid != 0: + raise SandboxError("Chroot sandboxes cannot specify a non-root uid/gid " + "({},{} were supplied via config)".format(uid, gid)) + self.mount_map = None def run(self, command, flags, *, cwd=None, env=None): diff --git a/buildstream/sandbox/sandbox.py b/buildstream/sandbox/sandbox.py index f5caaa5e1..3ab75f1a8 100644 --- a/buildstream/sandbox/sandbox.py +++ b/buildstream/sandbox/sandbox.py @@ -88,12 +88,14 @@ class Sandbox(): def __init__(self, context, project, directory, **kwargs): self.__context = context self.__project = project - self.__stdout = kwargs['stdout'] - self.__stderr = kwargs['stderr'] self.__directories = [] self.__cwd = None self.__env = None self.__mount_sources = {} + # Configuration from kwargs common to all subclasses + self.__config = kwargs['config'] + self.__stdout = kwargs['stdout'] + self.__stderr = kwargs['stderr'] # Setup the directories self.__directory = directory @@ -269,3 +271,13 @@ class Sandbox(): # (file): The stderr, or None to inherit def _get_output(self): return (self.__stdout, self.__stderr) + + # _get_config() + # + # Fetches the sandbox configuration object. + # + # Returns: + # (SandboxConfig): An object containing the configuration + # data passed in during construction. + def _get_config(self): + return self.__config |