controller v0.239.0: any backup tier lets an app update (R-475)
gates / gates (push) Successful in 14s
gates / gates (push) Successful in 14s
Operator ruling 2026-09-13. The update precondition walks Tier 2, Tier 1 (own recovery unit, "helyi") and Tier 3 (off-site, 15 s bound; unreachable counts as absent with a WARN) and leans on the first FRESH copy; the backup_max_age rule applies to whichever tier is chosen. No copy anywhere: back up first. Refused only when nothing exists and no backup can be taken. RunAppBackupNow tolerates a Tier-2 failure (WARN) and marks the captured unit proven current. The hold names the tier (második meghajtó / saját meghajtó / távoli mentés) and the date; pre-v0.239.0 holds keep their text. A successful off-site restore now lifts an update hold. The backups page still uses Tier2UnitRestorePoint unchanged. Scenarios G-M tested; red-proofs M, L, the tail and the off-site clear in felhom.eu documentation/audits/rulings-r472-r475-2026-09-13/.
This commit is contained in:
@@ -68,7 +68,7 @@ func TestSlice4_UpdateHoldTextNamesTheTimeAndTheCopy(t *testing.T) {
|
||||
m := &Manager{logger: log.New(io.Discard, "", 0), settings: sett}
|
||||
at := time.Date(2026, 9, 13, 8, 0, 0, 0, time.UTC)
|
||||
copyAt := time.Date(2026, 9, 13, 1, 30, 0, 0, time.UTC)
|
||||
if err := m.HoldAfterFailedUpdate("bookstack", at, copyAt); err != nil {
|
||||
if err := m.HoldAfterFailedUpdate("bookstack", at, copyAt, UpdateTierSecondDrive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h, ok := sett.GetRestoreHold("bookstack")
|
||||
@@ -77,7 +77,7 @@ func TestSlice4_UpdateHoldTextNamesTheTimeAndTheCopy(t *testing.T) {
|
||||
}
|
||||
held, why := m.RestoreHoldFor("bookstack")
|
||||
// Budapest is UTC+2 in September: 08:00Z → 10:00, 01:30Z → 03:30.
|
||||
want := fmt.Sprintf(UpdateHoldFmt, "bookstack", "2026-09-13 10:00", "2026-09-13 03:30")
|
||||
want := fmt.Sprintf(UpdateHoldFmt, "bookstack", "2026-09-13 10:00", "második meghajtó", "2026-09-13 03:30")
|
||||
if !held || why != want {
|
||||
t.Errorf("hold text =\n%q\nwant\n%q", why, want)
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestSlice4_RestoreHoldTextIsUnchanged(t *testing.T) {
|
||||
func TestSlice4_ASuccessfulRestoreClearsOnlyAnUpdateHold(t *testing.T) {
|
||||
sett := slice4Settings(t)
|
||||
m := &Manager{logger: log.New(io.Discard, "", 0), settings: sett}
|
||||
_ = m.HoldAfterFailedUpdate("upd", time.Now(), time.Now())
|
||||
_ = m.HoldAfterFailedUpdate("upd", time.Now(), time.Now(), UpdateTierLocal)
|
||||
_ = sett.SetRestoreHold(settings.RestoreHold{Stack: "rst", At: "2026-08-22T14:00:00Z"})
|
||||
m.clearUpdateHoldAfterRestore("upd")
|
||||
m.clearUpdateHoldAfterRestore("rst")
|
||||
@@ -136,7 +136,7 @@ func TestSlice4_UpdateBusy(t *testing.T) {
|
||||
func TestSlice4_NightlyLegsLeaveAHeldAppAlone(t *testing.T) {
|
||||
h := newAdmissionHarness(t, "held", "free")
|
||||
h.m.settings = slice4Settings(t)
|
||||
if err := h.m.HoldAfterFailedUpdate("held", time.Now(), time.Now()); err != nil {
|
||||
if err := h.m.HoldAfterFailedUpdate("held", time.Now(), time.Now(), UpdateTierSecondDrive); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.m.runVolumeDumps()
|
||||
|
||||
Reference in New Issue
Block a user