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>
172 lines
4.7 KiB
Python
Executable file
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
|