controller v0.272.0: the backup page says when a whole-box backup does not fit (R-685); R-671, R-670, R-677
gates / gates (push) Successful in 26s
gates / gates (push) Successful in 26s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -174,6 +174,9 @@ type Manager struct {
|
||||
|
||||
// updatingCheck (slice 4) — nil-safe; see isHeld / SetUpdatingCheck.
|
||||
updatingCheck func(stackName string) bool
|
||||
// undoCopyRemover (R-671, v0.272.0) deletes an app's leftover undo copies — stacks.Manager.RemoveUndoCopies,
|
||||
// wired in main.go (SetUndoCopyRemover). nil-safe: without it the copies stay, as before.
|
||||
undoCopyRemover func(stackName string) int
|
||||
|
||||
// R-475 update-precondition seams, one per tier. Nil → the real Tier2UnitRestorePoint /
|
||||
// ListRestorePoints / OffsiteSnapshotTimes. They let a test reach Tier 1 and Tier 3 without a drive
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||
)
|
||||
|
||||
// R-671 (v0.272.0) — the undo copies kept by an update hold are removed when a restore lifts that hold; a
|
||||
// RESTORE hold (R-379) lifts nothing and removes nothing; an app a guarded update is moving is never touched.
|
||||
//
|
||||
// COMPANION RED-PROOF (REPORT.md): drop the undoCopyRemover call from clearUpdateHoldAfterRestore — this test
|
||||
// fails at "the lifted update hold's undo copies were left behind".
|
||||
func TestR671_RestoreThatClearsAnUpdateHoldRemovesItsUndoCopies(t *testing.T) {
|
||||
sett := slice4Settings(t)
|
||||
m := &Manager{logger: log.New(io.Discard, "", 0), settings: sett}
|
||||
removed := map[string]int{}
|
||||
m.SetUndoCopyRemover(func(s string) int { removed[s]++; return 3 })
|
||||
_ = m.HoldAfterFailedUpdate("nextcloud", time.Now(), time.Now(), UpdateTierLocal)
|
||||
_ = sett.SetRestoreHold(settings.RestoreHold{Stack: "rst", At: "2026-08-22T14:00:00Z"})
|
||||
m.clearUpdateHoldAfterRestore("nextcloud")
|
||||
m.clearUpdateHoldAfterRestore("rst")
|
||||
if removed["nextcloud"] != 1 {
|
||||
t.Fatalf("the lifted update hold's undo copies were left behind (remover calls: %v)", removed)
|
||||
}
|
||||
if removed["rst"] != 0 {
|
||||
t.Fatal("a restore hold is not lifted by a restore, so nothing of it may be removed")
|
||||
}
|
||||
// an app mid-update: its copies are the live undo — never removed
|
||||
_ = m.HoldAfterFailedUpdate("moving", time.Now(), time.Now(), UpdateTierLocal)
|
||||
m.SetUpdatingCheck(func(s string) bool { return s == "moving" })
|
||||
m.clearUpdateHoldAfterRestore("moving")
|
||||
if removed["moving"] != 0 {
|
||||
t.Fatal("the undo copies of an app a guarded update is moving must never be removed")
|
||||
}
|
||||
}
|
||||
@@ -607,6 +607,21 @@ func (m *Manager) clearUpdateHoldAfterRestore(stackName string) {
|
||||
return
|
||||
}
|
||||
m.logger.Printf("[INFO] [backup] %s: restore completed — the update hold (set %s) is CLEARED", stackName, h.At)
|
||||
// R-671 (v0.272.0): the undo copies the hold kept describe the state this restore just replaced. They
|
||||
// were kept so the hold's data stayed recoverable; once the app is restored whole they are dead weight
|
||||
// (measured 2026-09-24 on 9202: three nextcloud copies, ~0.9 GiB, outlived the hold and nothing named
|
||||
// them). Removed here, and only here — never for a restore hold (R-379), never while an update moves the
|
||||
// app. Pinned by TestR671_RestoreThatClearsAnUpdateHoldRemovesItsUndoCopies.
|
||||
if m.undoCopyRemover != nil && (m.updatingCheck == nil || !m.updatingCheck(stackName)) {
|
||||
n := m.undoCopyRemover(stackName)
|
||||
m.logger.Printf("[INFO] [backup] %s: removed %d undo cop(y/ies) the lifted update hold had kept (R-671)", stackName, n)
|
||||
}
|
||||
}
|
||||
|
||||
// SetUndoCopyRemover wires the undo-copy cleanup (R-671): stacks.Manager.RemoveUndoCopies. INIT-ONLY, main.go —
|
||||
// pinned by TestUndoCopyRemoverIsWiredAtStartup (an AST walk). The backup package cannot import stacks.
|
||||
func (m *Manager) SetUndoCopyRemover(fn func(stackName string) int) {
|
||||
m.undoCopyRemover = fn
|
||||
}
|
||||
|
||||
// UpdateHeldStacks is the set of apps held stopped after a failed update (R-660, v0.268.0) — the
|
||||
|
||||
Reference in New Issue
Block a user