Tests: prerequisites checking reworked.
Prerequisites check moved to the module level to simplify class structure. Discovery and prerequisites checks functions moved to the separate files. Introduced "require" fixture to provide per-test requirements check.
This commit is contained in:
154
test/conftest.py
154
test/conftest.py
@@ -13,14 +13,8 @@ import time
|
||||
from multiprocessing import Process
|
||||
|
||||
import pytest
|
||||
from unit.check.chroot import check_chroot
|
||||
from unit.check.go import check_go
|
||||
from unit.check.isolation import check_isolation
|
||||
from unit.check.njs import check_njs
|
||||
from unit.check.node import check_node
|
||||
from unit.check.regex import check_regex
|
||||
from unit.check.tls import check_openssl
|
||||
from unit.check.unix_abstract import check_unix_abstract
|
||||
from unit.check.discover_available import discover_available
|
||||
from unit.check.check_prerequisites import check_prerequisites
|
||||
from unit.http import TestHTTP
|
||||
from unit.log import Log
|
||||
from unit.log import print_log_on_assert
|
||||
@@ -130,6 +124,9 @@ def pytest_generate_tests(metafunc):
|
||||
type = cls.application_type
|
||||
|
||||
def generate_tests(versions):
|
||||
if not versions:
|
||||
pytest.skip('no available module versions')
|
||||
|
||||
metafunc.fixturenames.append('tmp_ct')
|
||||
metafunc.parametrize('tmp_ct', versions)
|
||||
|
||||
@@ -140,75 +137,43 @@ def pytest_generate_tests(metafunc):
|
||||
|
||||
# take available module from option and generate tests for each version
|
||||
|
||||
for module, prereq_version in cls.prerequisites['modules'].items():
|
||||
if module in option.available['modules']:
|
||||
available_versions = option.available['modules'][module]
|
||||
available_modules = option.available['modules']
|
||||
|
||||
if prereq_version == 'all':
|
||||
for module, version in metafunc.module.prerequisites['modules'].items():
|
||||
if module in available_modules and available_modules[module]:
|
||||
available_versions = available_modules[module]
|
||||
|
||||
if version == 'all':
|
||||
generate_tests(available_versions)
|
||||
|
||||
elif prereq_version == 'any':
|
||||
elif version == 'any':
|
||||
option.generated_tests[
|
||||
metafunc.function.__name__
|
||||
] = f'{type} {available_versions[0]}'
|
||||
elif callable(prereq_version):
|
||||
generate_tests(list(filter(prereq_version, available_versions)))
|
||||
elif callable(version):
|
||||
generate_tests(list(filter(version, available_versions)))
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
f'''
|
||||
Unexpected prerequisite version "{prereq_version}" for module "{module}" in
|
||||
{cls}. 'all', 'any' or callable expected.'''
|
||||
Unexpected prerequisite version "{version}" for module "{module}".
|
||||
'all', 'any' or callable expected.'''
|
||||
)
|
||||
|
||||
|
||||
def pytest_sessionstart():
|
||||
option.available = {'modules': {}, 'features': {}}
|
||||
|
||||
unit = unit_run()
|
||||
output_version = subprocess.check_output(
|
||||
[unit['unitd'], '--version'], stderr=subprocess.STDOUT
|
||||
).decode()
|
||||
|
||||
if not _wait_for_record(r'controller started'):
|
||||
Log.print_log()
|
||||
exit("Unit is writing log too long")
|
||||
discover_available(unit)
|
||||
|
||||
# discover available modules from unit.log
|
||||
|
||||
for module in re.findall(
|
||||
r'module: ([a-zA-Z]+) (.*) ".*"$', Log.read(), re.M
|
||||
):
|
||||
versions = option.available['modules'].setdefault(module[0], [])
|
||||
if module[1] not in versions:
|
||||
versions.append(module[1])
|
||||
|
||||
# discover modules from check
|
||||
|
||||
option.available['modules']['go'] = check_go()
|
||||
option.available['modules']['njs'] = check_njs(output_version)
|
||||
option.available['modules']['node'] = check_node(option.current_dir)
|
||||
option.available['modules']['openssl'] = check_openssl(output_version)
|
||||
option.available['modules']['regex'] = check_regex(output_version)
|
||||
|
||||
# remove None values
|
||||
|
||||
option.available['modules'] = {
|
||||
k: v for k, v in option.available['modules'].items() if v is not None
|
||||
}
|
||||
|
||||
check_chroot()
|
||||
check_isolation()
|
||||
check_unix_abstract()
|
||||
|
||||
_clear_conf(f'{unit["temp_dir"]}/control.unit.sock')
|
||||
_clear_conf()
|
||||
|
||||
unit_stop()
|
||||
|
||||
Log.check_alerts()
|
||||
|
||||
if option.restart:
|
||||
shutil.rmtree(unit_instance['temp_dir'])
|
||||
shutil.rmtree(unit['temp_dir'])
|
||||
else:
|
||||
_clear_temp_dir()
|
||||
|
||||
@@ -225,38 +190,10 @@ def pytest_runtest_makereport(item):
|
||||
setattr(item, f'rep_{rep.when}', rep)
|
||||
|
||||
|
||||
@pytest.fixture(scope='class', autouse=True)
|
||||
def check_prerequisites(request):
|
||||
cls = request.cls
|
||||
missed = []
|
||||
|
||||
# check modules
|
||||
|
||||
if 'modules' in cls.prerequisites:
|
||||
available_modules = list(option.available['modules'].keys())
|
||||
|
||||
for module in cls.prerequisites['modules']:
|
||||
if module in available_modules:
|
||||
continue
|
||||
|
||||
missed.append(module)
|
||||
|
||||
if missed:
|
||||
pytest.skip(f'Unit has no {", ".join(missed)} module(s)')
|
||||
|
||||
# check features
|
||||
|
||||
if 'features' in cls.prerequisites:
|
||||
available_features = list(option.available['features'].keys())
|
||||
|
||||
for feature in cls.prerequisites['features']:
|
||||
if feature in available_features:
|
||||
continue
|
||||
|
||||
missed.append(feature)
|
||||
|
||||
if missed:
|
||||
pytest.skip(f'{", ".join(missed)} feature(s) not supported')
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def check_prerequisites_module(request):
|
||||
if hasattr(request.module, 'prerequisites'):
|
||||
check_prerequisites(request.module.prerequisites)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -283,7 +220,7 @@ def run(request):
|
||||
|
||||
# prepare log
|
||||
|
||||
with Log.open(encoding='utf-8') as f:
|
||||
with Log.open() as f:
|
||||
log = f.read()
|
||||
Log.set_pos(f.tell())
|
||||
|
||||
@@ -294,7 +231,7 @@ def run(request):
|
||||
# clean temp_dir before the next test
|
||||
|
||||
if not option.restart:
|
||||
_clear_conf(f'{unit["temp_dir"]}/control.unit.sock', log=log)
|
||||
_clear_conf(log=log)
|
||||
_clear_temp_dir()
|
||||
|
||||
# check descriptors
|
||||
@@ -384,7 +321,7 @@ def unit_run(state_dir=None):
|
||||
unit_instance['pid'] = f.read().rstrip()
|
||||
|
||||
if state_dir is None:
|
||||
_clear_conf(control_sock)
|
||||
_clear_conf()
|
||||
|
||||
_fds_info['main']['fds'] = _count_fds(unit_instance['pid'])
|
||||
|
||||
@@ -440,7 +377,9 @@ def unit_stop():
|
||||
|
||||
|
||||
@print_log_on_assert
|
||||
def _clear_conf(sock, *, log=None):
|
||||
def _clear_conf(*, log=None):
|
||||
sock = unit_instance['control_sock']
|
||||
|
||||
resp = http.put(
|
||||
url='/config',
|
||||
sock_type='unix',
|
||||
@@ -456,7 +395,10 @@ def _clear_conf(sock, *, log=None):
|
||||
def delete(url):
|
||||
return http.delete(url=url, sock_type='unix', addr=sock)['body']
|
||||
|
||||
if 'openssl' in option.available['modules']:
|
||||
if (
|
||||
'openssl' in option.available['modules']
|
||||
and option.available['modules']['openssl']
|
||||
):
|
||||
try:
|
||||
certs = json.loads(get('/certificates')).keys()
|
||||
|
||||
@@ -466,7 +408,10 @@ def _clear_conf(sock, *, log=None):
|
||||
for cert in certs:
|
||||
assert 'success' in delete(f'/certificates/{cert}'), 'delete cert'
|
||||
|
||||
if 'njs' in option.available['modules']:
|
||||
if (
|
||||
'njs' in option.available['modules']
|
||||
and option.available['modules']['njs']
|
||||
):
|
||||
try:
|
||||
scripts = json.loads(get('/js_modules')).keys()
|
||||
|
||||
@@ -621,19 +566,6 @@ def _count_fds(pid):
|
||||
return 0
|
||||
|
||||
|
||||
def _wait_for_record(pattern, name='unit.log', wait=150, flags=re.M):
|
||||
with Log.open(name) as file:
|
||||
for _ in range(wait):
|
||||
found = re.search(pattern, file.read(), flags)
|
||||
|
||||
if found is not None:
|
||||
break
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
return found
|
||||
|
||||
|
||||
def run_process(target, *args):
|
||||
global _processes
|
||||
|
||||
@@ -696,8 +628,8 @@ def date_to_sec_epoch():
|
||||
|
||||
@pytest.fixture
|
||||
def findall():
|
||||
def _findall(pattern, name='unit.log', flags=re.M):
|
||||
return re.findall(pattern, Log.read(name), flags)
|
||||
def _findall(*args, **kwargs):
|
||||
return Log.findall(*args, **kwargs)
|
||||
|
||||
return _findall
|
||||
|
||||
@@ -712,6 +644,11 @@ def is_unsafe(request):
|
||||
return request.config.getoption("--unsafe")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def require():
|
||||
return check_prerequisites
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def search_in_file():
|
||||
def _search_in_file(pattern, name='unit.log', flags=re.M):
|
||||
@@ -760,4 +697,7 @@ def unit_pid():
|
||||
|
||||
@pytest.fixture
|
||||
def wait_for_record():
|
||||
def _wait_for_record(*args, **kwargs):
|
||||
return Log.wait_for_record(*args, **kwargs)
|
||||
|
||||
return _wait_for_record
|
||||
|
||||
@@ -4,10 +4,10 @@ import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestAccessLog(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def load(self, script):
|
||||
super().load(script)
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ import pytest
|
||||
from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
|
||||
}
|
||||
|
||||
|
||||
class TestASGIApplication(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'python': lambda v: version.parse(v) >= version.parse('3.5')
|
||||
}
|
||||
}
|
||||
load_module = 'asgi'
|
||||
|
||||
def test_asgi_application_variables(self, date_to_sec_epoch, sec_epoch):
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')},
|
||||
'features': {'unix_abstract': True},
|
||||
}
|
||||
|
||||
|
||||
class TestASGIApplicationUnixAbstract(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'python': lambda v: version.parse(v) >= version.parse('3.5')
|
||||
},
|
||||
'features': ['unix_abstract'],
|
||||
}
|
||||
load_module = 'asgi'
|
||||
|
||||
def test_asgi_application_unix_abstract(self):
|
||||
|
||||
@@ -5,13 +5,12 @@ from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
|
||||
}
|
||||
|
||||
|
||||
class TestASGILifespan(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'python': lambda v: version.parse(v) >= version.parse('3.5')
|
||||
}
|
||||
}
|
||||
load_module = 'asgi'
|
||||
|
||||
def setup_cookies(self, prefix):
|
||||
|
||||
@@ -3,13 +3,12 @@ from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
|
||||
}
|
||||
|
||||
|
||||
class TestASGITargets(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'python': lambda v: version.parse(v) >= version.parse('3.5')
|
||||
}
|
||||
}
|
||||
load_module = 'asgi'
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -6,13 +6,12 @@ from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.applications.websockets import TestApplicationWebsocket
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': lambda v: version.parse(v) >= version.parse('3.5')}
|
||||
}
|
||||
|
||||
|
||||
class TestASGIWebsockets(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'python': lambda v: version.parse(v) >= version.parse('3.5')
|
||||
}
|
||||
}
|
||||
load_module = 'asgi'
|
||||
|
||||
ws = TestApplicationWebsocket()
|
||||
|
||||
@@ -2,10 +2,10 @@ import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestClientIP(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self.load('client_ip')
|
||||
|
||||
@@ -3,10 +3,10 @@ import socket
|
||||
import pytest
|
||||
from unit.control import TestControl
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestConfiguration(TestControl):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def try_addr(self, addr):
|
||||
return self.conf(
|
||||
{
|
||||
@@ -420,10 +420,10 @@ class TestConfiguration(TestControl):
|
||||
|
||||
assert 'success' in self.conf(conf)
|
||||
|
||||
def test_unprivileged_user_error(self, is_su, skip_alert):
|
||||
def test_unprivileged_user_error(self, require, skip_alert):
|
||||
require({'privileged_user': False})
|
||||
|
||||
skip_alert(r'cannot set user "root"', r'failed to apply new conf')
|
||||
if is_su:
|
||||
pytest.skip('unprivileged tests')
|
||||
|
||||
assert 'error' in self.conf(
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestForwardedHeader(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self.load('forwarded_header')
|
||||
@@ -190,9 +190,7 @@ class TestForwardedHeader(TestApplicationPython):
|
||||
== '1.1.1.1'
|
||||
), 'xff replace multi 3'
|
||||
assert (
|
||||
self.get_addr(
|
||||
xff='8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1'
|
||||
)
|
||||
self.get_addr(xff='8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1')
|
||||
== '2001:db8:3c4d:15::1a2f:1a2b'
|
||||
), 'xff chain ipv6'
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ import re
|
||||
import pytest
|
||||
from unit.applications.lang.go import TestApplicationGo
|
||||
|
||||
prerequisites = {'modules': {'go': 'all'}}
|
||||
|
||||
|
||||
class TestGoApplication(TestApplicationGo):
|
||||
prerequisites = {'modules': {'go': 'all'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, skip_alert):
|
||||
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
|
||||
|
||||
@@ -7,10 +7,10 @@ from unit.applications.lang.go import TestApplicationGo
|
||||
from unit.option import option
|
||||
from unit.utils import getns
|
||||
|
||||
prerequisites = {'modules': {'go': 'any'}, 'features': {'isolation': True}}
|
||||
|
||||
|
||||
class TestGoIsolation(TestApplicationGo):
|
||||
prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, skip_alert):
|
||||
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
|
||||
@@ -27,9 +27,6 @@ class TestGoIsolation(TestApplicationGo):
|
||||
|
||||
return (nobody_uid, nogroup_gid, nogroup)
|
||||
|
||||
def isolation_key(self, key):
|
||||
return key in option.available['features']['isolation'].keys()
|
||||
|
||||
def test_isolation_values(self):
|
||||
self.load('ns_inspect')
|
||||
|
||||
@@ -39,12 +36,13 @@ class TestGoIsolation(TestApplicationGo):
|
||||
if ns.upper() in obj['NS']:
|
||||
assert obj['NS'][ns.upper()] == ns_value, f'{ns} match'
|
||||
|
||||
def test_isolation_unpriv_user(self, is_su):
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
pytest.skip('unprivileged clone is not available')
|
||||
|
||||
if is_su:
|
||||
pytest.skip('privileged tests, skip this')
|
||||
def test_isolation_unpriv_user(self, require):
|
||||
require(
|
||||
{
|
||||
'privileged_user': False,
|
||||
'features': {'isolation': ['unprivileged_userns_clone']},
|
||||
}
|
||||
)
|
||||
|
||||
self.load('ns_inspect')
|
||||
obj = self.getjson()['body']
|
||||
@@ -101,9 +99,8 @@ class TestGoIsolation(TestApplicationGo):
|
||||
assert obj['UID'] == 0, 'uid match uidmap'
|
||||
assert obj['GID'] == 0, 'gid match gidmap'
|
||||
|
||||
def test_isolation_priv_user(self, is_su):
|
||||
if not is_su:
|
||||
pytest.skip('unprivileged tests, skip this')
|
||||
def test_isolation_priv_user(self, require):
|
||||
require({'privileged_user': True})
|
||||
|
||||
self.load('ns_inspect')
|
||||
|
||||
@@ -176,12 +173,12 @@ class TestGoIsolation(TestApplicationGo):
|
||||
assert obj['UID'] == nobody_uid, 'uid match uidmap user=nobody'
|
||||
assert obj['GID'] == nogroup_gid, 'gid match uidmap user=nobody'
|
||||
|
||||
def test_isolation_mnt(self):
|
||||
if not self.isolation_key('mnt'):
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
pytest.skip('unprivileged clone is not available')
|
||||
def test_isolation_mnt(self, require):
|
||||
require(
|
||||
{
|
||||
'features': {'isolation': ['unprivileged_userns_clone', 'mnt']},
|
||||
}
|
||||
)
|
||||
|
||||
self.load(
|
||||
'ns_inspect',
|
||||
@@ -205,19 +202,21 @@ class TestGoIsolation(TestApplicationGo):
|
||||
assert obj['NS']['MNT'] != getns('mnt'), 'mnt set'
|
||||
assert obj['NS']['USER'] != getns('user'), 'user set'
|
||||
|
||||
def test_isolation_pid(self, is_su):
|
||||
if not self.isolation_key('pid'):
|
||||
pytest.skip('pid namespace is not supported')
|
||||
def test_isolation_pid(self, is_su, require):
|
||||
require({'features': {'isolation': ['pid']}})
|
||||
|
||||
if not is_su:
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
pytest.skip('unprivileged clone is not available')
|
||||
|
||||
if not self.isolation_key('user'):
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if not self.isolation_key('mnt'):
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
require(
|
||||
{
|
||||
'features': {
|
||||
'isolation': [
|
||||
'unprivileged_userns_clone',
|
||||
'user',
|
||||
'mnt',
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
isolation = {'namespaces': {'pid': True}}
|
||||
|
||||
@@ -262,19 +261,20 @@ class TestGoIsolation(TestApplicationGo):
|
||||
== option.available['features']['isolation'][ns]
|
||||
), f'{ns} match'
|
||||
|
||||
def test_go_isolation_rootfs_container(self, is_su, temp_dir):
|
||||
def test_go_isolation_rootfs_container(self, is_su, require, temp_dir):
|
||||
if not is_su:
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
pytest.skip('unprivileged clone is not available')
|
||||
|
||||
if not self.isolation_key('user'):
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if not self.isolation_key('mnt'):
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if not self.isolation_key('pid'):
|
||||
pytest.skip('pid namespace is not supported')
|
||||
require(
|
||||
{
|
||||
'features': {
|
||||
'isolation': [
|
||||
'unprivileged_userns_clone',
|
||||
'user',
|
||||
'mnt',
|
||||
'pid',
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
isolation = {'rootfs': temp_dir}
|
||||
|
||||
@@ -294,12 +294,8 @@ class TestGoIsolation(TestApplicationGo):
|
||||
obj = self.getjson(url='/?file=/bin/sh')['body']
|
||||
assert not obj['FileExists'], 'file should not exists'
|
||||
|
||||
def test_go_isolation_rootfs_container_priv(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
if not self.isolation_key('mnt'):
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
def test_go_isolation_rootfs_container_priv(self, require, temp_dir):
|
||||
require({'privileged_user': True, 'features': {'isolation': ['mnt']}})
|
||||
|
||||
isolation = {
|
||||
'namespaces': {'mount': True},
|
||||
@@ -315,24 +311,27 @@ class TestGoIsolation(TestApplicationGo):
|
||||
obj = self.getjson(url='/?file=/bin/sh')['body']
|
||||
assert not obj['FileExists'], 'file should not exists'
|
||||
|
||||
def test_go_isolation_rootfs_automount_tmpfs(self, is_su, temp_dir):
|
||||
def test_go_isolation_rootfs_automount_tmpfs(
|
||||
self, is_su, require, temp_dir
|
||||
):
|
||||
try:
|
||||
open("/proc/self/mountinfo")
|
||||
except:
|
||||
pytest.skip('The system lacks /proc/self/mountinfo file')
|
||||
|
||||
if not is_su:
|
||||
if not self.isolation_key('unprivileged_userns_clone'):
|
||||
pytest.skip('unprivileged clone is not available')
|
||||
|
||||
if not self.isolation_key('user'):
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if not self.isolation_key('mnt'):
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if not self.isolation_key('pid'):
|
||||
pytest.skip('pid namespace is not supported')
|
||||
require(
|
||||
{
|
||||
'features': {
|
||||
'isolation': [
|
||||
'unprivileged_userns_clone',
|
||||
'user',
|
||||
'mnt',
|
||||
'pid',
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
isolation = {'rootfs': temp_dir}
|
||||
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from unit.applications.lang.go import TestApplicationGo
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'go': 'all'},
|
||||
'features': {'isolation': True},
|
||||
'privileged_user': True,
|
||||
}
|
||||
|
||||
|
||||
class TestGoIsolationRootfs(TestApplicationGo):
|
||||
prerequisites = {'modules': {'go': 'all'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, skip_alert):
|
||||
skip_alert(r'\[unit\] close\(\d+\) failed: Bad file descriptor')
|
||||
|
||||
def test_go_isolation_rootfs_chroot(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
if os.uname().sysname == 'Darwin':
|
||||
pytest.skip('chroot tests not supported on OSX')
|
||||
|
||||
isolation = {
|
||||
'rootfs': temp_dir,
|
||||
}
|
||||
def test_go_isolation_rootfs_chroot(self, temp_dir):
|
||||
isolation = {'rootfs': temp_dir}
|
||||
|
||||
self.load('ns_inspect', isolation=isolation)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestHTTPHeader(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def test_http_header_value_leading_sp(self):
|
||||
self.load('custom_header')
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ from unit.applications.lang.java import TestApplicationJava
|
||||
from unit.option import option
|
||||
from unit.utils import public_dir
|
||||
|
||||
prerequisites = {'modules': {'java': 'all'}}
|
||||
|
||||
|
||||
class TestJavaApplication(TestApplicationJava):
|
||||
prerequisites = {'modules': {'java': 'all'}}
|
||||
|
||||
def test_java_conf_error(self, temp_dir, skip_alert):
|
||||
skip_alert(
|
||||
r'realpath.*failed',
|
||||
|
||||
@@ -5,15 +5,12 @@ import pytest
|
||||
from unit.applications.lang.java import TestApplicationJava
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'java': 'all'}, 'privileged_user': True}
|
||||
|
||||
|
||||
class TestJavaIsolationRootfs(TestApplicationJava):
|
||||
prerequisites = {'modules': {'java': 'all'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('require root')
|
||||
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/jars')
|
||||
os.makedirs(f'{temp_dir}/tmp')
|
||||
os.chmod(f'{temp_dir}/tmp', 0o777)
|
||||
@@ -35,10 +32,7 @@ class TestJavaIsolationRootfs(TestApplicationJava):
|
||||
except subprocess.CalledProcessError:
|
||||
pytest.fail("Can't run mount process.")
|
||||
|
||||
def teardown_method(self, is_su):
|
||||
if not is_su:
|
||||
return
|
||||
|
||||
def teardown_method(self):
|
||||
try:
|
||||
subprocess.run(
|
||||
["umount", "--lazy", f"{option.temp_dir}/jars"],
|
||||
@@ -51,13 +45,8 @@ class TestJavaIsolationRootfs(TestApplicationJava):
|
||||
except subprocess.CalledProcessError:
|
||||
pytest.fail("Can't run umount process.")
|
||||
|
||||
def test_java_isolation_rootfs_chroot_war(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('require root')
|
||||
|
||||
isolation = {
|
||||
'rootfs': temp_dir,
|
||||
}
|
||||
def test_java_isolation_rootfs_chroot_war(self, temp_dir):
|
||||
isolation = {'rootfs': temp_dir}
|
||||
|
||||
self.load('empty_war', isolation=isolation)
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import pytest
|
||||
from unit.applications.lang.java import TestApplicationJava
|
||||
from unit.applications.websockets import TestApplicationWebsocket
|
||||
|
||||
prerequisites = {'modules': {'java': 'any'}}
|
||||
|
||||
|
||||
class TestJavaWebsockets(TestApplicationJava):
|
||||
prerequisites = {'modules': {'java': 'any'}}
|
||||
|
||||
ws = TestApplicationWebsocket()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -5,18 +5,16 @@ from unit.applications.proto import TestApplicationProto
|
||||
from unit.option import option
|
||||
from unit.utils import waitforfiles
|
||||
|
||||
prerequisites = {'modules': {'njs': 'any'}}
|
||||
|
||||
|
||||
class TestNJS(TestApplicationProto):
|
||||
prerequisites = {'modules': {'njs': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
assert 'success' in self.conf(
|
||||
{
|
||||
"listeners": {"*:7080": {"pass": "routes"}},
|
||||
"routes": [
|
||||
{"action": {"share": f"{temp_dir}/assets$uri"}}
|
||||
],
|
||||
"routes": [{"action": {"share": f"{temp_dir}/assets$uri"}}],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'njs': 'any'}}
|
||||
|
||||
|
||||
class TestNJSModules(TestApplicationProto):
|
||||
prerequisites = {'modules': {'njs': 'any'}}
|
||||
|
||||
def njs_script_load(self, module, name=None, expect='success'):
|
||||
if name is None:
|
||||
name = module
|
||||
|
||||
@@ -4,10 +4,10 @@ import pytest
|
||||
from unit.applications.lang.node import TestApplicationNode
|
||||
from unit.utils import waitforfiles
|
||||
|
||||
prerequisites = {'modules': {'node': 'all'}}
|
||||
|
||||
|
||||
class TestNodeApplication(TestApplicationNode):
|
||||
prerequisites = {'modules': {'node': 'all'}}
|
||||
|
||||
def assert_basic_application(self):
|
||||
resp = self.get()
|
||||
assert resp['headers']['Content-Type'] == 'text/plain', 'basic header'
|
||||
|
||||
@@ -2,14 +2,12 @@ from packaging import version
|
||||
from unit.applications.lang.node import TestApplicationNode
|
||||
from unit.applications.websockets import TestApplicationWebsocket
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'node': lambda v: version.parse(v) >= version.parse('14.16.0')}
|
||||
}
|
||||
|
||||
|
||||
class TestNodeESModules(TestApplicationNode):
|
||||
prerequisites = {
|
||||
'modules': {
|
||||
'node': lambda v: version.parse(v) >= version.parse('14.16.0')
|
||||
}
|
||||
}
|
||||
|
||||
es_modules = True
|
||||
ws = TestApplicationWebsocket()
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import pytest
|
||||
from unit.applications.lang.node import TestApplicationNode
|
||||
from unit.applications.websockets import TestApplicationWebsocket
|
||||
|
||||
prerequisites = {'modules': {'node': 'any'}}
|
||||
|
||||
|
||||
class TestNodeWebsockets(TestApplicationNode):
|
||||
prerequisites = {'modules': {'node': 'any'}}
|
||||
|
||||
ws = TestApplicationWebsocket()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -3,10 +3,10 @@ import re
|
||||
import pytest
|
||||
from unit.applications.lang.perl import TestApplicationPerl
|
||||
|
||||
prerequisites = {'modules': {'perl': 'all'}}
|
||||
|
||||
|
||||
class TestPerlApplication(TestApplicationPerl):
|
||||
prerequisites = {'modules': {'perl': 'all'}}
|
||||
|
||||
def test_perl_application(self, date_to_sec_epoch, sec_epoch):
|
||||
self.load('variables')
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import pytest
|
||||
from unit.applications.lang.php import TestApplicationPHP
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'php': 'all'}}
|
||||
|
||||
|
||||
class TestPHPApplication(TestApplicationPHP):
|
||||
prerequisites = {'modules': {'php': 'all'}}
|
||||
|
||||
def before_disable_functions(self):
|
||||
body = self.get()['body']
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from unit.control import TestControl
|
||||
|
||||
prerequisites = {'modules': {'php': 'any'}}
|
||||
|
||||
|
||||
class TestPHPBasic(TestControl):
|
||||
prerequisites = {'modules': {'php': 'any'}}
|
||||
|
||||
conf_app = {
|
||||
"app": {
|
||||
"type": "php",
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import pytest
|
||||
from unit.applications.lang.php import TestApplicationPHP
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'php': 'any'}, 'features': {'isolation': True}}
|
||||
|
||||
|
||||
class TestPHPIsolation(TestApplicationPHP):
|
||||
prerequisites = {'modules': {'php': 'any'}, 'features': ['isolation']}
|
||||
|
||||
def test_php_isolation_rootfs(self, is_su, temp_dir):
|
||||
isolation_features = option.available['features']['isolation'].keys()
|
||||
|
||||
if not is_su:
|
||||
if not 'unprivileged_userns_clone' in isolation_features:
|
||||
pytest.skip('requires unprivileged userns or root')
|
||||
|
||||
if 'user' not in isolation_features:
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if 'mnt' not in isolation_features:
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if 'pid' not in isolation_features:
|
||||
pytest.skip('pid namespace is not supported')
|
||||
|
||||
def test_php_isolation_rootfs(self, 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,
|
||||
@@ -42,25 +38,23 @@ class TestPHPIsolation(TestApplicationPHP):
|
||||
|
||||
assert self.get()['status'] == 200, 'empty rootfs'
|
||||
|
||||
def test_php_isolation_rootfs_extensions(self, is_su, temp_dir):
|
||||
isolation_features = option.available['features']['isolation'].keys()
|
||||
|
||||
if not is_su:
|
||||
if not 'unprivileged_userns_clone' in isolation_features:
|
||||
pytest.skip('requires unprivileged userns or root')
|
||||
|
||||
if 'user' not in isolation_features:
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if 'mnt' not in isolation_features:
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if 'pid' not in isolation_features:
|
||||
pytest.skip('pid namespace is not supported')
|
||||
|
||||
def test_php_isolation_rootfs_extensions(self, 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,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from unit.applications.lang.php import TestApplicationPHP
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'php': 'any'}}
|
||||
|
||||
|
||||
class TestPHPTargets(TestApplicationPHP):
|
||||
prerequisites = {'modules': {'php': 'any'}}
|
||||
|
||||
def test_php_application_targets(self):
|
||||
targets_dir = f"{option.test_dir}/php/targets"
|
||||
assert 'success' in self.conf(
|
||||
|
||||
@@ -8,10 +8,10 @@ from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
from unit.utils import waitforsocket
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestProxy(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
SERVER_PORT = 7999
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -8,10 +8,10 @@ from conftest import run_process
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.utils import waitforsocket
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestProxyChunked(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
SERVER_PORT = 7999
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -10,10 +10,10 @@ import pytest
|
||||
from packaging import version
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'all'}}
|
||||
|
||||
|
||||
class TestPythonApplication(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'all'}}
|
||||
|
||||
def test_python_application_variables(self, date_to_sec_epoch, sec_epoch):
|
||||
self.load('variables')
|
||||
|
||||
@@ -740,9 +740,8 @@ last line: 987654321
|
||||
), 'exception raise close'
|
||||
assert len(findall(r'Traceback')) == 8, 'traceback count 8'
|
||||
|
||||
def test_python_user_group(self, is_su):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
def test_python_user_group(self, require):
|
||||
require({'privileged_user': True})
|
||||
|
||||
nobody_uid = pwd.getpwnam('nobody').pw_uid
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from unit.control import TestControl
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestPythonBasic(TestControl):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
conf_app = {
|
||||
"app": {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestPythonEnvironment(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def test_python_environment_name_null(self):
|
||||
self.load('environment')
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ from unit.utils import findmnt
|
||||
from unit.utils import waitformount
|
||||
from unit.utils import waitforunmount
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}, 'features': {'isolation': True}}
|
||||
|
||||
|
||||
class TestPythonIsolation(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}, 'features': ['isolation']}
|
||||
|
||||
def get_cgroup(self, app_name):
|
||||
output = subprocess.check_output(
|
||||
['ps', 'ax', '-o', 'pid', '-o', 'cmd']
|
||||
@@ -31,25 +31,23 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
with open(cgroup, 'r') as f:
|
||||
return f.read().rstrip()
|
||||
|
||||
def test_python_isolation_rootfs(self, is_su, temp_dir):
|
||||
isolation_features = option.available['features']['isolation'].keys()
|
||||
|
||||
if not is_su:
|
||||
if not 'unprivileged_userns_clone' in isolation_features:
|
||||
pytest.skip('requires unprivileged userns or root')
|
||||
|
||||
if 'user' not in isolation_features:
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if 'mnt' not in isolation_features:
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if 'pid' not in isolation_features:
|
||||
pytest.skip('pid namespace is not supported')
|
||||
|
||||
def test_python_isolation_rootfs(self, 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,
|
||||
@@ -78,9 +76,8 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
|
||||
assert ret['body']['FileExists'], 'application exists in rootfs'
|
||||
|
||||
def test_python_isolation_rootfs_no_language_deps(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
def test_python_isolation_rootfs_no_language_deps(self, require, temp_dir):
|
||||
require({'privileged_user': True})
|
||||
|
||||
isolation = {'rootfs': temp_dir, 'automount': {'language_deps': False}}
|
||||
self.load('empty', isolation=isolation)
|
||||
@@ -103,9 +100,8 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
|
||||
assert waitforunmount(python_path), 'language_deps unmount'
|
||||
|
||||
def test_python_isolation_procfs(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
def test_python_isolation_procfs(self, require, temp_dir):
|
||||
require({'privileged_user': True})
|
||||
|
||||
isolation = {'rootfs': temp_dir, 'automount': {'procfs': False}}
|
||||
|
||||
@@ -123,12 +119,10 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
'FileExists'
|
||||
], '/proc/self'
|
||||
|
||||
def test_python_isolation_cgroup(self, is_su):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
if not 'cgroup' in option.available['features']['isolation']:
|
||||
pytest.skip('cgroup is not supported')
|
||||
def test_python_isolation_cgroup(self, require):
|
||||
require(
|
||||
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
|
||||
)
|
||||
|
||||
def set_cgroup_path(path):
|
||||
isolation = {'cgroup': {'path': path}}
|
||||
@@ -146,12 +140,10 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
|
||||
assert len(cgroup_rel.parts) >= len(cgroup_abs.parts)
|
||||
|
||||
def test_python_isolation_cgroup_two(self, is_su):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
if not 'cgroup' in option.available['features']['isolation']:
|
||||
pytest.skip('cgroup is not supported')
|
||||
def test_python_isolation_cgroup_two(self, require):
|
||||
require(
|
||||
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
|
||||
)
|
||||
|
||||
def set_two_cgroup_path(path, path2):
|
||||
script_path = f'{option.test_dir}/python/empty'
|
||||
@@ -193,12 +185,10 @@ class TestPythonIsolation(TestApplicationPython):
|
||||
set_two_cgroup_path('/scope/python', '/scope2/python')
|
||||
assert self.get_cgroup('one') != self.get_cgroup('two')
|
||||
|
||||
def test_python_isolation_cgroup_invalid(self, is_su):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
if not 'cgroup' in option.available['features']['isolation']:
|
||||
pytest.skip('cgroup is not supported')
|
||||
def test_python_isolation_cgroup_invalid(self, require):
|
||||
require(
|
||||
{'privileged_user': True, 'features': {'isolation': ['cgroup']}}
|
||||
)
|
||||
|
||||
def check_invalid(path):
|
||||
script_path = f'{option.test_dir}/python/empty'
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}, 'privileged_user': True}
|
||||
|
||||
|
||||
class TestPythonIsolation(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def test_python_isolation_chroot(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
isolation = {
|
||||
'rootfs': temp_dir,
|
||||
}
|
||||
def test_python_isolation_chroot(self, temp_dir):
|
||||
isolation = {'rootfs': temp_dir}
|
||||
|
||||
self.load('ns_inspect', isolation=isolation)
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestPythonProcman(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
self.app_name = f'app-{temp_dir.split("/")[-1]}'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'all'}}
|
||||
|
||||
|
||||
class TestPythonTargets(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'all'}}
|
||||
|
||||
def test_python_targets(self):
|
||||
python_dir = f'{option.test_dir}/python'
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestReconfigure(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
assert 'success' in self.conf(
|
||||
|
||||
@@ -5,10 +5,10 @@ import time
|
||||
import pytest
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestReconfigureTLS(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
if 'HAS_TLSv1_2' not in dir(ssl) or not ssl.HAS_TLSv1_2:
|
||||
|
||||
@@ -5,10 +5,10 @@ import time
|
||||
import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestRespawn(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
PATTERN_ROUTER = 'unit: router'
|
||||
PATTERN_CONTROLLER = 'unit: controller'
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestReturn(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self._load_conf(
|
||||
|
||||
@@ -2,12 +2,9 @@ import os
|
||||
|
||||
import pytest
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
from unit.option import option
|
||||
|
||||
|
||||
class TestRewrite(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
assert 'success' in self.conf(
|
||||
@@ -97,9 +94,8 @@ class TestRewrite(TestApplicationProto):
|
||||
)
|
||||
assert self.get(url='/foo?arg=val')['status'] == 200
|
||||
|
||||
def test_rewrite_njs(self):
|
||||
if 'njs' not in option.available['modules'].keys():
|
||||
pytest.skip('NJS is not available')
|
||||
def test_rewrite_njs(self, require):
|
||||
require({'modules': {'njs': 'any'}})
|
||||
|
||||
self.set_rewrite("`/${host}`", "/localhost")
|
||||
assert self.get()['status'] == 200
|
||||
|
||||
@@ -3,10 +3,10 @@ import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestRouting(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
assert 'success' in self.conf(
|
||||
@@ -232,9 +232,8 @@ class TestRouting(TestApplicationPython):
|
||||
assert self.get(url='/aBCaBbc')['status'] == 200
|
||||
assert self.get(url='/ABc')['status'] == 404
|
||||
|
||||
def test_routes_empty_regex(self):
|
||||
if not option.available['modules']['regex']:
|
||||
pytest.skip('requires regex')
|
||||
def test_routes_empty_regex(self, require):
|
||||
require({'modules': {'regex': True}})
|
||||
|
||||
self.route_match({"uri": "~"})
|
||||
assert self.get(url='/')['status'] == 200, 'empty regexp'
|
||||
@@ -244,9 +243,8 @@ class TestRouting(TestApplicationPython):
|
||||
assert self.get(url='/')['status'] == 404, 'empty regexp 2'
|
||||
assert self.get(url='/nothing')['status'] == 404, '/nothing'
|
||||
|
||||
def test_routes_bad_regex(self):
|
||||
if not option.available['modules']['regex']:
|
||||
pytest.skip('requires regex')
|
||||
def test_routes_bad_regex(self, require):
|
||||
require({'modules': {'regex': True}})
|
||||
|
||||
assert 'error' in self.route(
|
||||
{"match": {"uri": "~/bl[ah"}, "action": {"return": 200}}
|
||||
@@ -264,9 +262,8 @@ class TestRouting(TestApplicationPython):
|
||||
if 'error' not in status:
|
||||
assert self.get(url='/nothing_z')['status'] == 500, '/nothing_z'
|
||||
|
||||
def test_routes_match_regex_case_sensitive(self):
|
||||
if not option.available['modules']['regex']:
|
||||
pytest.skip('requires regex')
|
||||
def test_routes_match_regex_case_sensitive(self, require):
|
||||
require({'modules': {'regex': True}})
|
||||
|
||||
self.route_match({"uri": "~/bl[ah]"})
|
||||
|
||||
@@ -275,9 +272,8 @@ class TestRouting(TestApplicationPython):
|
||||
assert self.get(url='/blh')['status'] == 200, '/blh'
|
||||
assert self.get(url='/BLAH')['status'] == 404, '/BLAH'
|
||||
|
||||
def test_routes_match_regex_negative_case_sensitive(self):
|
||||
if not option.available['modules']['regex']:
|
||||
pytest.skip('requires regex')
|
||||
def test_routes_match_regex_negative_case_sensitive(self, require):
|
||||
require({'modules': {'regex': True}})
|
||||
|
||||
self.route_match({"uri": "!~/bl[ah]"})
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestRoutingTLS(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
def test_routes_match_scheme_tls(self):
|
||||
self.certificate()
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import subprocess
|
||||
import pytest
|
||||
from unit.applications.lang.ruby import TestApplicationRuby
|
||||
|
||||
prerequisites = {'modules': {'ruby': 'all'}}
|
||||
|
||||
|
||||
class TestRubyApplication(TestApplicationRuby):
|
||||
prerequisites = {'modules': {'ruby': 'all'}}
|
||||
|
||||
def test_ruby_application(self, date_to_sec_epoch, sec_epoch):
|
||||
self.load('variables')
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ from unit.applications.lang.ruby import TestApplicationRuby
|
||||
from unit.option import option
|
||||
from unit.utils import waitforglob
|
||||
|
||||
prerequisites = {'modules': {'ruby': 'all'}}
|
||||
|
||||
|
||||
class TestRubyHooks(TestApplicationRuby):
|
||||
prerequisites = {'modules': {'ruby': 'all'}}
|
||||
|
||||
def _wait_cookie(self, pattern, count):
|
||||
return waitforglob(
|
||||
f'{option.temp_dir}/ruby/hooks/cookie_{pattern}', count
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import pytest
|
||||
from unit.applications.lang.ruby import TestApplicationRuby
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'ruby': 'any'}, 'features': {'isolation': True}}
|
||||
|
||||
|
||||
class TestRubyIsolation(TestApplicationRuby):
|
||||
prerequisites = {'modules': {'ruby': 'any'}, 'features': ['isolation']}
|
||||
|
||||
def test_ruby_isolation_rootfs(self, is_su, temp_dir):
|
||||
isolation_features = option.available['features']['isolation'].keys()
|
||||
|
||||
if not is_su:
|
||||
if not 'unprivileged_userns_clone' in isolation_features:
|
||||
pytest.skip('requires unprivileged userns or root')
|
||||
|
||||
if 'user' not in isolation_features:
|
||||
pytest.skip('user namespace is not supported')
|
||||
|
||||
if 'mnt' not in isolation_features:
|
||||
pytest.skip('mnt namespace is not supported')
|
||||
|
||||
if 'pid' not in isolation_features:
|
||||
pytest.skip('pid namespace is not supported')
|
||||
|
||||
def test_ruby_isolation_rootfs(self, 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,
|
||||
|
||||
@@ -6,10 +6,10 @@ import time
|
||||
import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestSettings(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def sysctl(self):
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
|
||||
@@ -7,8 +7,6 @@ from unit.utils import waitforfiles
|
||||
|
||||
|
||||
class TestStatic(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir')
|
||||
|
||||
@@ -5,10 +5,10 @@ import pytest
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'features': {'chroot': True}}
|
||||
|
||||
|
||||
class TestStaticChroot(TestApplicationProto):
|
||||
prerequisites = {'features': ['chroot']}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir')
|
||||
@@ -62,9 +62,8 @@ class TestStaticChroot(TestApplicationProto):
|
||||
)
|
||||
assert self.get()['status'] != 200, 'share array bad'
|
||||
|
||||
def test_static_chroot_permission(self, is_su, temp_dir):
|
||||
if is_su:
|
||||
pytest.skip("does't work under root")
|
||||
def test_static_chroot_permission(self, require, temp_dir):
|
||||
require({'privileged_user': False})
|
||||
|
||||
os.chmod(f'{temp_dir}/assets/dir', 0o100)
|
||||
|
||||
@@ -81,9 +80,8 @@ class TestStaticChroot(TestApplicationProto):
|
||||
assert 'success' in self.update_action("", ".$uri")
|
||||
assert self.get(url=self.test_path)['status'] == 200, 'empty relative'
|
||||
|
||||
def test_static_chroot_relative(self, is_su):
|
||||
if is_su:
|
||||
pytest.skip("Does't work under root.")
|
||||
def test_static_chroot_relative(self, require):
|
||||
require({'privileged_user': False})
|
||||
|
||||
assert 'success' in self.update_action('.')
|
||||
assert self.get(url='/dir/file')['status'] == 403, 'relative chroot'
|
||||
|
||||
@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestStaticFallback(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
assets_dir = f'{temp_dir}/assets'
|
||||
|
||||
@@ -5,15 +5,12 @@ from pathlib import Path
|
||||
import pytest
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
|
||||
prerequisites = {'features': {'chroot': True}, 'privileged_user': True}
|
||||
|
||||
|
||||
class TestStaticMount(TestApplicationProto):
|
||||
prerequisites = {'features': ['chroot']}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, is_su, temp_dir):
|
||||
if not is_su:
|
||||
pytest.skip('requires root')
|
||||
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir/mount')
|
||||
os.makedirs(f'{temp_dir}/assets/dir/dir')
|
||||
os.makedirs(f'{temp_dir}/assets/mount')
|
||||
|
||||
@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestStaticShare(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir')
|
||||
|
||||
@@ -4,10 +4,10 @@ from pathlib import Path
|
||||
import pytest
|
||||
from unit.applications.proto import TestApplicationProto
|
||||
|
||||
prerequisites = {'features': {'chroot': True}}
|
||||
|
||||
|
||||
class TestStaticSymlink(TestApplicationProto):
|
||||
prerequisites = {'features': ['chroot']}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir/dir')
|
||||
|
||||
@@ -5,8 +5,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestStaticTypes(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
Path(f'{temp_dir}/assets').mkdir()
|
||||
|
||||
@@ -6,8 +6,6 @@ from unit.applications.proto import TestApplicationProto
|
||||
|
||||
|
||||
class TestStaticVariables(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self, temp_dir):
|
||||
os.makedirs(f'{temp_dir}/assets/dir')
|
||||
|
||||
@@ -4,10 +4,10 @@ from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
from unit.status import Status
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestStatus(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def check_connections(self, accepted, active, idle, closed):
|
||||
assert Status.get('/connections') == {
|
||||
'accepted': accepted,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
from unit.status import Status
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestStatusTLS(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
def test_status_tls_requests(self):
|
||||
self.certificate()
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import pytest
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestTLS(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
|
||||
|
||||
def add_tls(self, application='empty', cert='default', port=7080):
|
||||
assert 'success' in self.conf(
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@ import ssl
|
||||
import pytest
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestTLSConfCommand(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self.certificate()
|
||||
|
||||
@@ -14,10 +14,10 @@ from OpenSSL.SSL import (
|
||||
)
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestTLSSession(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self.certificate()
|
||||
|
||||
@@ -5,10 +5,10 @@ import pytest
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestTLSSNI(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
self._load_conf(
|
||||
|
||||
@@ -11,10 +11,10 @@ from OpenSSL.SSL import (
|
||||
)
|
||||
from unit.applications.tls import TestApplicationTLS
|
||||
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
|
||||
class TestTLSTicket(TestApplicationTLS):
|
||||
prerequisites = {'modules': {'openssl': 'any'}}
|
||||
|
||||
ticket = 'U1oDTh11mMxODuw12gS0EXX1E/PkZG13cJNQ6m5+6BGlfPTjNlIEw7PSVU3X1gTE'
|
||||
ticket2 = '5AV0DSYIYbZWZQB7fCnTHZmMxtotb/aXjam+n2XS79lTvX3Tq9xGqpC8XKNEF2lt'
|
||||
ticket80 = '6Pfil8lv/k8zf8MndPpfXaO5EAV6dhME6zs6CfUyq2yziynQwSywtKQMqHGnJ2HR\
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {
|
||||
'modules': {'python': 'any'},
|
||||
'features': {'unix_abstract': True},
|
||||
}
|
||||
|
||||
|
||||
class TestUnixAbstract(TestApplicationPython):
|
||||
prerequisites = {
|
||||
'modules': {'python': 'any'},
|
||||
'features': ['unix_abstract'],
|
||||
}
|
||||
|
||||
def test_unix_abstract_source(self):
|
||||
addr = '\0sock'
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import pytest
|
||||
from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.option import option
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestUpstreamsRR(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
assert 'success' in self.conf(
|
||||
|
||||
@@ -5,10 +5,10 @@ from unit.applications.lang.python import TestApplicationPython
|
||||
from unit.log import Log
|
||||
from unit.utils import waitforfiles
|
||||
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
|
||||
class TestUSR1(TestApplicationPython):
|
||||
prerequisites = {'modules': {'python': 'any'}}
|
||||
|
||||
def test_usr1_access_log(
|
||||
self, search_in_file, temp_dir, unit_pid, wait_for_record
|
||||
):
|
||||
|
||||
@@ -7,8 +7,6 @@ from unit.option import option
|
||||
|
||||
|
||||
class TestVariables(TestApplicationProto):
|
||||
prerequisites = {}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_method_fixture(self):
|
||||
assert 'success' in self.conf(
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
from unit.control import TestControl
|
||||
from unit.log import Log
|
||||
from unit.option import option
|
||||
|
||||
|
||||
|
||||
63
test/unit/check/check_prerequisites.py
Normal file
63
test/unit/check/check_prerequisites.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import pytest
|
||||
from unit.option import option
|
||||
|
||||
|
||||
def check_prerequisites(prerequisites):
|
||||
if 'privileged_user' in prerequisites:
|
||||
if prerequisites['privileged_user'] and not option.is_privileged:
|
||||
pytest.skip(
|
||||
'privileged user required',
|
||||
allow_module_level=True,
|
||||
)
|
||||
elif not prerequisites['privileged_user'] and option.is_privileged:
|
||||
pytest.skip(
|
||||
'unprivileged user required',
|
||||
allow_module_level=True,
|
||||
)
|
||||
|
||||
missed = []
|
||||
|
||||
# check modules
|
||||
|
||||
if 'modules' in prerequisites:
|
||||
available = option.available['modules']
|
||||
|
||||
for module in prerequisites['modules']:
|
||||
if module in available and available[module]:
|
||||
continue
|
||||
|
||||
missed.append(module)
|
||||
|
||||
if missed:
|
||||
pytest.skip(
|
||||
f'Unit has no {", ".join(missed)} module(s)',
|
||||
allow_module_level=True,
|
||||
)
|
||||
|
||||
# check features
|
||||
|
||||
if 'features' in prerequisites:
|
||||
available = option.available['features']
|
||||
require = prerequisites['features']
|
||||
|
||||
for feature in require:
|
||||
avail_feature = available[feature]
|
||||
|
||||
if feature in available and avail_feature:
|
||||
if isinstance(require[feature], list) and isinstance(
|
||||
avail_feature, dict
|
||||
):
|
||||
avail_keys = avail_feature.keys()
|
||||
|
||||
for key in require[feature]:
|
||||
if key not in avail_keys:
|
||||
missed.append(f'{feature}/{key}')
|
||||
continue
|
||||
|
||||
missed.append(feature)
|
||||
|
||||
if missed:
|
||||
pytest.skip(
|
||||
f'{", ".join(missed)} feature(s) not supported',
|
||||
allow_module_level=True,
|
||||
)
|
||||
@@ -7,26 +7,24 @@ http = TestHTTP()
|
||||
|
||||
|
||||
def check_chroot():
|
||||
available = option.available
|
||||
|
||||
resp = http.put(
|
||||
url='/config',
|
||||
sock_type='unix',
|
||||
addr=f'{option.temp_dir}/control.unit.sock',
|
||||
body=json.dumps(
|
||||
{
|
||||
"listeners": {"*:7080": {"pass": "routes"}},
|
||||
"routes": [
|
||||
{
|
||||
"action": {
|
||||
"share": option.temp_dir,
|
||||
"chroot": option.temp_dir,
|
||||
return (
|
||||
'success'
|
||||
in http.put(
|
||||
url='/config',
|
||||
sock_type='unix',
|
||||
addr=f'{option.temp_dir}/control.unit.sock',
|
||||
body=json.dumps(
|
||||
{
|
||||
"listeners": {"*:7080": {"pass": "routes"}},
|
||||
"routes": [
|
||||
{
|
||||
"action": {
|
||||
"share": option.temp_dir,
|
||||
"chroot": option.temp_dir,
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
),
|
||||
],
|
||||
}
|
||||
),
|
||||
)['body']
|
||||
)
|
||||
|
||||
if 'success' in resp['body']:
|
||||
available['features']['chroot'] = True
|
||||
|
||||
47
test/unit/check/discover_available.py
Normal file
47
test/unit/check/discover_available.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from unit.check.chroot import check_chroot
|
||||
from unit.check.go import check_go
|
||||
from unit.check.isolation import check_isolation
|
||||
from unit.check.njs import check_njs
|
||||
from unit.check.node import check_node
|
||||
from unit.check.regex import check_regex
|
||||
from unit.check.tls import check_openssl
|
||||
from unit.check.unix_abstract import check_unix_abstract
|
||||
from unit.log import Log
|
||||
from unit.option import option
|
||||
|
||||
|
||||
def discover_available(unit):
|
||||
output_version = subprocess.check_output(
|
||||
[unit['unitd'], '--version'], stderr=subprocess.STDOUT
|
||||
).decode()
|
||||
|
||||
# wait for controller start
|
||||
|
||||
if Log.wait_for_record(r'controller started') is None:
|
||||
Log.print_log()
|
||||
sys.exit("controller didn't start")
|
||||
|
||||
# discover modules from log file
|
||||
|
||||
for module in Log.findall(r'module: ([a-zA-Z]+) (.*) ".*"$'):
|
||||
versions = option.available['modules'].setdefault(module[0], [])
|
||||
if module[1] not in versions:
|
||||
versions.append(module[1])
|
||||
|
||||
# discover modules using check
|
||||
|
||||
option.available['modules']['go'] = check_go()
|
||||
option.available['modules']['njs'] = check_njs(output_version)
|
||||
option.available['modules']['node'] = check_node()
|
||||
option.available['modules']['openssl'] = check_openssl(output_version)
|
||||
option.available['modules']['regex'] = check_regex(output_version)
|
||||
|
||||
# Discover features using check. Features should be discovered after
|
||||
# modules since some features can require modules.
|
||||
|
||||
option.available['features']['chroot'] = check_chroot()
|
||||
option.available['features']['isolation'] = check_isolation()
|
||||
option.available['features']['unix_abstract'] = check_unix_abstract()
|
||||
@@ -2,5 +2,4 @@ from unit.applications.lang.go import TestApplicationGo
|
||||
|
||||
|
||||
def check_go():
|
||||
if TestApplicationGo.prepare_env('empty') is not None:
|
||||
return True
|
||||
return TestApplicationGo.prepare_env('empty') is not None
|
||||
|
||||
@@ -127,7 +127,7 @@ def check_isolation():
|
||||
}
|
||||
|
||||
else:
|
||||
return
|
||||
return False
|
||||
|
||||
resp = http.put(
|
||||
url='/config',
|
||||
@@ -137,23 +137,23 @@ def check_isolation():
|
||||
)
|
||||
|
||||
if 'success' not in resp['body']:
|
||||
return
|
||||
return False
|
||||
|
||||
userns = getns('user')
|
||||
if not userns:
|
||||
return
|
||||
return False
|
||||
|
||||
available['features']['isolation'] = {'user': userns}
|
||||
isolation = {'user': userns}
|
||||
|
||||
unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
|
||||
if os.path.exists(unp_clone_path):
|
||||
with open(unp_clone_path, 'r') as f:
|
||||
if str(f.read()).rstrip() == '1':
|
||||
available['features']['isolation'][
|
||||
'unprivileged_userns_clone'
|
||||
] = True
|
||||
isolation['unprivileged_userns_clone'] = True
|
||||
|
||||
for ns in allns:
|
||||
ns_value = getns(ns)
|
||||
if ns_value:
|
||||
available['features']['isolation'][ns] = ns_value
|
||||
isolation[ns] = ns_value
|
||||
|
||||
return isolation
|
||||
|
||||
@@ -2,5 +2,4 @@ import re
|
||||
|
||||
|
||||
def check_njs(output_version):
|
||||
if re.search('--njs', output_version):
|
||||
return True
|
||||
return re.search('--njs', output_version)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from unit.option import option
|
||||
|
||||
def check_node(current_dir):
|
||||
if not os.path.exists(f'{current_dir}/node/node_modules'):
|
||||
return None
|
||||
|
||||
def check_node():
|
||||
if not os.path.exists(f'{option.current_dir}/node/node_modules'):
|
||||
return False
|
||||
|
||||
try:
|
||||
v_bytes = subprocess.check_output(['/usr/bin/env', 'node', '-v'])
|
||||
@@ -12,4 +14,4 @@ def check_node(current_dir):
|
||||
return [str(v_bytes, 'utf-8').lstrip('v').rstrip()]
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
return False
|
||||
|
||||
@@ -2,7 +2,4 @@ import re
|
||||
|
||||
|
||||
def check_regex(output_version):
|
||||
if re.search('--no-regex', output_version):
|
||||
return False
|
||||
|
||||
return True
|
||||
return not re.search('--no-regex', output_version)
|
||||
|
||||
@@ -6,7 +6,6 @@ def check_openssl(output_version):
|
||||
try:
|
||||
subprocess.check_output(['which', 'openssl'])
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
return False
|
||||
|
||||
if re.search('--openssl', output_version):
|
||||
return True
|
||||
return re.search('--openssl', output_version)
|
||||
|
||||
@@ -7,19 +7,19 @@ http = TestHTTP()
|
||||
|
||||
|
||||
def check_unix_abstract():
|
||||
available = option.available
|
||||
|
||||
resp = http.put(
|
||||
url='/config',
|
||||
sock_type='unix',
|
||||
addr=f'{option.temp_dir}/control.unit.sock',
|
||||
body=json.dumps(
|
||||
{
|
||||
"listeners": {"unix:@sock": {"pass": "routes"}},
|
||||
"routes": [],
|
||||
}
|
||||
),
|
||||
return (
|
||||
'success'
|
||||
in http.put(
|
||||
url='/config',
|
||||
sock_type='unix',
|
||||
addr=f'{option.temp_dir}/control.unit.sock',
|
||||
body=json.dumps(
|
||||
{
|
||||
"listeners": {
|
||||
f'unix:@{option.temp_dir}/sock': {"pass": "routes"}
|
||||
},
|
||||
"routes": [],
|
||||
}
|
||||
),
|
||||
)['body']
|
||||
)
|
||||
|
||||
if 'success' in resp['body']:
|
||||
available['features']['unix_abstract'] = True
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
from unit.option import option
|
||||
|
||||
@@ -25,7 +26,7 @@ class Log:
|
||||
@print_log_on_assert
|
||||
def check_alerts(log=None):
|
||||
if log is None:
|
||||
log = Log.read(encoding='utf-8')
|
||||
log = Log.read()
|
||||
|
||||
found = False
|
||||
alerts = re.findall(r'.+\[alert\].+', log)
|
||||
@@ -51,12 +52,16 @@ class Log:
|
||||
if found and option.detailed:
|
||||
print('skipped.')
|
||||
|
||||
@staticmethod
|
||||
def findall(pattern, name=UNIT_LOG, flags=re.M):
|
||||
return re.findall(pattern, Log.read(name), flags)
|
||||
|
||||
@staticmethod
|
||||
def get_path(name=UNIT_LOG):
|
||||
return f'{option.temp_dir}/{name}'
|
||||
|
||||
@staticmethod
|
||||
def open(name=UNIT_LOG, encoding=None):
|
||||
def open(name=UNIT_LOG, encoding='utf-8'):
|
||||
file = open(Log.get_path(name), 'r', encoding=encoding, errors='ignore')
|
||||
file.seek(Log.pos.get(name, 0))
|
||||
|
||||
@@ -71,7 +76,7 @@ class Log:
|
||||
sys.stdout.flush()
|
||||
|
||||
if log is None:
|
||||
log = Log.read(encoding='utf-8')
|
||||
log = Log.read()
|
||||
|
||||
sys.stdout.write(log)
|
||||
|
||||
@@ -93,3 +98,16 @@ class Log:
|
||||
pos = Log.pos.get(UNIT_LOG, 0)
|
||||
Log.pos[UNIT_LOG] = Log.pos.get(name, 0)
|
||||
Log.pos[name] = pos
|
||||
|
||||
@staticmethod
|
||||
def wait_for_record(pattern, name=UNIT_LOG, wait=150, flags=re.M):
|
||||
with Log.open(name) as file:
|
||||
for _ in range(wait):
|
||||
found = re.search(pattern, file.read(), flags)
|
||||
|
||||
if found is not None:
|
||||
break
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
return found
|
||||
|
||||
@@ -4,6 +4,7 @@ import platform
|
||||
class Options:
|
||||
_options = {
|
||||
'architecture': platform.architecture()[0],
|
||||
'available': {'modules': {}, 'features': {}},
|
||||
'is_privileged': os.geteuid() == 0,
|
||||
'skip_alerts': [],
|
||||
'skip_sanitizer': False,
|
||||
|
||||
Reference in New Issue
Block a user