R-1270 follow-up (found live on 9202): remove with „delete its backups too” deletes the app's restore-undo folder too — a not-started restore's kept copy outlived the remove; the wiring test also pins the D47 set-aside removal. Red-proved. Commit only.
gates / gates (push) Successful in 1m29s

This commit is contained in:
2026-10-25 10:36:20 +01:00
parent 1f9c863f79
commit 1f8295918e
4 changed files with 12 additions and 2 deletions
+3
View File
@@ -80,6 +80,9 @@ felhom.eu `07` §6.6.
is common). The data stays restored; the restore's undo copy is KEPT, like a failed put-back's copy for support —
no new retention rule. A watcher looks 30 more minutes: when the app runs, the record turns to success („…elindult:
a visszaállítás kész.") and the copy is removed, as after any successful restore.
- **Found live on 9202, fixed without a row:** the kept undo copy of a not-started restore (and any copy kept for
support) outlived the app's remove with „delete its backups too". The remove now deletes `backups/restore-undo/<app>`
with the app's other backups (`TestR474_RemoveHandlerDeletesUnitMirrorAndPrefs` extended, seen failing).
- Tests (each seen failing with the old WARN): `TestR1270_AUnitRestoreWhoseAppDoesNotStartIsNotASuccess`,
`…ALateStartTurnsTheRecordIntoASuccess`, `…TheOffsiteRestoreToo`, web `…TheRecordOfANotStartedRestore`; controls
`…AHealthyRestoreIsASuccessAsBefore`, `…MarkLateStartTouchesOnlyItsOwnRecord`.
@@ -33,7 +33,10 @@ func TestR474_RemoveHandlerDeletesUnitMirrorAndPrefs(t *testing.T) {
})
}
joined := " " + strings.Join(sel, " ") + " "
for _, want := range []string{"RecoveryUnitPath", "Tier2MirrorDirsForApp", "RemoveTier2Mirrors", "DeleteAppBackupPrefs"} {
// 2026-10-25: the set-aside units of earlier installs (D47) and the restore's kept undo copy (found live on 9202: a
// not-started restore's copy outlived the app's remove with its backups) go too.
for _, want := range []string{"RecoveryUnitPath", "Tier2MirrorDirsForApp", "RemoveTier2Mirrors", "DeleteAppBackupPrefs",
"SetAsideDirsForApp", "RemoveSetAsideUnits", "RestoreUndoDir"} {
if !strings.Contains(joined, " "+want+" ") {
t.Errorf("removeStack must call %s", want)
}
+2 -1
View File
@@ -1054,7 +1054,8 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri
if body.RemoveBackups && r.backupMgr != nil {
nsRoot := r.backupMgr.AppNamespaceRoot(name)
if nsRoot != "" {
backupPaths = append(backupPaths, backup.RecoveryUnitPath(nsRoot, name), backup.AppDBDumpPath(nsRoot, name))
backupPaths = append(backupPaths, backup.RecoveryUnitPath(nsRoot, name), backup.AppDBDumpPath(nsRoot, name),
backup.RestoreUndoDir(nsRoot, name)) // a restore's kept undo copy is a backup of the app too (2026-10-25)
}
mirrorDirs = r.backupMgr.Tier2MirrorDirsForApp(name)
// D47: the units of earlier installs set aside for this app are its backups too (the D25 kept-copy rule).
@@ -42,6 +42,9 @@ type liveVolumeCopy struct {
keep bool // set when a put-back could not be done: the copy is the support path back, never removed
}
// RestoreUndoDir is restoreUndoDir for the app's remove with „delete its backups too" (a kept copy goes with it).
func RestoreUndoDir(nsRoot, stack string) string { return restoreUndoDir(nsRoot, stack) }
// restoreUndoDir is where the live-volume copy of one restore lives. Under backups/ on purpose: no capture set
// includes it (appbackup.captureset skips backups/), and backups/primary — the recovery unit — is never written.
func restoreUndoDir(nsRoot, stack string) string {