finalize-staged: Ensure /boot automount doesn't expire

If `/boot` is an automount, then the unit will be stopped as soon as the
automount expires. That's would defeat the purpose of using systemd to
delay finalizing the deployment until shutdown. This is not uncommon as
`systemd-gpt-auto-generator` will create an automount unit for `/boot`
when it's the EFI System Partition and there's no fstab entry.

To ensure that systemd doesn't stop the service early when the `/boot`
automount expires, introduce a new unit that holds `/boot` open until
it's sent `SIGTERM`. This uses a new `--hold` option for
`finalize-staged` that loads but doesn't lock the sysroot. A separate
unit is used since we want the process to remain active throughout the
finalization run in `ExecStop`. That wouldn't work if it was specified
in `ExecStart` in the same unit since it would be killed before the
`ExecStop` action was run.

Fixes: #2543
This commit is contained in:
Dan Nicholson committed 2022-08-30 09:16:39 -06:00
1 parent e30a3b6b17
commit f3db79e7fa
6 files changed
+193 -7

No files matched your search

+5 -2
View File
@@ -463,6 +463,7 @@ fn impl_transaction_test<M: AsRef<str>>(
"
systemctl stop rpm-ostreed
systemctl stop ostree-finalize-staged
systemctl stop ostree-finalize-staged-hold
ostree reset testrepo:${testref} ${booted_commit}
rpm-ostree cleanup -pbrm
",
@@ -504,7 +505,8 @@ fn impl_transaction_test<M: AsRef<str>>(
InterruptStrategy::Force(ForceInterruptStrategy::Kill9) => {
bash!(
"systemctl kill -s KILL rpm-ostreed || true
systemctl kill -s KILL ostree-finalize-staged || true"
systemctl kill -s KILL ostree-finalize-staged || true
systemctl kill -s KILL ostree-finalize-staged-hold || true"
)?;
live_strategy = Some(strategy);
}
@@ -528,7 +530,8 @@ fn impl_transaction_test<M: AsRef<str>>(
InterruptStrategy::Polite(PoliteInterruptStrategy::Stop) => {
bash!(
"systemctl stop rpm-ostreed || true
systemctl stop ostree-finalize-staged || true"
systemctl stop ostree-finalize-staged || true
systemctl stop ostree-finalize-staged-hold || true"
)?;
live_strategy = Some(strategy);
}
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
# https://github.com/ostreedev/ostree/issues/2543
set -xeuo pipefail
. ${KOLA_EXT_DATA}/libinsttest.sh
case "${AUTOPKGTEST_REBOOT_MARK:-}" in
"")
# Ensure boot is mount point
mountpoint /boot
# Create an automount unit with an extremely short timeout
cat > /etc/systemd/system/boot.automount <<"EOF"
[Automount]
Where=/boot
TimeoutIdleSec=1
[Install]
WantedBy=local-fs.target
EOF
systemctl daemon-reload
systemctl enable boot.automount
# Unmount /boot, start the automount unit, and ensure the units are
# in the correct states.
umount /boot
systemctl start boot.automount
boot_state=$(systemctl show -P ActiveState boot.mount)
boot_auto_state=$(systemctl show -P ActiveState boot.automount)
assert_streq "${boot_state}" inactive
assert_streq "${boot_auto_state}" active
# Trigger a new staged deployment and check that the relevant units
# are enabled.
ostree admin deploy --stage --karg-append=somedummykarg=1 "${host_commit}"
rpm-ostree status --json
deployment_staged=$(rpmostree_query_json '.deployments[0].staged')
assert_streq "${deployment_staged}" true
test -f /run/ostree/staged-deployment
finalize_staged_state=$(systemctl show -P ActiveState ostree-finalize-staged.service)
finalize_staged_hold_state=$(systemctl show -P ActiveState ostree-finalize-staged-hold.service)
assert_streq "${finalize_staged_state}" active
assert_streq "${finalize_staged_hold_state}" active
# Sleep 1 second to ensure the boot automount idle timeout has
# passed and then check that /boot is still mounted.
sleep 1
boot_state=$(systemctl show -P ActiveState boot.mount)
assert_streq "${boot_state}" active
/tmp/autopkgtest-reboot "2"
;;
"2")
rpm-ostree status --json
deployment_staged=$(rpmostree_query_json '.deployments[0].staged')
assert_streq "${deployment_staged}" false
test ! -f /run/ostree/staged-deployment
assert_file_has_content_literal /proc/cmdline somedummykarg=1
# Check that the finalize and hold services succeeded in the
# previous boot. Dump them to the test log to help debugging.
prepare_tmpdir
journalctl -b -1 -o short-monotonic \
-u ostree-finalize-staged.service \
-u ostree-finalize-staged-hold.service \
-u boot.mount \
-u boot.automount \
> logs.txt
cat logs.txt
assert_file_has_content logs.txt 'ostree-finalize-staged.service: \(Succeeded\|Deactivated successfully\)'
assert_file_has_content logs.txt 'ostree-finalize-staged-hold.service: \(Succeeded\|Deactivated successfully\)'
# Check that the hold service remained active and kept /boot mounted until
# the finalize service completed.
finalize_stopped=$(journalctl -b -1 -o json -g Stopped -u ostree-finalize-staged.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
hold_stopping=$(journalctl -b -1 -o json -g Stopping -u ostree-finalize-staged-hold.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
hold_stopped=$(journalctl -b -1 -o json -g Stopped -u ostree-finalize-staged-hold.service | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
boot_unmounting=$(journalctl -b -1 -o json -g Unmounting -u boot.mount | tail -n1 | jq -r .__MONOTONIC_TIMESTAMP)
test "${finalize_stopped}" -lt "${hold_stopping}"
test "${hold_stopped}" -lt "${boot_unmounting}"
;;
*) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;;
esac