diff --git a/tests/base.py b/tests/base.py index 8ca571d070..179d9faf5f 100644 --- a/tests/base.py +++ b/tests/base.py @@ -62,6 +62,7 @@ from git.exc import NoSuchPathError import yaml import paramiko from zuul.lib.connections import ConnectionRegistry +from psutil import Popen import tests.fakegithub import zuul.driver.gerrit.gerritsource as gerritsource @@ -3310,6 +3311,18 @@ class PostgresqlSchemaFixture(fixtures.Fixture): cur.execute("drop user %s" % self.name) +class FakeCPUTimes: + def __init__(self): + self.user = 0 + self.system = 0 + self.children_user = 0 + self.children_system = 0 + + +def cpu_times(self): + return FakeCPUTimes() + + class BaseTestCase(testtools.TestCase): log = logging.getLogger("zuul.test") wait_timeout = 90 @@ -3408,6 +3421,10 @@ class BaseTestCase(testtools.TestCase): self.addCleanup(handler.close) self.addCleanup(handler.flush) + if sys.platform == 'darwin': + # Popen.cpu_times() is broken on darwin so patch it with a fake. + Popen.cpu_times = cpu_times + class SymLink(object): def __init__(self, target): diff --git a/tests/unit/test_bubblewrap.py b/tests/unit/test_bubblewrap.py index 8e6dddb776..822cb3ca5f 100644 --- a/tests/unit/test_bubblewrap.py +++ b/tests/unit/test_bubblewrap.py @@ -13,6 +13,7 @@ import fixtures import logging import subprocess +import sys import tempfile import testtools import time @@ -21,6 +22,7 @@ import os from zuul.driver import bubblewrap from zuul.executor.server import SshAgent from tests.base import iterate_timeout +from unittest import skipIf class TestBubblewrap(testtools.TestCase): @@ -53,6 +55,7 @@ class TestBubblewrap(testtools.TestCase): # Make sure the _r's are closed self.assertEqual([], po.fds) + @skipIf(sys.platform == 'darwin', 'Not supported on MacOS') def test_bubblewrap_leak(self): bwrap = bubblewrap.BubblewrapDriver() context = bwrap.getExecutionContext() diff --git a/tools/fake_bwrap.py b/tools/fake_bwrap.py new file mode 100755 index 0000000000..ed8de76053 --- /dev/null +++ b/tools/fake_bwrap.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +# Copyright 2020 BMW Group +# +# 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 os +import subprocess + + +def main(): + pos_args = { + '--dir': 1, + '--tmpfs': 1, + '--ro-bind': 2, + '--bind': 2, + '--chdir': 1, + '--uid': 1, + '--gid': 1, + '--file': 2, + '--proc': 1, + '--dev': 1, + } + bool_args = [ + '--unshare-all', + '--unshare-user', + '--unshare-user-try', + '--unshare-ipc', + '--unshare-pid', + '--unshare-net', + '--unshare-uts', + '--unshare-cgroup', + '--unshare-cgroup-try', + '--share-net', + '--die-with-parent', + ] + parser = argparse.ArgumentParser() + for arg, nargs in pos_args.items(): + parser.add_argument(arg, nargs=nargs, action='append') + for arg in bool_args: + parser.add_argument(arg, action='store_true') + parser.add_argument('args', metavar='args', nargs=argparse.REMAINDER, + help='Command') + + args = parser.parse_args() + + for fd, path in args.file: + fd = int(fd) + if path.startswith('/etc'): + # Ignore write requests to /etc + continue + print('Writing file from %s to %s' % (fd, path)) + count = 0 + with open(path, 'wb') as output: + data = os.read(fd, 32000) + while data: + count += len(data) + output.write(data) + data = os.read(fd, 32000) + print('Wrote file (%s bytes)' % count) + + if args.chdir: + os.chdir(args.chdir[0][0]) + result = subprocess.run(args.args, shell=False, check=False) + exit(result.returncode) + + +if __name__ == '__main__': + main()