D48: a restore downloads first — every restore path resolves and pulls every image of the definition that will run before the stop, under the image-work lock held until the restore returns; a failed pull refuses with the app untouched (a deleted version and a busy registry read differently), one operator line. Red-proved. Commit only.
gates / gates (push) Successful in 1m26s

This commit is contained in:
2026-10-25 09:38:28 +01:00
parent b680db85cb
commit 2d71270858
16 changed files with 404 additions and 3 deletions
+9
View File
@@ -1541,6 +1541,15 @@ func main() {
})
// R-893 option A (D20 = A): a failed off-site restore that was PUT BACK — the live volumes and definition poured
// back and the app started. The same operator-tier event as the hold above (no new event type), its own leg.
// D48: a restore refused BEFORE anything was touched because an image of the backup's version could not be
// downloaded — the operator line that tells a deleted version (R-698, R-933) from a busy registry.
backupMgr.SetRestorePullRefusedNotify(func(stack, ref string, cause error) {
d := notify.BackupRunFailuresDetails{RunKind: "restore", Failed: 1, Attempted: 1}
d.Apps = append(d.Apps, notify.RunFailureDetail{App: stack, Leg: "restore-image-unavailable", Reason: cause.Error()})
msg := fmt.Sprintf("App %q: a restore was REFUSED before anything was touched — the image %s could not be downloaded (D48). "+
"The app runs as it was; the household was told. Cause: %v", stack, ref, cause)
notifier.NotifyBackupRunFailures(msg, d)
})
// R-1270: a restore whose data is back but whose app did not start in time — one operator line, its own leg.
backupMgr.SetRestoreNotStartedNotify(func(stack, kind string, cause error) {
d := notify.BackupRunFailuresDetails{RunKind: "restore", Failed: 1, Attempted: 1}