diff options
Diffstat (limited to 'zuul/driver')
-rw-r--r-- | zuul/driver/__init__.py | 24 | ||||
-rw-r--r-- | zuul/driver/bubblewrap/__init__.py | 168 | ||||
-rw-r--r-- | zuul/driver/nullwrap/__init__.py | 28 |
3 files changed, 220 insertions, 0 deletions
diff --git a/zuul/driver/__init__.py b/zuul/driver/__init__.py index 671996ac3..0c3105d1b 100644 --- a/zuul/driver/__init__.py +++ b/zuul/driver/__init__.py @@ -254,3 +254,27 @@ class ReporterInterface(object): """ pass + + +@six.add_metaclass(abc.ABCMeta) +class WrapperInterface(object): + """The wrapper interface to be implmeneted by a driver. + + A driver which wraps execution of commands executed by Zuul should + implement this interface. + + """ + + @abc.abstractmethod + def getPopen(self, **kwargs): + """Create and return a subprocess.Popen factory wrapped however the + driver sees fit. + + This method is required by the interface + + :arg dict kwargs: key/values for use by driver as needed + + :returns: a callable that takes the same args as subprocess.Popen + :rtype: Callable + """ + pass diff --git a/zuul/driver/bubblewrap/__init__.py b/zuul/driver/bubblewrap/__init__.py new file mode 100644 index 000000000..6426fbbb1 --- /dev/null +++ b/zuul/driver/bubblewrap/__init__.py @@ -0,0 +1,168 @@ +# Copyright 2012 Hewlett-Packard Development Company, L.P. +# Copyright 2013 OpenStack Foundation +# Copyright 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import argparse +import grp +import logging +import os +import pwd +import subprocess +import sys + +from zuul.driver import (Driver, WrapperInterface) + + +class WrappedPopen(object): + def __init__(self, command, passwd_r, group_r): + self.command = command + self.passwd_r = passwd_r + self.group_r = group_r + + def __call__(self, args, *sub_args, **kwargs): + try: + args = self.command + args + if kwargs.get('close_fds') or sys.version_info.major >= 3: + # The default in py3 is close_fds=True, so we need to pass + # our open fds in. However, this can only work right in + # py3.2 or later due to the lack of 'pass_fds' in prior + # versions. So until we are py3 only we can only bwrap + # things that are close_fds=False + pass_fds = list(kwargs.get('pass_fds', [])) + for fd in (self.passwd_r, self.group_r): + if fd not in pass_fds: + pass_fds.append(fd) + kwargs['pass_fds'] = pass_fds + proc = subprocess.Popen(args, *sub_args, **kwargs) + finally: + self.__del__() + return proc + + def __del__(self): + if self.passwd_r: + try: + os.close(self.passwd_r) + except OSError: + pass + self.passwd_r = None + if self.group_r: + try: + os.close(self.group_r) + except OSError: + pass + self.group_r = None + + +class BubblewrapDriver(Driver, WrapperInterface): + name = 'bubblewrap' + log = logging.getLogger("zuul.BubblewrapDriver") + + bwrap_command = [ + 'bwrap', + '--dir', '/tmp', + '--tmpfs', '/tmp', + '--dir', '/var', + '--dir', '/var/tmp', + '--dir', '/run/user/{uid}', + '--ro-bind', '/usr', '/usr', + '--ro-bind', '/lib', '/lib', + '--ro-bind', '/lib64', '/lib64', + '--ro-bind', '/bin', '/bin', + '--ro-bind', '/sbin', '/sbin', + '--ro-bind', '/etc/resolv.conf', '/etc/resolv.conf', + '--ro-bind', '{ansible_dir}', '{ansible_dir}', + '--ro-bind', '{ssh_auth_sock}', '{ssh_auth_sock}', + '--dir', '{work_dir}', + '--bind', '{work_dir}', '{work_dir}', + '--dev', '/dev', + '--dir', '{user_home}', + '--chdir', '/', + '--unshare-all', + '--share-net', + '--uid', '{uid}', + '--gid', '{gid}', + '--file', '{uid_fd}', '/etc/passwd', + '--file', '{gid_fd}', '/etc/group', + ] + + def reconfigure(self, tenant): + pass + + def stop(self): + pass + + def getPopen(self, **kwargs): + # Set zuul_dir if it was not passed in + if 'zuul_dir' in kwargs: + zuul_dir = kwargs['zuul_dir'] + else: + zuul_python_dir = os.path.dirname(sys.executable) + # We want the dir directly above bin to get the whole venv + zuul_dir = os.path.normpath(os.path.join(zuul_python_dir, '..')) + + bwrap_command = list(self.bwrap_command) + if not zuul_dir.startswith('/usr'): + bwrap_command.extend(['--ro-bind', zuul_dir, zuul_dir]) + + # Need users and groups + uid = os.getuid() + passwd = pwd.getpwuid(uid) + passwd_bytes = b':'.join( + ['{}'.format(x).encode('utf8') for x in passwd]) + (passwd_r, passwd_w) = os.pipe() + os.write(passwd_w, passwd_bytes) + os.close(passwd_w) + + gid = os.getgid() + group = grp.getgrgid(gid) + group_bytes = b':'.join( + ['{}'.format(x).encode('utf8') for x in group]) + group_r, group_w = os.pipe() + os.write(group_w, group_bytes) + os.close(group_w) + + kwargs = dict(kwargs) # Don't update passed in dict + kwargs['uid'] = uid + kwargs['gid'] = gid + kwargs['uid_fd'] = passwd_r + kwargs['gid_fd'] = group_r + kwargs['user_home'] = passwd.pw_dir + command = [x.format(**kwargs) for x in bwrap_command] + + wrapped_popen = WrappedPopen(command, passwd_r, group_r) + + return wrapped_popen + + +def main(args=None): + driver = BubblewrapDriver() + + parser = argparse.ArgumentParser() + parser.add_argument('work_dir') + parser.add_argument('ansible_dir') + parser.add_argument('run_args', nargs='+') + cli_args = parser.parse_args() + + ssh_auth_sock = os.environ.get('SSH_AUTH_SOCK') + + popen = driver.getPopen(work_dir=cli_args.work_dir, + ansible_dir=cli_args.ansible_dir, + ssh_auth_sock=ssh_auth_sock) + x = popen(cli_args.run_args) + x.wait() + + +if __name__ == '__main__': + main() diff --git a/zuul/driver/nullwrap/__init__.py b/zuul/driver/nullwrap/__init__.py new file mode 100644 index 000000000..ebcd1da39 --- /dev/null +++ b/zuul/driver/nullwrap/__init__.py @@ -0,0 +1,28 @@ +# Copyright 2012 Hewlett-Packard Development Company, L.P. +# Copyright 2013 OpenStack Foundation +# Copyright 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import subprocess + +from zuul.driver import (Driver, WrapperInterface) + + +class NullwrapDriver(Driver, WrapperInterface): + name = 'nullwrap' + log = logging.getLogger("zuul.NullwrapDriver") + + def getPopen(self, **kwargs): + return subprocess.Popen |