Commit ef41d960 authored by Kirill Smelkov's avatar Kirill Smelkov

Factor checking whether user-namespaces are available into trun.userns_available()

In the next patch we'll also add detection whether FUSE works inside
user namespaces. Before doing that stop duplicating related code in
between trun.py and nxdtest_test.py .

/reviewed-by @jerome
/reviewed-on nexedi/nxdtest!13
parent b42ccfa5
...@@ -34,7 +34,7 @@ from golang import context, sync ...@@ -34,7 +34,7 @@ from golang import context, sync
import pytest import pytest
from nxdtest import main from nxdtest import main, trun
@pytest.fixture @pytest.fixture
...@@ -78,21 +78,7 @@ def with_and_without_userns(tmp_path, monkeypatch, request): ...@@ -78,21 +78,7 @@ def with_and_without_userns(tmp_path, monkeypatch, request):
reason="this functionality needs user-namespaces to work")) reason="this functionality needs user-namespaces to work"))
# @userns_only marks test as requiring user-namespaces to succeed. # @userns_only marks test as requiring user-namespaces to succeed.
try: userns_works, _ = trun.userns_available()
with open(devnull, 'w') as null:
# since trun uses unshare(1) instead of direct system calls, use all
# those unshare options used by trun to verify that we indeed have
#
# 1) userns support from kernel, and
# 2) recent enough unshare that won't fail due to "unknown option".
#
# change this back to plain `unshare -U` when/if trun is reworked to
# use system calls directly.
subprocess.check_call(['unshare', '-Umc', '--keep-caps', 'true'], stdout=null, stderr=null)
except (OSError, subprocess.CalledProcessError):
userns_works = False
else:
userns_works = True
userns_only = pytest.mark.userns_only userns_only = pytest.mark.userns_only
......
...@@ -36,6 +36,27 @@ from os.path import join, devnull ...@@ -36,6 +36,27 @@ from os.path import join, devnull
from golang import func, defer from golang import func, defer
import prctl import prctl
# userns_available detects if user-namespaces and necessary features are provided by OS kernel.
def userns_available(): # -> (yes|no, {details})
have = {"userns": False}
try:
# check if user namespaces are available
with open(devnull, "w") as null:
xrun(["unshare"] + _unshare_argv + ["true"], stdout=null, stderr=null)
except (OSError, CalledProcessError):
pass
else:
have["userns"] = True
ok = True
for _, haveit in have.items():
if not haveit:
ok = False
return (ok, have)
_unshare_argv = ["-Umc", "--keep-caps"]
def main(): def main():
# Try to respawn ourselves in user-namespace where we can mount things, e.g. new /tmp. # Try to respawn ourselves in user-namespace where we can mount things, e.g. new /tmp.
# Keep current uid/gid the same for better traceability. In other words current user # Keep current uid/gid the same for better traceability. In other words current user
...@@ -48,17 +69,17 @@ def main(): ...@@ -48,17 +69,17 @@ def main():
mypid = str(os.getpid()) mypid = str(os.getpid())
_ = os.environ.get("_NXDTEST_TRUN_RESPAWNED", "") _ = os.environ.get("_NXDTEST_TRUN_RESPAWNED", "")
if mypid != _: if mypid != _:
uargv = ["-Umc", "--keep-caps"] # NOTE keep this in sync with @userns_only in nxdtest_test.py userns_works, details = userns_available()
try: if not userns_works:
# check if user namespaces are available
with open(devnull, "w") as null:
xrun(["unshare"] + uargv + ["true"], stdout=null, stderr=null)
except (OSError, CalledProcessError):
in_userns = False in_userns = False
print("# user namespaces not available. isolation and many checks will be deactivated.") details_str = [] # of +feat, -feat
for feat, haveit in details.items():
details_str.append('%s%s' % ('+' if haveit else '-', feat))
print("# user namespaces not available (%s)." % " ".join(details_str))
print("# isolation and many checks will be deactivated.")
else: else:
os.environ["_NXDTEST_TRUN_RESPAWNED"] = mypid os.environ["_NXDTEST_TRUN_RESPAWNED"] = mypid
os.execvp("unshare", ["unshare"] + uargv + [sys.executable] + sys.argv) os.execvp("unshare", ["unshare"] + _unshare_argv + [sys.executable] + sys.argv)
raise AssertionError("unreachable") raise AssertionError("unreachable")
# either respawned in new namespace, or entered here without respawn with in_userns=n. # either respawned in new namespace, or entered here without respawn with in_userns=n.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment