debian-forge/test/run/test_mount.py
Tomáš Hozza 56987fdda4 Test: make run/test_mount.py not depend on installed osbuild
Previously, the unit test depended on osbuild modules being installed on
the system. As a result, this made the test not work in CI where we do
not install osbuild when running unit tests. In addition, the stage
executed by the unit test would use different version of osbuild
internals than the version that is being tests, which could result in
issues or not testing the intended code.

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
2023-04-12 11:57:18 +02:00

172 lines
4.7 KiB
Python
Executable file

#!/usr/bin/python3
#
# Runtime Tests for Device Host Services
#
import errno
import json
import os
import subprocess
import sys
import tempfile
from contextlib import contextmanager
import pytest
from osbuild import devices, host, meta, mounts
from ..test import TestBase
@pytest.fixture(name="tmpdir")
def tmpdir_fixture():
with tempfile.TemporaryDirectory(prefix="test-devices-") as tmp:
yield tmp
@contextmanager
def make_arguments(opts):
os.makedirs("/run/osbuild/api")
with open("/run/osbuild/api/arguments", "w", encoding="utf-8") as f:
json.dump(opts, f)
try:
yield
finally:
os.remove("/run/osbuild/api/arguments")
os.rmdir("/run/osbuild/api")
@contextmanager
def make_dev_tmpfs(tmpdir):
dev_path = os.path.join(tmpdir, "dev")
os.makedirs(dev_path)
subprocess.run(["mount", "-t", "tmpfs", "-o", "nosuid", "none", dev_path], check=True)
yield dev_path
subprocess.run(["umount", "--lazy", dev_path], check=True)
def create_image(tmpdir):
# create a file to contain an image
tree = os.path.join(tmpdir, "tree")
os.makedirs(tree)
size = 2 * 1024 * 1024
file = os.path.join(tree, "image.img")
with open(file, "wb") as f:
f.truncate(size)
# Create an FS in the image
mkfsopts = {
"devices": {
"device": {
"path": file
}
},
"options": {
"label": "TEST",
"volid": "7B7795E7",
"fat-size": 32
}
}
with make_arguments(mkfsopts):
env = os.environ.copy()
env["PYTHONPATH"] = os.curdir
subprocess.run(
[os.path.join(os.curdir, "stages", "org.osbuild.mkfs.fat")],
env=env,
check=True,
stdout=sys.stdout,
stderr=sys.stderr)
return tree, size
def mount(mgr, devpath, tree, size, mountpoint, options):
index = meta.Index(os.curdir)
# Device manager to open the loopback device
devmgr = devices.DeviceManager(mgr, devpath, tree)
# get a Device for the loopback
dev = devices.Device(
"loop",
index.get_module_info(
"Device",
"org.osbuild.loopback"
),
None,
{
"filename": "image.img",
"start": 0,
"size": size // 512 # size is in sectors / blocks
}
)
# open the device and get its loopback path
lpath = os.path.join(
devpath,
devmgr.open(dev)["path"]
)
# mount the loopback
mounts.MountManager(
devmgr,
mountpoint
).mount(
mounts.Mount(
lpath,
index.get_module_info("Mount", "org.osbuild.fat"),
dev,
"/",
options
)
)
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_without_options(tmpdir):
tree, size = create_image(tmpdir)
options = {}
with tempfile.TemporaryDirectory(dir=tmpdir) as mountpoint:
with host.ServiceManager() as mgr:
with make_dev_tmpfs(tmpdir) as devpath:
mount(mgr, devpath, tree, size, mountpoint, options)
with open(os.path.join(mountpoint, "test"), "w", encoding="utf-8") as f:
f.write("should work")
os.remove(os.path.join(mountpoint, "test"))
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_all_options(tmpdir):
tree, size = create_image(tmpdir)
options = {
"readonly": True,
"uid": 0,
"gid": 0,
"umask": "077",
"shortname": "winnt"
}
print(options)
with tempfile.TemporaryDirectory(dir=tmpdir) as mountpoint:
with host.ServiceManager() as mgr:
with make_dev_tmpfs(tmpdir) as devpath:
mount(mgr, devpath, tree, size, mountpoint, options)
# Check FS is read only
with pytest.raises(OSError) as err:
with open(os.path.join(mountpoint, "test"), "w", encoding="utf-8") as f:
f.write("should not work")
assert err.value.errno == errno.EROFS
# Check the other options
st = os.lstat(mountpoint)
assert st.st_mode == 0o040700
assert st.st_uid == 0
assert st.st_gid == 0
shortname_tested = False
proc = subprocess.run("mount", capture_output=True, check=True)
for line in proc.stdout.splitlines():
strline = line.decode("utf-8")
if mountpoint in strline:
assert "winnt" in strline
shortname_tested = True
assert shortname_tested