Preserve patched_at across the post-restart re-mount
TrueNAS compatibility / compat (push) Successful in 15s
Release / release (push) Successful in 15s
CI / python 3.11 (push) Failing after 7s
CI / python 3.12 (push) Successful in 17s
CI / python 3.13 (push) Successful in 20s
CI / shell (shellcheck + syntax) (push) Successful in 9s
TrueNAS compatibility / compat (push) Successful in 15s
Release / release (push) Successful in 15s
CI / python 3.11 (push) Failing after 7s
CI / python 3.12 (push) Successful in 17s
CI / python 3.13 (push) Successful in 20s
CI / shell (shellcheck + syntax) (push) Successful in 9s
create_task.py verify decides "loaded" by comparing middlewared's start time against hook_status.json's patched_at. The post-restart re-mount restores the same patch the boot pass already applied, but it runs after the restart — so letting apply.sh re-stamp made patched_at newer than the process that had correctly imported the patch, and verify reported FAIL while everything was working. That would have fired on every boot where docker's nvidia sysext merge detaches the overlay. Found on hardware while exercising the candidate: a new lying status introduced by the fix for a lying status. The snapshot lives in /run, not the repo — a leftover file there would leave the tree dirty, which update.sh refuses to run over.
This commit is contained in:
+8
-1
@@ -76,7 +76,14 @@ worse than no alert, because one day it carries a security fix.
|
||||
straight back into the same race. So the overlay is re-mounted for the benefit
|
||||
of the next restart, and the question of whether *this* middlewared actually
|
||||
holds the patch is left to the one thing that can answer it exactly — the
|
||||
in-process alert below.
|
||||
in-process alert below. That re-mount preserves `hook_status.json`'s
|
||||
`patched_at`: `create_task.py verify` decides "loaded" by comparing
|
||||
middlewared's start time against that stamp, so a re-apply running *after* the
|
||||
restart would have made the stamp newer than the process which correctly
|
||||
imported the patch, and `verify` would have reported FAIL forever on every
|
||||
boot where the sysext merge detaches the overlay. Caught on hardware while
|
||||
validating the candidate — a new lying status introduced by the fix for a
|
||||
lying status.
|
||||
|
||||
Two supporting fixes fell out of the same failure. `_ensure_writable` treated
|
||||
"one of our overlays is listed on this directory" as "already done" — but it
|
||||
|
||||
@@ -143,7 +143,26 @@ else
|
||||
_log "nvidia sysext merge follows it) -- re-mounting for the next restart."
|
||||
_log "Whether THIS middlewared loaded the patch is answered in-process by"
|
||||
_log "the 'installed but NOT loaded' alert, not by this check."
|
||||
|
||||
# Preserve hook_status.json's patched_at across this re-mount.
|
||||
#
|
||||
# create_task.py verify decides "loaded" by comparing middlewared's start
|
||||
# time against patched_at. This re-apply restores the SAME patch the boot
|
||||
# pass already applied, but it runs *after* the restart -- so letting it
|
||||
# re-stamp would make patched_at newer than the process that correctly
|
||||
# imported the patch, and verify would report FAIL forever, on every boot
|
||||
# where docker's sysext merge detaches the overlay. That is precisely the
|
||||
# lying-status failure this release exists to remove, so do not introduce a
|
||||
# new one. The snapshot lives in /run, never in the repo: a leftover file
|
||||
# there would leave the tree dirty and update.sh refuses to run over that.
|
||||
_saved_status=/run/truecloud-hook_status.pre
|
||||
cp -p "$PATCH_DIR/hook_status.json" "$_saved_status" 2>/dev/null
|
||||
|
||||
TRUECLOUD_REAPPLY=1 /bin/bash "$PATCH_DIR/patch/apply.sh"
|
||||
|
||||
if [ -f "$_saved_status" ]; then
|
||||
mv -f "$_saved_status" "$PATCH_DIR/hook_status.json" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
_log "=== deferred restart complete ==="
|
||||
|
||||
@@ -172,3 +172,28 @@ def test_mount_retries_on_a_private_workdir():
|
||||
body = src[start:end]
|
||||
assert body.count("mount -t overlay") == 2, "expected a retry mount"
|
||||
assert 'work="/run/truecloud-${tag}-work.$$"' in body
|
||||
|
||||
|
||||
def test_post_restart_remount_preserves_the_patched_at_stamp():
|
||||
"""create_task.py verify compares middlewared's start time to patched_at.
|
||||
|
||||
The post-restart re-mount restores the same patch the boot pass applied, so
|
||||
letting apply.sh re-stamp would make patched_at newer than the process that
|
||||
correctly imported it -- verify would then report FAIL forever on every boot
|
||||
where docker's sysext merge detaches the overlay.
|
||||
"""
|
||||
src = wait_restart_source()
|
||||
tail = src[src.index("systemctl try-restart middlewared"):]
|
||||
assert "hook_status.json" in tail
|
||||
save = tail.index("/run/truecloud-hook_status.pre")
|
||||
reapply = tail.index("TRUECLOUD_REAPPLY=1")
|
||||
restore = tail.rindex("hook_status.json")
|
||||
assert save < reapply < restore, "snapshot must bracket the re-apply"
|
||||
|
||||
|
||||
def test_status_snapshot_is_not_written_into_the_repo():
|
||||
"""A leftover file in the repo dir leaves the tree dirty, and update.sh
|
||||
refuses to run over a dirty tree -- that once made the patch un-updatable."""
|
||||
src = wait_restart_source()
|
||||
assert "/run/truecloud-hook_status.pre" in src
|
||||
assert '"$PATCH_DIR/hook_status.json.pre' not in src
|
||||
|
||||
Reference in New Issue
Block a user