Compare commits

...

6 Commits

Author SHA1 Message Date
Yu Watanabe fb25fb32d9
Merge d3c944ac39 into 710653d3bc 2025-04-17 21:22:12 +02:00
Daan De Meyer 710653d3bc test: Use meson add_test_setup() instead of environment variables
We add a default test setup that excludes the integration-tests suite
so that the integration tests don't run by default. This allows us to
get rid of $SYSTEMD_INTEGRATION_TESTS. Then, we add two extra setups:
'integration' and 'shell'. The 'integration' setup does not exclude the
integration-tests suite, and so can be used to run the integration tests.
The 'shell' setup does the same, but additionally sets $TEST_SHELL=1,
allowing to get rid of $TEST_SHELL in the docs.
2025-04-17 20:31:08 +02:00
Daan De Meyer d9826d303b mkosi: update mkosi commit reference to dbb4020beee2cdf250f93a425794f1cf8b0fe693
* dbb4020bee mkosi: Use tools tree by default in repository config
* a2407a305c dnf: Stop messing around with plugins
* eee382ebc6 Fix mkosi help
* 8d4f9969bb mkosi-obs: simplify generation of signed UEFI auth files
*   364dfc65eb Merge pull request #3661 from septatrix/ssh-runtime
|\
| * ab3b52841c Improve Ssh= documentation
| * 79878d7e6c Add new Ssh=auto and Ssh=runtime options
*   49036322c2 Merge pull request #3682 from DaanDeMeyer/history
|\
| * 96e512fe6e installer: Make sure package manager state is preserved in the image
| * b859a7cf0a Only copy repository metadata from specific subdirs from /var
| * c8bf8e4278 Rename cache_subdirs() to package_subdirs()
* |   54b59c4a2e Merge pull request #3696 from DaanDeMeyer/history-cli
|\ \
| * | 898d89e887 Rework version bumping
| * | cc45fe3bad Only write CLI arguments to history instead of full config
| * | 1def443097 Disallow using --rerun-build-scripts with --force again
| * | 87b03ee264 Rename get_configdir() to finalize_configdir()
| * | 9c1217a217 Get rid of to_json() methods on Args and Config
| |/
* | 124f551e77 mkosi-obs: do not publish roothash
* | fc86100e51 mkosi-obs: append certs from mkosi.uefi.db/ to 'db'
* | 8bee4cb8e2 Make sure sync scripts are executable
|/
* a7e90514fa Simplify tools tree out of date error
* f9956daba7 Fail if --rerun-build-scripts is used and tools is out of date
* d94bf56ae8 mkosi-initrd: add specific configuration for plymouth in Debian
* 8235ddbc5b Take shared lock in copy_ephemeral()
* 19c74d5ba5 Two follow ups for #3678
*   0d6f15e8c3 Merge pull request #3678 from DaanDeMeyer/history
|\
| * 5410c4c7af tests: Require genkey to be run once upfront
| * 86b8c611a1 tests: Drop unused tools field
| * c3d1bd0dde Rework history <=> sandbox integration
* fce4db970f zypper: display debugging output if ARG_DEBUG is set
* 2c052b9d45 Allow PCR signing settings to be overridden in sub-images
* 00c220225b zypper: do not fail if a package configured to be removed is not found
2025-04-17 18:30:17 +01:00
Luca Boccassi 10ed8cda58 Revert "mkosi: temporarily disable panic_on_warn"
The BRTFS issue that caused a spurious WARN has been fixed and
backported to Noble, so we can enable panic_on_warm again.

This reverts commit 930d65ccca.
2025-04-17 17:03:47 +01:00
Yu Watanabe d3c944ac39 TEST-17-UDEV: add test case for queued events serialization/deserialization 2025-04-17 04:24:04 +09:00
Yu Watanabe e38a171cb7 udev: serialize queued events on exit, and deserialize them in the next invocation
To make systemd-udevd not lose received uevents on restart. This may be
important on switching root, even though we typically trigger all devices
after switching root by systemd-udev-trigger.service, but it may be
disabled or modified by admin.
2025-04-17 04:24:00 +09:00
11 changed files with 207 additions and 44 deletions

View File

@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: systemd/mkosi@7e4ec15aee6b98300b2ee14265bc647a716a9f8a
- uses: systemd/mkosi@dbb4020beee2cdf250f93a425794f1cf8b0fe693
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
# immediately, we remove the files in the background. However, we first move them to a different location
@ -90,7 +90,6 @@ jobs:
sudo mkosi sandbox -- \
meson setup \
--buildtype=debugoptimized \
-Dintegration-tests=true \
build
- name: Build image
@ -120,7 +119,8 @@ jobs:
meson test \
-C build \
--no-rebuild \
--suite integration-tests \
--setup=integration \
--suite=integration-tests \
--print-errorlogs \
--no-stdsplit \
--num-processes "$(($(nproc) - 1))" \

View File

@ -120,7 +120,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: systemd/mkosi@7e4ec15aee6b98300b2ee14265bc647a716a9f8a
- uses: systemd/mkosi@dbb4020beee2cdf250f93a425794f1cf8b0fe693
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
# immediately, we remove the files in the background. However, we first move them to a different location
@ -197,7 +197,6 @@ jobs:
sudo mkosi sandbox -- \
meson setup \
--buildtype=debugoptimized \
-Dintegration-tests=true \
-Dbpf-framework=disabled \
build
@ -233,7 +232,8 @@ jobs:
meson test \
-C build \
--no-rebuild \
--suite integration-tests \
--setup=integration \
--suite=integration-tests \
--print-errorlogs \
--no-stdsplit \
--num-processes "$(($(nproc) - 1))" \

View File

@ -13,6 +13,12 @@ project('systemd', 'c',
meson_version : '>= 0.62.0',
)
add_test_setup(
'default',
exclude_suites : ['integration-tests'],
is_default : true,
)
project_major_version = meson.project_version().split('.')[0].split('~')[0]
if meson.project_version().contains('.')
project_minor_version = meson.project_version().split('.')[-1].split('~')[0]
@ -339,7 +345,6 @@ meson_build_sh = find_program('tools/meson-build.sh')
want_tests = get_option('tests')
want_slow_tests = want_tests != 'false' and get_option('slow-tests')
want_fuzz_tests = want_tests != 'false' and get_option('fuzz-tests')
want_integration_tests = want_tests != 'false' and get_option('integration-tests')
install_tests = want_tests != 'false' and get_option('install-tests')
if add_languages('cpp', native : false, required : fuzzer_build)
@ -2661,10 +2666,6 @@ endif
#####################################################################
mkosi = find_program('mkosi', required : false)
if want_integration_tests and not mkosi.found()
error('Could not find mkosi which is required to run the integration tests')
endif
mkosi_depends = public_programs
foreach executable : ['systemd-journal-remote', 'systemd-sbsign', 'systemd-keyutil']

View File

@ -509,7 +509,7 @@ 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,
option('integration-tests', type : 'boolean', value : false, deprecated : true,
description : 'run the integration tests')
option('ok-color', type : 'combo',

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Config]
MinimumVersion=commit:7e4ec15aee6b98300b2ee14265bc647a716a9f8a
MinimumVersion=commit:dbb4020beee2cdf250f93a425794f1cf8b0fe693
Dependencies=
exitrd
initrd
@ -78,8 +78,7 @@ KernelCommandLine=
oops=panic
panic=-1
softlockup_panic=1
# Disabled due to BTRFS issue, waiting for the fix to become available
panic_on_warn=0
panic_on_warn=1
psi=1
mitigations=off

View File

@ -11,6 +11,7 @@
#include "fd-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "io-util.h"
#include "iovec-util.h"
#include "list.h"
#include "mkdir.h"
@ -148,6 +149,7 @@ Manager* manager_free(Manager *manager) {
free(manager->worker_notify_socket_path);
sd_device_monitor_unref(manager->monitor);
udev_ctrl_unref(manager->ctrl);
sd_varlink_server_unref(manager->varlink_server);
@ -863,6 +865,102 @@ static int event_queue_insert(Manager *manager, sd_device *dev) {
return 0;
}
static int manager_serialize_events(Manager *manager) {
int r;
assert(manager);
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *storage = NULL;
r = device_monitor_new_full(&storage, MONITOR_GROUP_NONE, -EBADF);
if (r < 0)
return log_warning_errno(r, "Failed to create new device monitor instance: %m");
union sockaddr_union a;
r = device_monitor_get_address(storage, &a);
if (r < 0)
return log_warning_errno(r, "Failed to get address of device monitor socket: %m");
uint64_t n = 0;
LIST_FOREACH(event, event, manager->events) {
if (event->state != EVENT_QUEUED)
continue;
r = device_monitor_send(storage, &a, event->dev);
if (r < 0)
return log_device_warning_errno(event->dev, r, "Failed to save event to socket storage: %m");
n++;
}
if (n == 0)
return 0;
(void) notify_remove_fd_warn("event-serialization");
r = notify_push_fd(sd_device_monitor_get_fd(storage), "event-serialization");
if (r < 0)
return log_warning_errno(r, "Failed to push event serialization fd to service manager: %m");
log_debug("Serialized %"PRIu64" events.", n);
return 0;
}
static int manager_deserialize_events(Manager *manager, int *fd) {
int r;
assert(manager);
assert(fd);
assert(*fd >= 0);
/* This may take and invalidate passed file descriptor even on failure. */
/* At this stage, we have not receive any events from the kernel, hence should be empty. */
if (manager->events)
return log_warning_errno(SYNTHETIC_ERRNO(EALREADY), "Received multiple event storage socket (%i).", *fd);
r = sd_is_socket(*fd, AF_NETLINK, SOCK_RAW, /* listening = */ -1);
if (r < 0)
return log_warning_errno(r, "Failed to verify type of event storage socket (%i): %m", *fd);
if (r == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Received invalid event storage socket (%i).", *fd);
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *storage = NULL;
r = device_monitor_new_full(&storage, MONITOR_GROUP_NONE, *fd);
if (r < 0)
return log_warning_errno(r, "Failed to initialize event storage: %m");
TAKE_FD(*fd);
r = device_monitor_allow_unicast_sender(storage, storage);
if (r < 0)
return log_warning_errno(r, "Failed to set trusted sender for event storage: %m");
uint64_t n = 0;
for (;;) {
r = fd_wait_for_event(sd_device_monitor_get_fd(storage), POLLIN, 0);
if (r == -EINTR)
continue;
if (r < 0)
return log_warning_errno(r, "Failed to wait for event from event storage: %m");
if (r == 0)
break;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
r = sd_device_monitor_receive(storage, &dev);
if (r < 0) {
log_warning_errno(r, "Failed to receive device from event storage, ignoring: %m");
continue;
}
r = event_queue_insert(manager, dev);
if (r < 0)
log_device_warning_errno(dev, r, "Failed to insert device into event queue, ignoring: %m");
n++;
}
log_debug("Deserialized %"PRIu64" events.", n);
return 0;
}
static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
int r;
@ -1077,6 +1175,8 @@ static int on_post_exit(Manager *manager) {
if (!hashmap_isempty(manager->workers))
return 0; /* There still exist running workers. */
(void) manager_serialize_events(manager);
udev_watch_dump();
return sd_event_exit(manager->event, 0);
}
@ -1181,6 +1281,8 @@ static int manager_listen_fds(Manager *manager) {
r = manager_init_inotify(manager, fd);
else if (streq(names[i], "config-serialization"))
r = manager_deserialize_config(manager, &fd);
else if (streq(names[i], "event-serialization"))
r = manager_deserialize_events(manager, &fd);
else
r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Received unexpected fd (%s), ignoring.", names[i]);
@ -1248,6 +1350,10 @@ int manager_main(Manager *manager) {
_unused_ _cleanup_(notify_on_cleanup) const char *notify_message =
notify_start(NOTIFY_READY, NOTIFY_STOPPING);
/* Remove the socket from fdstore, to make not future invocations get events on the previous
* invocation. Otherwise, the stored events may be processed multiple times. */
(void) notify_remove_fd_warn("event-serialization");
r = sd_event_loop(manager->event);
if (r < 0)
return log_error_errno(r, "Event loop failed: %m");

View File

@ -38,14 +38,14 @@ directory (`OutputDirectory=`) to point to the other directory using `mkosi/mkos
After the image has been built, the integration tests can be run with:
```shell
$ env SYSTEMD_INTEGRATION_TESTS=1 mkosi -f sandbox -- meson test -C build --suite integration-tests --num-processes "$(($(nproc) / 4))"
$ mkosi -f sandbox -- meson test -C build --setup=integration --suite integration-tests --num-processes "$(($(nproc) / 4))"
```
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.
```shell
$ env SYSTEMD_INTEGRATION_TESTS=1 mkosi -f sandbox -- meson test -C build -v TEST-01-BASIC
$ mkosi -f sandbox -- meson test -C build --setup=integration -v TEST-01-BASIC
```
See `mkosi -f sandbox -- meson introspect build --tests` for a list of tests.
@ -55,7 +55,7 @@ To interactively debug a failing integration test, the `--interactive` option
newer:
```shell
$ env SYSTEMD_INTEGRATION_TESTS=1 mkosi -f sandbox -- meson test -C build -i TEST-01-BASIC
$ mkosi -f sandbox -- meson test -C build --setup=integration -i TEST-01-BASIC
```
Due to limitations in meson, the integration tests do not yet depend on the
@ -64,7 +64,7 @@ running the integration tests. To rebuild the image and rerun a test, the
following command can be used:
```shell
$ mkosi -f sandbox -- meson compile -C build mkosi && env SYSTEMD_INTEGRATION_TESTS=1 mkosi -f sandbox -- meson test -C build -v TEST-01-BASIC
$ mkosi -f sandbox -- meson compile -C build mkosi && mkosi -f sandbox -- meson test -C build --setup=integration -v TEST-01-BASIC
```
The integration tests use the same mkosi configuration that's used when you run
@ -78,7 +78,7 @@ To iterate on an integration test, let's first get a shell in the integration te
the following:
```shell
$ mkosi -f sandbox -- meson compile -C build mkosi && env SYSTEMD_INTEGRATION_TESTS=1 TEST_SHELL=1 mkosi -f sandbox -- meson test -C build -i TEST-01-BASIC
$ mkosi -f sandbox -- meson compile -C build mkosi && mkosi -f sandbox -- meson test -C build --setup=shell -i TEST-01-BASIC
```
This will get us a shell in the integration test environment after booting the machine without running the
@ -107,7 +107,7 @@ re-running the test will first install the new packages we just built, make a ne
the test again. You can keep running the loop of `mkosi -R`, `systemctl soft-reboot` and
`systemctl start ...` until the changes to the integration test are working.
If you're debugging a failing integration test (running `meson test --interactive` without `TEST_SHELL`),
If you're debugging a failing integration test (running `meson test --interactive`),
there's no need to run `systemctl start ...`, running `systemctl soft-reboot` on its own is sufficient to
rerun the test.
@ -120,10 +120,6 @@ rerun the test.
`TEST_NO_KVM=1`: Disable qemu KVM auto-detection (may be necessary when you're
trying to run the *vanilla* qemu and have both qemu and qemu-kvm installed)
`TEST_SHELL=1`: Configure the machine to be more *user-friendly* for
interactive debugging (e.g. by setting a usable default terminal, suppressing
the shutdown after the test, etc.).
`TEST_MATCH_SUBTEST=subtest`: If the test makes use of `run_subtests` use this
variable to provide a POSIX extended regex to run only subtests matching the
expression.

View File

@ -361,7 +361,7 @@ def statfs(path: Path) -> str:
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--mkosi', required=True)
parser.add_argument('--mkosi', default=None)
parser.add_argument('--meson-source-dir', required=True, type=Path)
parser.add_argument('--meson-build-dir', required=True, type=Path)
parser.add_argument('--name', required=True)
@ -379,6 +379,12 @@ def main() -> None:
parser.add_argument('mkosi_args', nargs='*')
args = parser.parse_args()
if not args.mkosi:
args.mkosi = shutil.which('mkosi')
if not args.mkosi:
print('Could not find mkosi which is required to run the integration tests', file=sys.stderr)
sys.exit(1)
# The meson source directory can either be the top-level repository directory or the
# test/integration-tests/standalone subdirectory in the repository directory. The mkosi configuration
# will always be a parent directory of one of these directories and at most 4 levels upwards, so don't
@ -395,13 +401,6 @@ def main() -> None:
)
exit(1)
if not bool(int(os.getenv('SYSTEMD_INTEGRATION_TESTS', '0'))):
print(
f'SYSTEMD_INTEGRATION_TESTS=1 not found in environment, skipping {args.name}',
file=sys.stderr,
)
exit(77)
if args.slow and not bool(int(os.getenv('SYSTEMD_SLOW_TESTS', '0'))):
print(
f'SYSTEMD_SLOW_TESTS=1 not found in environment, skipping {args.name}',

View File

@ -1,5 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# We'd give these more descriptive names but only alphanumeric characters are allowed.
add_test_setup('integration')
add_test_setup('shell', env : {'TEST_SHELL' : '1'})
integration_test_wrapper = find_program('integration-test-wrapper.py')
integration_tests = []
integration_test_template = {
@ -129,11 +133,11 @@ foreach integration_test : integration_tests
integration_test_args += ['--skip']
endif
if not mkosi.found()
continue
if mkosi.found()
integration_test_args += ['--mkosi', mkosi.full_path()]
endif
integration_test_args += ['--mkosi', mkosi.full_path(), '--']
integration_test_args += ['--']
if integration_test['cmdline'].length() > 0
integration_test_args += [
@ -151,19 +155,12 @@ foreach integration_test : integration_tests
integration_test_args += integration_test['mkosi-args']
integration_test_env = {}
if want_integration_tests
integration_test_env += {'SYSTEMD_INTEGRATION_TESTS': '1'}
endif
# We don't explicitly depend on the "mkosi" target because that means the image is rebuilt on every
# "ninja -C build". Instead, the mkosi target has to be rebuilt manually before running the
# integration tests with mkosi.
test(
integration_test['name'],
integration_test_wrapper,
env : integration_test_env,
args : integration_test_args,
timeout : integration_test['timeout'],
priority : integration_test['priority'],

View File

@ -16,7 +16,6 @@ project('systemd-testsuite',
fs = import('fs')
mkosi = find_program('mkosi', required : true)
want_integration_tests = true
# meson refuses .. in subdir() so we use a symlink to trick it into accepting it anyway.
subdir('integration-tests')

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
# Test for queued events serialization on stop and deserialization on start
rules="/run/udev/rules.d/99-test-17.serialization.rules"
mkdir -p "${rules%/*}"
cat > "$rules" <<'EOF'
SUBSYSTEM!="mem", GOTO="end"
KERNEL!="null", GOTO="end"
ACTION=="remove", GOTO="end"
IMPORT{db}="INVOCATIONS"
IMPORT{program}="/bin/bash -c 'systemctl show --property=InvocationID systemd-udevd.service'"
ENV{INVOCATIONS}+="%E{ACTION}_%E{SEQNUM}_%E{InvocationID}"
ACTION=="add", RUN+="/bin/bash -c ':> /tmp/marker'", RUN+="/usr/bin/sleep 10"
LABEL="end"
EOF
udevadm control --reload --log-level=debug
udevadm settle --timeout 30
rm -f /tmp/marker
INVOCATION_BEFORE="$(systemctl show --property=InvocationID --value systemd-udevd.service)"
for action in remove add change change change change; do
udevadm trigger --action="$action" /dev/null
done
timeout 10 bash -c 'until test -e /tmp/marker; do sleep .1; done'
systemctl restart systemd-udevd.service
udevadm settle --timeout 30
INVOCATION_AFTER="$(systemctl show --property=InvocationID --value systemd-udevd.service)"
udevadm info --no-pager /dev/null
previous_seqnum=0
expected_action=add
expected_invocation="$INVOCATION_BEFORE"
count=0
while read -r action seqnum invocation; do
test "$seqnum" -gt "$previous_seqnum"
assert_eq "$action" "$expected_action"
assert_eq "$invocation" "$expected_invocation"
previous_seqnum="$seqnum"
expected_action=change
expected_invocation="$INVOCATION_AFTER"
: $((++count))
done < <(udevadm info -q property --property=INVOCATIONS --value /dev/null | sed -e 's/ /\n/g; s/_/ /g')
assert_eq "$count" "5"
rm -f "$rules" /tmp/marker
udevadm control --reload
exit 0