]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: verify sysupdate invokes the notification callout directory
authorLennart Poettering <lennart@amutable.com>
Thu, 28 May 2026 09:42:57 +0000 (11:42 +0200)
committerLennart Poettering <lennart@amutable.com>
Wed, 24 Jun 2026 11:06:19 +0000 (13:06 +0200)
Extend TEST-72-SYSUPDATE with a check that, after a successful update,
systemd-sysupdate connects to every socket linked into
/run/systemd/sysupdate/notify/ and invokes
io.systemd.SysUpdate.Notify.OnCompletedUpdate(). A tiny recorder socket is
hooked into that directory; it captures the request and replies with success.
We assert the recorded call carries the expected method, version and resource
list, and that a subsequent no-op update emits no notification.

test/units/TEST-72-SYSUPDATE.sh

index 4ac8b8a52d3623e55a84c555d77512d3e74ee7f4..6cabcab5bb2ba6707dc2c9a1525978a4347c2154 100755 (executable)
@@ -49,6 +49,10 @@ systemctl daemon-reload
 at_exit() {
     set +e
 
+    systemctl stop test-sysupdate-notify-recorder.socket
+    rm -f /run/systemd/system/test-sysupdate-notify-recorder.socket \
+          /run/systemd/system/test-sysupdate-notify-recorder@.service
+
     losetup -n --output NAME --associated "$BACKING_FILE" | while read -r loop_dev; do
         losetup --detach "$loop_dev"
     done
@@ -947,4 +951,85 @@ test ! -f "$CLEANUP/target/alpha-v2.bin"
 
 rm -rf "$CONFIGDIR" "$INSTALLDB" "$CLEANUP"
 
+# Verify the notification callout: after a successful update, sysupdate must connect to every socket in
+# /run/systemd/sysupdate/notify/ and invoke io.systemd.SysUpdate.Notify.OnCompletedUpdate(). We hook a tiny
+# recorder socket into that directory that captures the request and replies with success.
+NOTIFY_LOG="$WORKDIR/notify.log"
+rm -f "$NOTIFY_LOG"
+
+cat >"$WORKDIR/notify-recorder.py" <<EOF
+#!/usr/bin/env python3
+# Minimal Varlink server: read one NUL-terminated request, record it, reply with empty parameters.
+import sys
+buf = b""
+while True:
+    c = sys.stdin.buffer.read(1)
+    if not c or c == b"\x00":
+        break
+    buf += c
+with open("$NOTIFY_LOG", "ab") as f:
+    f.write(buf + b"\n")
+sys.stdout.buffer.write(b'{"parameters":{}}\x00')
+sys.stdout.buffer.flush()
+EOF
+chmod +x "$WORKDIR/notify-recorder.py"
+
+cat >/run/systemd/system/test-sysupdate-notify-recorder.socket <<EOF
+[Socket]
+ListenStream=/run/systemd/sysupdate/notify/io.test.SysUpdateRecorder
+Accept=yes
+EOF
+
+cat >"/run/systemd/system/test-sysupdate-notify-recorder@.service" <<EOF
+[Service]
+ExecStart=$WORKDIR/notify-recorder.py
+StandardInput=socket
+StandardOutput=socket
+EOF
+
+systemctl daemon-reload
+systemctl start test-sysupdate-notify-recorder.socket
+
+rm -rf "$CONFIGDIR" "$WORKDIR/blobs"
+mkdir -p "$CONFIGDIR" "$WORKDIR/blobs"
+echo "hello" >"$WORKDIR/source/notifytest-v1.bin"
+(cd "$WORKDIR/source" && sha256sum notifytest-v1.bin >SHA256SUMS)
+cat >"$CONFIGDIR/01-notifytest.transfer" <<EOF
+[Source]
+Type=url-file
+Path=file://$WORKDIR/source
+MatchPattern=notifytest-@v.bin
+
+[Target]
+Type=regular-file
+Path=$WORKDIR/blobs
+MatchPattern=notifytest-@v.bin
+InstancesMax=1
+EOF
+
+# A real update must trigger exactly one notification carrying the version and the updated resources.
+# The callout is synchronous (sysupdate blocks until the subscriber replied, which happens after the
+# request was recorded), so the log is fully written by the time the update returns.
+"$SYSUPDATE" --verify=no update
+test -s "$NOTIFY_LOG"  # the notification must have been recorded
+notify_line="$(tail -n1 "$NOTIFY_LOG")"
+echo "Recorded notification: $notify_line"
+jq -e '.method == "io.systemd.SysUpdate.Notify.OnCompletedUpdate"' <<<"$notify_line" >/dev/null
+jq -e '.parameters.version == "v1"' <<<"$notify_line" >/dev/null
+jq -e '.parameters.resources | length >= 1' <<<"$notify_line" >/dev/null
+jq -e '.parameters.resources | all(has("transfer"))' <<<"$notify_line" >/dev/null
+
+# A no-op update ("No update needed") must NOT emit a notification.
+rm -f "$NOTIFY_LOG"
+"$SYSUPDATE" --verify=no update
+test ! -s "$NOTIFY_LOG"
+
+systemctl stop test-sysupdate-notify-recorder.socket
+rm -f /run/systemd/system/test-sysupdate-notify-recorder.socket \
+      /run/systemd/system/test-sysupdate-notify-recorder@.service
+systemctl daemon-reload
+rm -rf "$CONFIGDIR" "$WORKDIR/blobs"
+rm -f "$WORKDIR/source/notifytest-v1.bin" "$WORKDIR/source/SHA256SUMS" \
+      "$WORKDIR/notify-recorder.py" "$NOTIFY_LOG"
+
 touch /testok