Merge pull request #32121 from CodethinkLabs/basic-mkosi-integration-tests

Basic mkosi integration tests
This commit is contained in:
Luca Boccassi 2024-04-18 21:02:41 +02:00 committed by GitHub
commit 0c8bda7dc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 226 additions and 2 deletions

View file

@ -2573,6 +2573,18 @@ endif
#####################################################################
if get_option('integration-tests') != false
system_mkosi = custom_target('system_mkosi',
build_always_stale : true,
output : 'system',
console : true,
command : ['mkosi', '-C', meson.project_source_root(), '--image=system', '--format=disk', '--output-dir', meson.project_build_root() / '@OUTPUT@', '--without-tests', '-fi', 'build'],
depends : [executables_by_name['bootctl'], executables_by_name['systemd-measure'], executables_by_name['systemd-repart'], ukify],
)
endif
############################################################
subdir('rules.d')
subdir('test')

View file

@ -498,6 +498,8 @@ option('install-tests', type : 'boolean', value : false,
description : 'install test executables')
option('log-message-verification', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'do fake printf() calls to verify format strings')
option('integration-tests', type : 'boolean', value : false,
description : 'run the integration tests')
option('ok-color', type : 'combo',
choices : ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',

View file

@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Config]
Images=system
@Images=system
MinimumVersion=23~devel
[Output]
@ -21,6 +21,7 @@ BuildSourcesEphemeral=yes
@Incremental=yes
@RuntimeSize=8G
@RuntimeBuildSources=yes
@QemuSmp=2
ToolsTreePackages=virtiofsd
KernelCommandLineExtra=systemd.crash_shell
systemd.log_level=debug,console:info
@ -33,7 +34,7 @@ KernelCommandLineExtra=systemd.crash_shell
rw
# Lower the default device timeout so we get a shell earlier if the root device does
# not appear for some reason.
systemd.default_device_timeout_sec=10
systemd.default_device_timeout_sec=20
# Make sure no LSMs are enabled by default.
apparmor=0
selinux=0

View file

@ -5,6 +5,9 @@
[Content]
Autologin=yes
ExtraTrees=
%D/mkosi.crt:/usr/lib/verity.d/mkosi.crt # sysext verification key
Packages=
acl
bash-completion

View file

@ -0,0 +1,2 @@
[Service]
PassEnvironment=SYSTEMD_UNIT_PATH

View file

@ -28,6 +28,22 @@ To run just one of the cases:
$ sudo make -C test/TEST-01-BASIC clean setup run
To run the meson-based integration test config
enable integration tests and options for required commands with the following:
$ meson configure build -Dintegration-tests=true -Dremote=enabled -Dopenssl=enabled -Dblkid=enabled -Dtpm2=enabled
Once enabled the integration tests can be run with:
$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))"
As usual, specific tests can be run in meson by appending the name of the test
which is usually the name of the directory e.g.
$ sudo meson test -C build/ --suite integration-tests --num-processes "$((nproc / 2))" TEST-01-BASIC
See `meson introspect build --tests` for a list of tests.
Specifying the build directory
==============================

View file

@ -0,0 +1,13 @@
test_params += {
'mkosi_args': test_params['mkosi_args'] + [
'--kernel-command-line-extra=' + ' '.join([
'''
frobnicate!
systemd.setenv=TEST_CMDLINE_NEWLINE=foo
systemd.setenv=TEST_CMDLINE_NEWLINE=bar
''',
]),
],
}

134
test/integration_test_wrapper.py Executable file
View file

@ -0,0 +1,134 @@
#!/usr/bin/python3
# SPDX-License-Identifier: LGPL-2.1-or-later
'''Test wrapper command for driving integration tests.
Note: This is deliberately rough and only intended to drive existing tests
with the expectation that as part of formally defining the API it will be tidy.
'''
import argparse
import logging
import os
from pathlib import Path
import shlex
import subprocess
TEST_EXIT_DROPIN = """\
[Unit]
SuccessAction=exit
FailureAction=exit
"""
EMERGENCY_EXIT_DROPIN = """\
[Unit]
Wants=emergency-exit.service
"""
EMERGENCY_EXIT_SERVICE = """\
[Unit]
DefaultDependencies=no
Conflicts=shutdown.target
Conflicts=rescue.service
Before=shutdown.target
Before=rescue.service
FailureAction=exit
[Service]
ExecStart=false
"""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--test-name', required=True)
parser.add_argument('--mkosi-image-name', required=True)
parser.add_argument('--mkosi-output-path', required=True, type=Path)
parser.add_argument('--test-number', required=True)
parser.add_argument('--no-emergency-exit',
dest='emergency_exit', default=True, action='store_false',
help="Disable emergency exit drop-ins for interactive debugging")
parser.add_argument('mkosi_args', nargs="*")
def main():
logging.basicConfig(level=logging.DEBUG)
args = parser.parse_args()
test_unit_name = f"testsuite-{args.test_number}.service"
# Machine names shouldn't have / since it's used as a file name
# and it must be a valid hostname so 64 chars max
machine_name = args.test_name.replace('/', '_')[:64]
logging.debug(f"test name: {args.test_name}\n"
f"test number: {args.test_number}\n"
f"image: {args.mkosi_image_name}\n"
f"mkosi output path: {args.mkosi_output_path}\n"
f"mkosi args: {args.mkosi_args}\n"
f"emergency exit: {args.emergency_exit}")
journal_file = Path(f"{machine_name}.journal").absolute()
logging.info(f"Capturing journal to {journal_file}")
mkosi_args = [
'mkosi',
'--directory', Path('..').resolve(),
'--output-dir', args.mkosi_output_path.absolute(),
'--machine', machine_name,
'--image', args.mkosi_image_name,
'--format=disk',
'--runtime-build-sources=no',
'--ephemeral',
'--forward-journal', journal_file,
*(
[
'--credential',
f"systemd.extra-unit.emergency-exit.service={shlex.quote(EMERGENCY_EXIT_SERVICE)} "
f"systemd.unit-dropin.emergency.target={shlex.quote(EMERGENCY_EXIT_DROPIN)}",
]
if args.emergency_exit
else []
),
f"--credential=systemd.unit-dropin.{test_unit_name}={shlex.quote(TEST_EXIT_DROPIN)}",
'--append',
'--kernel-command-line-extra',
' '.join([
'systemd.hostname=H',
f"SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-{args.test_number}.units:/usr/lib/systemd/tests/testdata/units:",
'systemd.unit=testsuite.target',
f"systemd.wants={test_unit_name}",
]),
*args.mkosi_args,
]
mkosi_args += ['qemu']
logging.debug(f"Running {shlex.join(os.fspath(a) for a in mkosi_args)}")
try:
subprocess.run(mkosi_args, check=True)
except subprocess.CalledProcessError as e:
if e.returncode not in (0, 77):
suggested_command = [
'journalctl',
'--all',
'--no-hostname',
'-o', 'short-monotonic',
'--file', journal_file,
f"_SYSTEMD_UNIT={test_unit_name}",
'+', f"SYSLOG_IDENTIFIER=testsuite-{args.test_number}.sh",
'+', 'PRIORITY=4',
'+', 'PRIORITY=3',
'+', 'PRIORITY=2',
'+', 'PRIORITY=1',
'+', 'PRIORITY=0',
]
logging.info("Test failed, relevant logs can be viewed with: "
f"{shlex.join(os.fspath(a) for a in suggested_command)}")
exit(e.returncode)
if __name__ == '__main__':
main()

View file

@ -331,3 +331,44 @@ if want_tests != 'false' and conf.get('ENABLE_KERNEL_INSTALL') == 1
depends : deps,
suite : 'kernel-install')
endif
############################################################
if get_option('integration-tests') != false
integration_test_wrapper = find_program('integration_test_wrapper.py')
integration_tests = {
'01': 'TEST-01-BASIC',
'02': 'TEST-02-UNITTESTS',
}
foreach test_number, dirname : integration_tests
test_unit_name = f'testsuite-@test_number@.service'
test_params = {
'test_name' : dirname,
'mkosi_image_name' : 'system',
'mkosi_output_path' : system_mkosi,
'test_number' : test_number,
'mkosi_args' : [],
'depends' : [system_mkosi],
'timeout' : 600,
}
# TODO: This fs.exists call isn't included in rebuild logic
# so if you add a new meson.build in a subdir
# you need to touch another build file to get it to reparse.
if fs.exists(dirname / 'meson.build')
subdir(dirname)
endif
args = ['--test-name', test_params['test_name'],
'--mkosi-image-name', test_params['mkosi_image_name'],
'--mkosi-output-path', test_params['mkosi_output_path'],
'--test-number', test_params['test_number']]
args += ['--'] + test_params['mkosi_args']
test(test_params['test_name'],
integration_test_wrapper,
env: test_env,
args : args,
depends : test_params['depends'],
timeout : test_params['timeout'],
suite : 'integration-tests')
endforeach
endif