mirror of
https://github.com/systemd/systemd.git
synced 2026-08-10 17:14:31 +00:00
ci: add integration test for new bootctl functionality
This commit is contained in:
@@ -397,4 +397,275 @@ testcase_install_varlink() {
|
||||
bootctl is-installed
|
||||
}
|
||||
|
||||
cleanup_link() {
|
||||
if [[ -n "${LINK_WORKDIR:-}" ]]; then
|
||||
rm -rf "$LINK_WORKDIR"
|
||||
unset LINK_WORKDIR
|
||||
fi
|
||||
restore_esp
|
||||
}
|
||||
|
||||
testcase_bootctl_link() {
|
||||
if ! command -v ukify >/dev/null; then
|
||||
echo "ukify not found, skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
backup_esp
|
||||
LINK_WORKDIR="$(mktemp --directory /tmp/test-bootctl-link.XXXXXXXXXX)"
|
||||
trap cleanup_link RETURN ERR
|
||||
|
||||
# Ensure loader/entries directory is present
|
||||
bootctl install --make-entry-directory=yes
|
||||
|
||||
local ESP
|
||||
ESP="$(bootctl --print-esp-path)"
|
||||
|
||||
# Build a minimal UKI via ukify. The .linux content does not need to be a
|
||||
# real kernel — bootctl link only requires a valid PE with .osrel (and the
|
||||
# systemd-stub SBAT marker that pe_is_uki() checks for).
|
||||
cat >"$LINK_WORKDIR/os-release" <<'EOF'
|
||||
ID=testos
|
||||
NAME="Test OS"
|
||||
PRETTY_NAME="Test OS"
|
||||
EOF
|
||||
echo "fake-kernel" >"$LINK_WORKDIR/vmlinuz"
|
||||
echo "fake-initrd" >"$LINK_WORKDIR/initrd"
|
||||
echo "fake-sysext-data" >"$LINK_WORKDIR/hello.sysext.raw"
|
||||
echo "fake-confext-data" >"$LINK_WORKDIR/hello.confext.raw"
|
||||
echo "fake-credential" >"$LINK_WORKDIR/hello.cred"
|
||||
|
||||
ukify build \
|
||||
--linux "$LINK_WORKDIR/vmlinuz" \
|
||||
--initrd "$LINK_WORKDIR/initrd" \
|
||||
--os-release "@$LINK_WORKDIR/os-release" \
|
||||
--uname "1.2.3-testkernel" \
|
||||
--cmdline "quiet" \
|
||||
--output "$LINK_WORKDIR/testuki.efi"
|
||||
|
||||
# Pin an explicit entry token so the resulting filenames are deterministic
|
||||
local TOKEN="systemdtest"
|
||||
local BOOTCTL=(bootctl "--entry-token=literal:$TOKEN")
|
||||
|
||||
# --- Test 1: basic link/unlink ---
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi"
|
||||
|
||||
# Exactly one entry file should exist, named "${TOKEN}-commit_1.conf"
|
||||
local ENTRY="$ESP/loader/entries/${TOKEN}-commit_1.conf"
|
||||
test -f "$ENTRY"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# Verify the entry file contents
|
||||
grep "^title " "$ENTRY" >/dev/null
|
||||
grep "^uki /${TOKEN}/testuki.efi\$" "$ENTRY" >/dev/null
|
||||
grep "^version 1\$" "$ENTRY" >/dev/null
|
||||
|
||||
# Make sure bootctl list sees it
|
||||
bootctl list --json=short | grep -F "${TOKEN}-commit_1.conf" >/dev/null
|
||||
|
||||
# Remove it again using the ID (entry IDs include the .conf suffix)
|
||||
"${BOOTCTL[@]}" unlink "${TOKEN}-commit_1.conf"
|
||||
test ! -e "$ENTRY"
|
||||
test ! -e "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# --- Test 2: link with --entry-title/--entry-version/--entry-commit/--tries-left ---
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" \
|
||||
--entry-title="My Funky Entry" \
|
||||
--entry-version="9.8.7" \
|
||||
--entry-commit=42 \
|
||||
--tries-left=3
|
||||
|
||||
ENTRY="$ESP/loader/entries/${TOKEN}-commit_42.9.8.7+3.conf"
|
||||
test -f "$ENTRY"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
grep "^title My Funky Entry\$" "$ENTRY" >/dev/null
|
||||
grep "^version 42.9.8.7\$" "$ENTRY" >/dev/null
|
||||
grep "^uki /${TOKEN}/testuki.efi\$" "$ENTRY" >/dev/null
|
||||
|
||||
# Unlink using the ID (the tries counter "+3" is stripped from the canonical ID)
|
||||
"${BOOTCTL[@]}" unlink "${TOKEN}-commit_42.9.8.7.conf"
|
||||
test ! -e "$ENTRY"
|
||||
test ! -e "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# --- Test 3: link with extras (-X and --extra=) ---
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" \
|
||||
--entry-commit=50 \
|
||||
-X "$LINK_WORKDIR/hello.sysext.raw" \
|
||||
--extra="$LINK_WORKDIR/hello.confext.raw" \
|
||||
-X "$LINK_WORKDIR/hello.cred"
|
||||
|
||||
ENTRY="$ESP/loader/entries/${TOKEN}-commit_50.conf"
|
||||
test -f "$ENTRY"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
test -f "$ESP/$TOKEN/hello.sysext.raw"
|
||||
test -f "$ESP/$TOKEN/hello.confext.raw"
|
||||
test -f "$ESP/$TOKEN/hello.cred"
|
||||
|
||||
grep "^extra /${TOKEN}/hello.sysext.raw\$" "$ENTRY" >/dev/null
|
||||
grep "^extra /${TOKEN}/hello.confext.raw\$" "$ENTRY" >/dev/null
|
||||
grep "^extra /${TOKEN}/hello.cred\$" "$ENTRY" >/dev/null
|
||||
|
||||
# Unlink must also clean up the extra resources
|
||||
"${BOOTCTL[@]}" unlink "${TOKEN}-commit_50.conf"
|
||||
test ! -e "$ENTRY"
|
||||
test ! -e "$ESP/$TOKEN/testuki.efi"
|
||||
test ! -e "$ESP/$TOKEN/hello.sysext.raw"
|
||||
test ! -e "$ESP/$TOKEN/hello.confext.raw"
|
||||
test ! -e "$ESP/$TOKEN/hello.cred"
|
||||
|
||||
# --- Test 4: --oldest drops the lowest commit first ---
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=10
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=20
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=30
|
||||
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_10.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_20.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_30.conf"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
"${BOOTCTL[@]}" unlink --oldest=yes
|
||||
test ! -e "$ESP/loader/entries/${TOKEN}-commit_10.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_20.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_30.conf"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
"${BOOTCTL[@]}" unlink --oldest=yes
|
||||
test ! -e "$ESP/loader/entries/${TOKEN}-commit_20.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_30.conf"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# --- Test 5: --dry-run leaves everything in place ---
|
||||
"${BOOTCTL[@]}" --dry-run unlink "${TOKEN}-commit_30.conf"
|
||||
test -f "$ESP/loader/entries/${TOKEN}-commit_30.conf"
|
||||
test -f "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# Actually remove it now
|
||||
"${BOOTCTL[@]}" unlink "${TOKEN}-commit_30.conf"
|
||||
test ! -e "$ESP/loader/entries/${TOKEN}-commit_30.conf"
|
||||
test ! -e "$ESP/$TOKEN/testuki.efi"
|
||||
|
||||
# --- Test 6: invalid combinations are rejected ---
|
||||
# Neither an ID nor --oldest
|
||||
(! "${BOOTCTL[@]}" unlink)
|
||||
# Both an ID and --oldest
|
||||
(! "${BOOTCTL[@]}" unlink --oldest=yes "${TOKEN}-commit_1.conf")
|
||||
|
||||
# --- Test 7: refusing to link when --keep-free cannot be satisfied ---
|
||||
(! "${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=99 --keep-free=1T)
|
||||
test ! -e "$ESP/loader/entries/${TOKEN}-commit_99.conf"
|
||||
|
||||
# --- Test 8: refusing to re-link the same commit number ---
|
||||
"${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=77
|
||||
(! "${BOOTCTL[@]}" link "$LINK_WORKDIR/testuki.efi" --entry-commit=77)
|
||||
"${BOOTCTL[@]}" unlink "${TOKEN}-commit_77.conf"
|
||||
|
||||
# --- Test 9: passing a non-UKI is rejected ---
|
||||
(! "${BOOTCTL[@]}" link "$LINK_WORKDIR/vmlinuz")
|
||||
|
||||
# === Varlink coverage ===
|
||||
#
|
||||
# Exercise io.systemd.BootControl.Link/Unlink by forking bootctl as a
|
||||
# varlink server via 'varlinkctl call <binary>'. Note the Varlink schema
|
||||
# has no way to supply a literal entry token (unlike --entry-token= on
|
||||
# the command line), so the token is chosen by bootctl from
|
||||
# machine-id/os-release — we recover it from the returned id.
|
||||
local BOOTCTL_BIN vreply vid vtoken
|
||||
BOOTCTL_BIN="$(type -p bootctl)"
|
||||
|
||||
# --- Test 10: Link + Unlink via varlink ---
|
||||
vreply="$(varlinkctl call --json=short \
|
||||
--push-fd="$LINK_WORKDIR/testuki.efi" \
|
||||
"$BOOTCTL_BIN" io.systemd.BootControl.Link \
|
||||
'{"kernelFilename":"vluki.efi","kernelFileDescriptor":0}')"
|
||||
vid="$(echo "$vreply" | jq -r '.ids[0]')"
|
||||
test -n "$vid"
|
||||
test "$vid" != "null"
|
||||
vtoken="${vid%%-commit_*}"
|
||||
test -n "$vtoken"
|
||||
|
||||
test -f "$ESP/loader/entries/$vid"
|
||||
test -f "$ESP/$vtoken/vluki.efi"
|
||||
grep "^uki /$vtoken/vluki.efi\$" "$ESP/loader/entries/$vid" >/dev/null
|
||||
|
||||
varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
"{\"id\":\"$vid\"}"
|
||||
test ! -e "$ESP/loader/entries/$vid"
|
||||
test ! -e "$ESP/$vtoken/vluki.efi"
|
||||
|
||||
# --- Test 11: Link with entryTitle/entryVersion/entryCommit/triesLeft + extraFiles via varlink ---
|
||||
vreply="$(varlinkctl call --json=short \
|
||||
--push-fd="$LINK_WORKDIR/testuki.efi" \
|
||||
--push-fd="$LINK_WORKDIR/hello.sysext.raw" \
|
||||
--push-fd="$LINK_WORKDIR/hello.cred" \
|
||||
"$BOOTCTL_BIN" io.systemd.BootControl.Link \
|
||||
'{"kernelFilename":"vluki2.efi","kernelFileDescriptor":0,"entryTitle":"Varlink Title","entryVersion":"2.3.4","entryCommit":111,"triesLeft":2,"extraFiles":[{"filename":"hello.sysext.raw","fileDescriptor":1},{"filename":"hello.cred","fileDescriptor":2}]}')"
|
||||
vid="$(echo "$vreply" | jq -r '.ids[0]')"
|
||||
# The returned id has the tries counter ("+2") stripped
|
||||
assert_eq "$vid" "$vtoken-commit_111.2.3.4.conf"
|
||||
# The on-disk entry filename includes the tries counter
|
||||
local VENTRY="$ESP/loader/entries/$vtoken-commit_111.2.3.4+2.conf"
|
||||
test -f "$VENTRY"
|
||||
test -f "$ESP/$vtoken/vluki2.efi"
|
||||
test -f "$ESP/$vtoken/hello.sysext.raw"
|
||||
test -f "$ESP/$vtoken/hello.cred"
|
||||
|
||||
grep "^title Varlink Title\$" "$VENTRY" >/dev/null
|
||||
grep "^version 111.2.3.4\$" "$VENTRY" >/dev/null
|
||||
grep "^extra /$vtoken/hello.sysext.raw\$" "$VENTRY" >/dev/null
|
||||
grep "^extra /$vtoken/hello.cred\$" "$VENTRY" >/dev/null
|
||||
|
||||
varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
"{\"id\":\"$vid\"}"
|
||||
test ! -e "$VENTRY"
|
||||
test ! -e "$ESP/$vtoken/vluki2.efi"
|
||||
test ! -e "$ESP/$vtoken/hello.sysext.raw"
|
||||
test ! -e "$ESP/$vtoken/hello.cred"
|
||||
|
||||
# --- Test 12: Unlink oldest via varlink ---
|
||||
local c
|
||||
for c in 210 220 230; do
|
||||
varlinkctl call --quiet \
|
||||
--push-fd="$LINK_WORKDIR/testuki.efi" \
|
||||
"$BOOTCTL_BIN" io.systemd.BootControl.Link \
|
||||
"{\"kernelFilename\":\"vluki3.efi\",\"kernelFileDescriptor\":0,\"entryCommit\":$c}"
|
||||
done
|
||||
test -f "$ESP/loader/entries/$vtoken-commit_210.conf"
|
||||
test -f "$ESP/loader/entries/$vtoken-commit_220.conf"
|
||||
test -f "$ESP/loader/entries/$vtoken-commit_230.conf"
|
||||
|
||||
varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
'{"oldest":true}'
|
||||
test ! -e "$ESP/loader/entries/$vtoken-commit_210.conf"
|
||||
test -f "$ESP/loader/entries/$vtoken-commit_220.conf"
|
||||
test -f "$ESP/loader/entries/$vtoken-commit_230.conf"
|
||||
test -f "$ESP/$vtoken/vluki3.efi"
|
||||
|
||||
# Clean up remaining entries
|
||||
varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
"{\"id\":\"$vtoken-commit_220.conf\"}"
|
||||
varlinkctl call --quiet "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
"{\"id\":\"$vtoken-commit_230.conf\"}"
|
||||
test ! -e "$ESP/loader/entries/$vtoken-commit_220.conf"
|
||||
test ! -e "$ESP/loader/entries/$vtoken-commit_230.conf"
|
||||
test ! -e "$ESP/$vtoken/vluki3.efi"
|
||||
|
||||
# --- Test 13: Link with a non-UKI via varlink returns InvalidKernelImage ---
|
||||
varlinkctl call --quiet \
|
||||
--push-fd="$LINK_WORKDIR/vmlinuz" \
|
||||
--graceful=io.systemd.BootControl.InvalidKernelImage \
|
||||
"$BOOTCTL_BIN" io.systemd.BootControl.Link \
|
||||
'{"kernelFilename":"notauki.efi","kernelFileDescriptor":0}'
|
||||
|
||||
# --- Test 14: Unlink with invalid argument combinations is rejected ---
|
||||
# Both id and oldest=true
|
||||
(! varlinkctl call "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
'{"id":"foo.conf","oldest":true}')
|
||||
# Neither id nor oldest
|
||||
(! varlinkctl call "$BOOTCTL_BIN" io.systemd.BootControl.Unlink '{}')
|
||||
# Invalid id characters (e.g. a glob)
|
||||
(! varlinkctl call "$BOOTCTL_BIN" io.systemd.BootControl.Unlink \
|
||||
'{"id":"foo*.conf"}')
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
||||
Reference in New Issue
Block a user