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 and .

/reviewed-by @jerome
/reviewed-on nexedi/nxdtest!13
parent b42ccfa5
......@@ -34,7 +34,7 @@ from golang import context, sync
import pytest
from nxdtest import main
from nxdtest import main, trun
......@@ -78,21 +78,7 @@ def with_and_without_userns(tmp_path, monkeypatch, request):
reason="this functionality needs user-namespaces to work"))
# @userns_only marks test as requiring user-namespaces to succeed.
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
userns_works = True
userns_works, _ = trun.userns_available()
userns_only = pytest.mark.userns_only
......@@ -36,6 +36,27 @@ from os.path import join, devnull
from golang import func, defer
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}
# check if user namespaces are available
with open(devnull, "w") as null:
xrun(["unshare"] + _unshare_argv + ["true"], stdout=null, stderr=null)
except (OSError, CalledProcessError):
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():
# 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
......@@ -48,17 +69,17 @@ def main():
mypid = str(os.getpid())
_ = os.environ.get("_NXDTEST_TRUN_RESPAWNED", "")
if mypid != _:
uargv = ["-Umc", "--keep-caps"] # NOTE keep this in sync with @userns_only in
# check if user namespaces are available
with open(devnull, "w") as null:
xrun(["unshare"] + uargv + ["true"], stdout=null, stderr=null)
except (OSError, CalledProcessError):
userns_works, details = userns_available()
if not userns_works:
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.")
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")
# either respawned in new namespace, or entered here without respawn with in_userns=n.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment