Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current git HEAD tests fail on Ubuntu 24.04: isolation_rootfs #1412

Open
thresheek opened this issue Sep 6, 2024 · 6 comments
Open

Current git HEAD tests fail on Ubuntu 24.04: isolation_rootfs #1412

thresheek opened this issue Sep 6, 2024 · 6 comments

Comments

@thresheek
Copy link
Member

On Ubuntu 24.04, when built with:

./configure && ./configure python --config=python3-config && ./configure go --go-path= && ./configure php && ./configure ruby && make && make python3 go ruby php go-install

rootfs isolation tests are failing, while other isolation tests seem to pass:

python3 -m pytest test/test_go_isolation.py test/test_php_isolation.py test/test_python_isolation.py test/test_go_isolation.py -k rootfs
error: no such command: `component`

	View all installed commands with `cargo --list`
	Find a package to install `component` with `cargo search cargo-component`
================================================================================================================ test session starts ================================================================================================================
platform linux -- Python 3.12.3, pytest-7.4.4, pluggy-1.4.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /home/thresh/unit/test
configfile: pytest.ini
collected 26 items / 16 deselected / 10 selected

test/test_go_isolation.py::test_go_isolation_rootfs_container FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:02 [warn] 23500#23500 Unit is running unprivileged, then it cannot use arbitrary user and group.
2024/09/06 01:57:02 [info] 23500#23500 unit 1.33.0 started
2024/09/06 01:57:02 [info] 23501#23501 discovery started
2024/09/06 01:57:02 [notice] 23501#23501 module: php 8.3.6 "/home/thresh/unit/build/lib/unit/modules/php.unit.so"
2024/09/06 01:57:02 [notice] 23501#23501 module: python 3.12.3 "/home/thresh/unit/build/lib/unit/modules/python3.unit.so"
2024/09/06 01:57:02 [notice] 23501#23501 module: ruby 3.2.3 "/home/thresh/unit/build/lib/unit/modules/ruby.unit.so"
2024/09/06 01:57:02 [info] 23500#23500 controller started
2024/09/06 01:57:02 [notice] 23500#23500 process 23501 exited with code 0
2024/09/06 01:57:02 [info] 23503#23503 router started
2024/09/06 01:57:19 [notice] 23500#23500 process 25213 exited with code 0
2024/09/06 01:57:19 [alert] 25214#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:19 [warn] 25214#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:19 [notice] 23500#23500 process 25214 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:02 [warn] 23500#23500 Unit is running unprivileged, then it cannot use arbitrary user and group.
2024/09/06 01:57:02 [info] 23500#23500 unit 1.33.0 started
2024/09/06 01:57:02 [info] 23501#23501 discovery started
2024/09/06 01:57:02 [notice] 23501#23501 module: php 8.3.6 "/home/thresh/unit/build/lib/unit/modules/php.unit.so"
2024/09/06 01:57:02 [notice] 23501#23501 module: python 3.12.3 "/home/thresh/unit/build/lib/unit/modules/python3.unit.so"
2024/09/06 01:57:02 [notice] 23501#23501 module: ruby 3.2.3 "/home/thresh/unit/build/lib/unit/modules/ruby.unit.so"
2024/09/06 01:57:02 [info] 23500#23500 controller started
2024/09/06 01:57:02 [notice] 23500#23500 process 23501 exited with code 0
2024/09/06 01:57:02 [info] 23503#23503 router started
2024/09/06 01:57:19 [notice] 23500#23500 process 25213 exited with code 0
2024/09/06 01:57:19 [alert] 25214#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:19 [warn] 25214#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:19 [notice] 23500#23500 process 25214 exited with code 1

test/test_go_isolation.py::test_go_isolation_rootfs_container ERROR
test/test_go_isolation.py::test_go_isolation_rootfs_container_priv SKIPPED (privileged user required)
test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25303 exited with code 0
2024/09/06 01:57:20 [alert] 25304#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25304#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25304 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25303 exited with code 0
2024/09/06 01:57:20 [alert] 25304#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25304#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25304 exited with code 1

test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs ERROR
test/test_php_isolation.py::test_php_isolation_rootfs FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25309 exited with code 0
2024/09/06 01:57:20 [alert] 25310#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25310#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25310 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25309 exited with code 0
2024/09/06 01:57:20 [alert] 25310#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25310#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25310 exited with code 1

test/test_php_isolation.py::test_php_isolation_rootfs ERROR
test/test_php_isolation.py::test_php_isolation_rootfs_extensions FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25315 exited with code 0
2024/09/06 01:57:20 [alert] 25316#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25316#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25316 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25315 exited with code 0
2024/09/06 01:57:20 [alert] 25316#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25316#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25316 exited with code 1

test/test_php_isolation.py::test_php_isolation_rootfs_extensions ERROR
test/test_python_isolation.py::test_python_isolation_rootfs FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25321 exited with code 0
2024/09/06 01:57:20 [alert] 25322#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25322#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25322 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:20 [notice] 23500#23500 process 25321 exited with code 0
2024/09/06 01:57:20 [alert] 25322#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:20 [warn] 25322#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:20 [notice] 23500#23500 process 25322 exited with code 1

test/test_python_isolation.py::test_python_isolation_rootfs ERROR
test/test_python_isolation.py::test_python_isolation_rootfs_no_language_deps SKIPPED (privileged user required)
test/test_go_isolation.py::test_go_isolation_rootfs_container FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:21 [notice] 23500#23500 process 25417 exited with code 0
2024/09/06 01:57:21 [alert] 25418#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:21 [warn] 25418#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:21 [notice] 23500#23500 process 25418 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:21 [notice] 23500#23500 process 25417 exited with code 0
2024/09/06 01:57:21 [alert] 25418#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:21 [warn] 25418#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:21 [notice] 23500#23500 process 25418 exited with code 1

test/test_go_isolation.py::test_go_isolation_rootfs_container ERROR
test/test_go_isolation.py::test_go_isolation_rootfs_container_priv SKIPPED (privileged user required)
test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs FAILEDPath to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:22 [notice] 23500#23500 process 25508 exited with code 0
2024/09/06 01:57:22 [alert] 25509#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:22 [warn] 25509#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:22 [notice] 23500#23500 process 25509 exited with code 1
Path to unit.log:
/tmp/unit-test-r57s6x1k/unit.log

2024/09/06 01:57:22 [notice] 23500#23500 process 25508 exited with code 0
2024/09/06 01:57:22 [alert] 25509#1 mount("none", "/tmp/unit-test-r57s6x1k/proc", "proc", 2097162, "") (13: Permission denied)
2024/09/06 01:57:22 [warn] 25509#1 umount2(/tmp/unit-test-r57s6x1k/proc, MNT_DETACH) (1: Operation not permitted)
2024/09/06 01:57:22 [notice] 23500#23500 process 25509 exited with code 1

test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs ERROR

====================================================================================================================== ERRORS =======================================================================================================================
______________________________________________________________________________________________ ERROR at teardown of test_go_isolation_rootfs_container ______________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_go_isolation_rootfs_container>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:02 [warn] 23500#23500 Unit is running unprivileged, then it cannot use arbitrary user and group.\n20... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:19 [notice] 23500#23500 process 25214 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
___________________________________________________________________________________________ ERROR at teardown of test_go_isolation_rootfs_automount_tmpfs ___________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_go_isolation_rootfs_automount_tmpfs>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:20 [notice] 23500#23500 process 25303 exited with code 0\n2024/09/06 01:57:20 [alert] 25304#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:20 [notice] 23500#23500 process 25304 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
__________________________________________________________________________________________________ ERROR at teardown of test_php_isolation_rootfs ___________________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_php_isolation_rootfs>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:20 [notice] 23500#23500 process 25309 exited with code 0\n2024/09/06 01:57:20 [alert] 25310#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:20 [notice] 23500#23500 process 25310 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
_____________________________________________________________________________________________ ERROR at teardown of test_php_isolation_rootfs_extensions _____________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_php_isolation_rootfs_extensions>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:20 [notice] 23500#23500 process 25315 exited with code 0\n2024/09/06 01:57:20 [alert] 25316#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:20 [notice] 23500#23500 process 25316 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
_________________________________________________________________________________________________ ERROR at teardown of test_python_isolation_rootfs _________________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_python_isolation_rootfs>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:20 [notice] 23500#23500 process 25321 exited with code 0\n2024/09/06 01:57:20 [alert] 25322#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:20 [notice] 23500#23500 process 25322 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
______________________________________________________________________________________________ ERROR at teardown of test_go_isolation_rootfs_container ______________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_go_isolation_rootfs_container>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:21 [notice] 23500#23500 process 25417 exited with code 0\n2024/09/06 01:57:21 [alert] 25418#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:21 [notice] 23500#23500 process 25418 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
___________________________________________________________________________________________ ERROR at teardown of test_go_isolation_rootfs_automount_tmpfs ___________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_go_isolation_rootfs_automount_tmpfs>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 01:57:22 [notice] 23500#23500 process 25508 exited with code 0\n2024/09/06 01:57:22 [alert] 25509#1 mount(... MNT_DETACH) (1: Operation not permitted)\n2024/09/06 01:57:22 [notice] 23500#23500 process 25509 exited with code 1\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
===================================================================================================================== FAILURES ======================================================================================================================
________________________________________________________________________________________________________ test_go_isolation_rootfs_container _________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_go_isolation_rootfs_container(is_su, require, temp_dir):
        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

        isolation = {'rootfs': temp_dir}

        if not is_su:
            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('ns_inspect', isolation=isolation)

>       obj = client.getjson(url='/?file=/go/app')['body']

test/test_go_isolation.py:295:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.go.ApplicationGo object at 0x73e9c403fda0>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:19 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
_____________________________________________________________________________________________________ test_go_isolation_rootfs_automount_tmpfs ______________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_go_isolation_rootfs_automount_tmpfs(is_su, require, temp_dir):
        try:
            open("/proc/self/mountinfo", encoding='utf-8')
        except:
            pytest.skip('The system lacks /proc/self/mountinfo file')

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

        isolation = {'rootfs': temp_dir}

        if not is_su:
            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        isolation['automount'] = {'tmpfs': False}

        client.load('ns_inspect', isolation=isolation)

>       obj = client.getjson(url='/?mounts=true')['body']

test/test_go_isolation.py:354:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.go.ApplicationGo object at 0x73e9c403fda0>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:20 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
_____________________________________________________________________________________________________________ test_php_isolation_rootfs _____________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_php_isolation_rootfs(is_su, require, temp_dir):
        isolation = {'rootfs': temp_dir}

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('phpinfo', isolation=isolation)

        assert 'success' in client.conf(
            '"/app/php/phpinfo"', 'applications/phpinfo/root'
        )
        assert 'success' in client.conf(
            '"/app/php/phpinfo"', 'applications/phpinfo/working_directory'
        )

>       assert client.get()['status'] == 200, 'empty rootfs'
E       AssertionError: empty rootfs
E       assert 503 == 200

test/test_php_isolation.py:40: AssertionError
_______________________________________________________________________________________________________ test_php_isolation_rootfs_extensions ________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_php_isolation_rootfs_extensions(is_su, require, temp_dir):
        isolation = {'rootfs': temp_dir}

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('list-extensions', isolation=isolation)

        assert 'success' in client.conf(
            '"/app/php/list-extensions"', 'applications/list-extensions/root'
        )

        assert 'success' in client.conf(
            {'file': '/php/list-extensions/php.ini'},
            'applications/list-extensions/options',
        )

        assert 'success' in client.conf(
            '"/app/php/list-extensions"',
            'applications/list-extensions/working_directory',
        )

>       extensions = client.getjson()['body']

test/test_php_isolation.py:82:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.php.ApplicationPHP object at 0x73e9c403da90>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:20 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
___________________________________________________________________________________________________________ test_python_isolation_rootfs ____________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_python_isolation_rootfs(is_su, require, temp_dir):
        isolation = {'rootfs': temp_dir}

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('ns_inspect', isolation=isolation)

        assert not (
            client.getjson(url=f'/?path={temp_dir}')['body']['FileExists']
>       ), 'temp_dir does not exists in rootfs'

test/test_python_isolation.py:61:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.python.ApplicationPython object at 0x73e9c3f076e0>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:20 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
________________________________________________________________________________________________________ test_go_isolation_rootfs_container _________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_go_isolation_rootfs_container(is_su, require, temp_dir):
        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

        isolation = {'rootfs': temp_dir}

        if not is_su:
            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('ns_inspect', isolation=isolation)

>       obj = client.getjson(url='/?file=/go/app')['body']

test/test_go_isolation.py:295:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.go.ApplicationGo object at 0x73e9c403fda0>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:21 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
_____________________________________________________________________________________________________ test_go_isolation_rootfs_automount_tmpfs ______________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x73e9c40513a0>, temp_dir = '/tmp/unit-test-r57s6x1k'

    def test_go_isolation_rootfs_automount_tmpfs(is_su, require, temp_dir):
        try:
            open("/proc/self/mountinfo", encoding='utf-8')
        except:
            pytest.skip('The system lacks /proc/self/mountinfo file')

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

        isolation = {'rootfs': temp_dir}

        if not is_su:
            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        isolation['automount'] = {'tmpfs': False}

        client.load('ns_inspect', isolation=isolation)

>       obj = client.getjson(url='/?mounts=true')['body']

test/test_go_isolation.py:354:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/http.py:291: in getjson
    return self.get(json=True, **kwargs)
test/unit/http.py:165: in get
    return self.http('GET', **kwargs)
test/unit/http.py:122: in http
    resp = self._parse_json(resp)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <unit.applications.lang.go.ApplicationGo object at 0x73e9c403fda0>
resp = {'body': '<!DOCTYPE html><title>Error 503</title><p>Error 503.\r\n', 'headers': {'Connection': 'close', 'Content-Length': '54', 'Content-Type': 'text/html', 'Date': 'Fri, 06 Sep 2024 01:57:22 GMT', ...}, 'status': 503}

    def _parse_json(self, resp):
        headers = resp['headers']

        assert 'Content-Type' in headers
>       assert headers['Content-Type'] == 'application/json'
E       AssertionError

test/unit/http.py:284: AssertionError
============================================================================================================== short test summary info ==============================================================================================================
FAILED test/test_go_isolation.py::test_go_isolation_rootfs_container - AssertionError
FAILED test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs - AssertionError
FAILED test/test_php_isolation.py::test_php_isolation_rootfs - AssertionError: empty rootfs
FAILED test/test_php_isolation.py::test_php_isolation_rootfs_extensions - AssertionError
FAILED test/test_python_isolation.py::test_python_isolation_rootfs - AssertionError
FAILED test/test_go_isolation.py::test_go_isolation_rootfs_container - AssertionError
FAILED test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs - AssertionError
ERROR test/test_go_isolation.py::test_go_isolation_rootfs_container - AssertionError: alert(s)
ERROR test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs - AssertionError: alert(s)
ERROR test/test_php_isolation.py::test_php_isolation_rootfs - AssertionError: alert(s)
ERROR test/test_php_isolation.py::test_php_isolation_rootfs_extensions - AssertionError: alert(s)
ERROR test/test_python_isolation.py::test_python_isolation_rootfs - AssertionError: alert(s)
ERROR test/test_go_isolation.py::test_go_isolation_rootfs_container - AssertionError: alert(s)
ERROR test/test_go_isolation.py::test_go_isolation_rootfs_automount_tmpfs - AssertionError: alert(s)
=============================================================================================== 7 failed, 3 skipped, 16 deselected, 7 errors in 8.58s ===============================================================================================
@thresheek thresheek changed the title Current git HEAD tests fail on Ubuntu 24.04 Current git HEAD tests fail on Ubuntu 24.04: isolation_rootfs Sep 6, 2024
@ac000
Copy link
Member

ac000 commented Sep 6, 2024

I see various permission denied errors, I wonder if they need to run as root there... we do run the tests as root in the ci and they all pass on Ubuntu (usually)...

@thresheek
Copy link
Member Author

They seem to succeed running as sudo, yes.

Also, with sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0, and running unprivileged, they also succeed, except for php:

====================================================================================================================== ERRORS =======================================================================================================================
__________________________________________________________________________________________________ ERROR at teardown of test_php_isolation_rootfs ___________________________________________________________________________________________________

request = <SubRequest 'run' for <Function test_php_isolation_rootfs>>

    @pytest.fixture(autouse=True)
    def run(request):
        unit = unit_run()

        option.skip_alerts = [
            r'read signalfd\(4\) failed',
            r'sendmsg.+failed',
            r'recvmsg.+failed',
        ]
        option.skip_sanitizer = False

        _fds_info['main']['skip'] = False
        _fds_info['router']['skip'] = False
        _fds_info['controller']['skip'] = False

        yield

        # stop unit

        error_stop_unit = unit_stop()
        error_stop_processes = stop_processes()

        # prepare log

        with Log.open() as f:
            log = f.read()
            Log.set_pos(f.tell())

        if not option.save_log and option.restart:
            shutil.rmtree(unit['temp_dir'])
            Log.set_pos(0)

        # clean temp_dir before the next test

        if not option.restart:
            _clear_conf(log=log)
            _clear_temp_dir()

        # check descriptors

        _check_fds(log=log)

        # check processes id's and amount

        _check_processes()

        # print unit.log in case of error

        if hasattr(request.node, 'rep_call') and request.node.rep_call.failed:
            Log.print_log(log)

        if error_stop_unit or error_stop_processes:
            Log.print_log(log)

        # check unit.log for errors

        assert error_stop_unit is None, 'stop unit'
        assert error_stop_processes is None, 'stop processes'

>       Log.check_alerts(log=log)

test/conftest.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/unit/log.py:17: in inner_function
    raise exception
test/unit/log.py:14: in inner_function
    func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

log = '2024/09/06 02:49:31 [notice] 38126#1 app process 38127 (isolated 2) exited with code 0\n2024/09/06 02:49:31 [notice] ...140 (isolated 2) exited on signal 11 (core dumped)\n2024/09/06 02:49:31 [info] 38142#3 "phpinfo" application started\n'

    @staticmethod
    @print_log_on_assert
    def check_alerts(log=None):
        if log is None:
            log = Log.read()

        found = False
        alerts = re.findall(r'.+\[alert\].+', log)

        if alerts:
            found = True

            if option.detailed:
                print('\nAll alerts/sanitizer errors found in log:')
                _ = [print(alert) for alert in alerts]

        if option.skip_alerts:
            for skip in option.skip_alerts:
                alerts = [al for al in alerts if re.search(skip, al) is None]

>       assert not alerts, 'alert(s)'
E       AssertionError: alert(s)

test/unit/log.py:45: AssertionError
===================================================================================================================== FAILURES ======================================================================================================================
_____________________________________________________________________________________________________________ test_php_isolation_rootfs _____________________________________________________________________________________________________________

is_su = False, require = <function check_prerequisites at 0x7493335793a0>, temp_dir = '/tmp/unit-test-zcn6cl_d'

    def test_php_isolation_rootfs(is_su, require, temp_dir):
        isolation = {'rootfs': temp_dir}

        if not is_su:
            require(
                {
                    'features': {
                        'isolation': [
                            'unprivileged_userns_clone',
                            'user',
                            'mnt',
                            'pid',
                        ]
                    }
                }
            )

            isolation['namespaces'] = {
                'mount': True,
                'credential': True,
                'pid': True,
            }

        client.load('phpinfo', isolation=isolation)

        assert 'success' in client.conf(
            '"/app/php/phpinfo"', 'applications/phpinfo/root'
        )
        assert 'success' in client.conf(
            '"/app/php/phpinfo"', 'applications/phpinfo/working_directory'
        )

>       assert client.get()['status'] == 200, 'empty rootfs'
E       AssertionError: empty rootfs
E       assert 503 == 200

test/test_php_isolation.py:40: AssertionError
============================================================================================================== short test summary info ==============================================================================================================
FAILED test/test_php_isolation.py::test_php_isolation_rootfs - AssertionError: empty rootfs
ERROR test/test_php_isolation.py::test_php_isolation_rootfs - AssertionError: alert(s)
========================================================================================== 1 failed, 6 passed, 3 skipped, 16 deselected, 1 error in 8.86s ===========================================================================================

@thresheek
Copy link
Member Author

thresheek commented Sep 6, 2024

The tests that are currrently run here as CI are not run on Ubuntu 24.04, but on 22.04.

@ac000
Copy link
Member

ac000 commented Sep 6, 2024

They seem to succeed running as sudo, yes.

Also, with sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0, and running unprivileged, they also succeed, except for php:

Yeah, Ubuntu...

I tend to ignore such outlier failures...

@callahad
Copy link
Collaborator

callahad commented Sep 9, 2024

Sounds like this is not a blocker, because it's related to apparmor restricting the use of userns mapping, so you either need to run with sudo or relax the apparmor config.

There is the remaining PHP failure, where it's returning a 503 when we expect a 200, but the log output is truncated so I don't actually see what the error is above. Nevertheless, it succeeds with sudo.

Is the above correct?

@thresheek
Copy link
Member Author

Yep.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants