debian-forge/test/mod/test_buildroot.py
AaronH88 99c739fd60 test: test buildroot read_with_timeout function
- Added a new stage that is stuck in an infinite loop
- Added two tests that use this stage and force a timeout
2021-12-03 14:29:36 +00:00

196 lines
5.6 KiB
Python

#
# Test for the build root
#
import pathlib
import os
import select
import subprocess
import sys
import time
from tempfile import TemporaryDirectory
import pytest
from osbuild.buildroot import BuildRoot
from osbuild.monitor import LogMonitor, NullMonitor
from osbuild.pipeline import detect_host_runner
from ..test import TestBase
@pytest.fixture(name="tempdir")
def tempdir_fixture():
with TemporaryDirectory(prefix="lvm2-") as tmp:
yield tmp
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_basic(tempdir):
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
monitor = NullMonitor(sys.stderr.fileno())
with BuildRoot("/", runner, libdir, var) as root:
r = root.run(["/usr/bin/true"], monitor)
assert r.returncode == 0
# Test we can use `.run` multiple times
r = root.run(["/usr/bin/true"], monitor)
assert r.returncode == 0
r = root.run(["/usr/bin/false"], monitor)
assert r.returncode != 0
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_runner_fail(tempdir):
runner = "org.osbuild.nonexistantrunner"
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
logfile = os.path.join(tempdir, "log.txt")
with BuildRoot("/", runner, libdir, var) as root, \
open(logfile, "w") as log:
monitor = LogMonitor(log.fileno())
r = root.run(["/usr/bin/true"], monitor)
assert r.returncode == 1
with open(logfile) as f:
log = f.read()
assert log
assert r.output
assert log == r.output
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_output(tempdir):
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
data = "42. cats are superior to dogs"
monitor = NullMonitor(sys.stderr.fileno())
with BuildRoot("/", runner, libdir, var) as root:
r = root.run(["/usr/bin/echo", data], monitor)
assert r.returncode == 0
assert data in r.output.strip()
@pytest.mark.skipif(not TestBase.have_test_data(), reason="no test-data access")
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_bind_mounts(tempdir):
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
rw_data = pathlib.Path(tempdir, "data")
rw_data.mkdir()
scripts = os.path.join(TestBase.locate_test_data(), "scripts")
monitor = NullMonitor(sys.stderr.fileno())
with BuildRoot("/", runner, libdir, var) as root:
ro_binds = [f"{scripts}:/scripts"]
cmd = ["/scripts/mount_flags.py",
"/scripts",
"ro"]
r = root.run(cmd, monitor, readonly_binds=ro_binds)
assert r.returncode == 0
cmd = ["/scripts/mount_flags.py",
"/rw-data",
"ro"]
binds = [f"{rw_data}:/rw-data"]
r = root.run(cmd, monitor, binds=binds, readonly_binds=ro_binds)
assert r.returncode == 1
@pytest.mark.skipif(not TestBase.have_test_data(), reason="no test-data access")
@pytest.mark.skipif(not os.path.exists("/sys/fs/selinux"), reason="no SELinux")
def test_selinuxfs_ro(tempdir):
# /sys/fs/selinux must never be writable in the container
# because RPM and other tools must not assume the policy
# of the host is the valid policy
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
scripts = os.path.join(TestBase.locate_test_data(), "scripts")
monitor = NullMonitor(sys.stderr.fileno())
with BuildRoot("/", runner, libdir, var) as root:
ro_binds = [f"{scripts}:/scripts"]
cmd = ["/scripts/mount_flags.py",
"/sys/fs/selinux",
"ro"]
r = root.run(cmd, monitor, readonly_binds=ro_binds)
assert r.returncode == 0
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_proc_overrides(tempdir):
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
cmdline = "is-this-the-real-world"
monitor = NullMonitor(sys.stderr.fileno())
with BuildRoot("/", runner, libdir, var) as root:
root.proc.cmdline = cmdline
r = root.run(["cat", "/proc/cmdline"], monitor)
assert r.returncode == 0
assert cmdline in r.output.strip()
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_timeout(tempdir):
runner = detect_host_runner()
libdir = os.path.abspath(os.curdir)
var = pathlib.Path(tempdir, "var")
var.mkdir()
with BuildRoot("/", runner, libdir, var) as root:
READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
poller = select.poll()
proc = subprocess.Popen(['python3', libdir + '/stages/org.osbuild.test.timeout'], stdout = subprocess.PIPE)
start = time.monotonic()
timeout = 4
poller.register(proc.stdout.fileno(), READ_ONLY)
with pytest.raises(TimeoutError):
root.read_with_timeout(proc, poller, start, timeout)
proc = subprocess.Popen(['python3', libdir + '/stages/org.osbuild.test.timeout'], stdout = subprocess.PIPE)
start = time.monotonic()
timeout = 0
poller.register(proc.stdout.fileno(), READ_ONLY)
with pytest.raises(TimeoutError):
root.read_with_timeout(proc, poller, start, timeout)