Harden service reload recovery

This commit is contained in:
ushan0v
2026-07-11 02:40:57 +03:00
parent 6bfdca6229
commit 834b8b8d4b
4 changed files with 65 additions and 36 deletions
@@ -165,11 +165,19 @@ service_trigger_signature=abc" "$(cat "$TARGET_RELOAD_STATE")" "captured reload
printf 'format=1\n' >"$SNAPSHOT_FILE"
printf 'old=1\n' >"$TARGET_RELOAD_STATE"
printf 'stale=1\n' >"$TARGET_RELOAD_STATE.snapshot.100.200"
printf 'stale=1\n' >"$TARGET_RELOAD_STATE.snapshot.interrupted"
printf 'keep=1\n' >"$WORK_DIR/unrelated.snapshot.100.200"
state_ucode clear-reload-state "$TARGET_RELOAD_STATE" "$SNAPSHOT_FILE"
[ ! -e "$TARGET_RELOAD_STATE" ] ||
fail "ucode should clear reload state file"
[ ! -e "$SNAPSHOT_FILE" ] ||
fail "ucode should clear reload state snapshot file"
[ ! -e "$TARGET_RELOAD_STATE.snapshot.100.200" ] &&
[ ! -e "$TARGET_RELOAD_STATE.snapshot.interrupted" ] ||
fail "ucode should clear stale snapshots owned by the reload state file"
[ -e "$WORK_DIR/unrelated.snapshot.100.200" ] ||
fail "ucode should preserve unrelated snapshot files"
cat >"$WORK_DIR/service-dns-state.json" <<'JSON'
{
@@ -62,6 +62,25 @@ require_pattern 'startup_config_fingerprint = external_config_fingerprint();' \
"startup must snapshot runtime-relevant config after validation"
require_pattern 'mark_pending_reload_if_config_changed(startup_config_fingerprint, "config_changed_during_start")' \
"startup must queue reload when config changes while service is starting"
awk '
/function restart_runtime_for_reload\(\)/ { in_restart = 1 }
in_restart && /status = start_impl\(\);/ { saw_start_impl = 1 }
in_restart && /dnsmasq_configure\(true\)/ { duplicated_dns = 1 }
in_restart && /write-current-reload-state-clean/ { duplicated_state = 1 }
in_restart && /DNS_FAILOVER_UC.*start-runtime/ { duplicated_failover = 1 }
in_restart && /^}/ { done = 1; exit }
END { exit done && saw_start_impl && !duplicated_dns && !duplicated_state && !duplicated_failover ? 0 : 1 }
' "$LIFECYCLE_UC" ||
fail "reload fallback restart must reuse the complete start_impl path without duplicating finalization"
require_pattern 'function abort_reload(status, runtime_changed)' \
"reload failures must share one cleanup decision owner"
require_pattern 'return abort_reload(status, true);' \
"reload failures after runtime mutation must clean the partial runtime"
cleanup_function_line="$(grep -nF 'function cleanup_failed_runtime()' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
abort_function_line="$(grep -nF 'function abort_reload(status, runtime_changed)' "$LIFECYCLE_UC" | head -n1 | cut -d: -f1)"
[ -n "$cleanup_function_line" ] && [ -n "$abort_function_line" ] &&
[ "$cleanup_function_line" -lt "$abort_function_line" ] ||
fail "cleanup_failed_runtime must be declared before abort_reload for ucode runtime calls"
require_pattern 'pending_reload_log_context(reason)' \
"startup queued reload must be visible in logs"
require_pattern 'return "current reload";' \