[Backport release-26.05] switch-to-configuration-ng: handle socket-activated Accept=yes services (#533369)

This commit is contained in:
Ramses 2026-06-19 21:20:21 +00:00 committed by GitHub
commit 9b6c023296
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 1 deletions

View file

@ -43,6 +43,19 @@ let
server.serve_forever()
'';
# Per-connection (Accept=yes) socket-activated service that requires the
# connection socket to be passed via socket activation and fails when started
# without one. It greets the client and stays alive for as long as the
# connection is held open.
acceptSocketTest = pkgs.writeShellScript "accept-socket-test.sh" ''
if [ "''${LISTEN_FDS:-0}" -lt 1 ]; then
echo "Expected exactly one socket, got 0" >&2
exit 4
fi
printf hello >&3
exec ${lib.getExe' pkgs.coreutils "cat"} <&3 >/dev/null
'';
in
{
name = "switch-test";
@ -508,6 +521,26 @@ in
};
};
accept-socket.configuration = {
systemd.sockets.accept-socket = {
wantedBy = [ "sockets.target" ];
listenStreams = [ "/run/accept-test.sock" ];
socketConfig = {
Accept = "yes";
SocketMode = "0777";
};
};
systemd.services."accept-socket@" = {
description = "A per-connection socket-activated service";
serviceConfig.ExecStart = acceptSocketTest;
};
};
accept-socket-service-modified.configuration = {
imports = [ accept-socket.configuration ];
systemd.services."accept-socket@".serviceConfig.X-Test = "test";
};
mount.configuration = {
systemd.mounts = [
{
@ -1587,6 +1620,37 @@ in
if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello":
raise Exception("Socket was not properly activated after the service was restarted")
with subtest("socket-activated services with Accept=yes"):
# Socket-activated services don't get started, just the socket
machine.fail("[ -S /run/accept-test.sock ]")
out = switch_to_specialisation("${machine}", "accept-socket")
assert_contains(out, "the following new units were started: accept-socket.socket\n")
machine.succeed("[ -S /run/accept-test.sock ]")
# Hold a connection open so a per-connection instance keeps running
machine.succeed("socat EXEC:'sleep infinity' UNIX-CONNECT:/run/accept-test.sock >&2 &")
instance = machine.wait_until_succeeds(
"systemctl list-units --no-legend --state=running 'accept-socket@*.service' "
+ "| grep -m1 -o 'accept-socket@[^ ]*\\.service'"
).strip()
# Changing the templated service must stop the running instance and
# restart the socket instead of (re)starting the per-connection
# instance, which cannot be started without a connection socket
out = switch_to_specialisation("${machine}", "accept-socket-service-modified")
assert_contains(out, "stopping the following units:")
assert_contains(out, instance)
assert_contains(out, "accept-socket.socket")
assert_contains(out, "\nstarting the following units: accept-socket.socket\n")
assert_lacks(out, "NOT restarting the following changed units:")
assert_lacks(out, "\nrestarting the following units:")
# The per-connection instance must not be (re)started
starting = out[out.index("\nstarting the following units:") :]
assert instance not in starting, f"instance {instance} should not be (re)started"
# Socket-activation of the unit still works
if machine.succeed("socat - UNIX-CONNECT:/run/accept-test.sock </dev/null") != "hello":
raise Exception("Socket was not properly activated after the service was changed")
with subtest("mounts"):
switch_to_specialisation("${machine}", "mount")
out = machine.succeed("mount | grep 'on /testmount'")

View file

@ -769,7 +769,11 @@ fn handle_modified_unit(
};
if sockets.is_empty() {
sockets.push(format!("{base_name}.socket"));
// For a templated instance (`foo@bar.service`), `base_name`
// includes the trailing `@`; the implicitly-associated socket
// is `foo.socket`, so strip it.
let socket_base = base_name.strip_suffix('@').unwrap_or(base_name);
sockets.push(format!("{socket_base}.socket"));
}
for socket in &sockets {