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:
@@ -1,3 +1,54 @@
|
||||
## v0.239.0 — any backup tier lets an app update (2026-09-13, R-475)
|
||||
|
||||
**MinAgent: 0.129.0** (unchanged)
|
||||
|
||||
**Operator ruling 2026-09-13: every backup counts.** Until now the update's precondition was the
|
||||
Tier-2 unit predicate alone, so an app with no second-drive copy could never be updated. On demo-hp
|
||||
`gokapi` and `nextcloud` were in exactly that state, each with a fresh recovery unit on its own drive.
|
||||
|
||||
**WHAT CHANGED.**
|
||||
|
||||
- **`backup.Manager.UpdateRestorePoints`**, beside `Tier2UnitRestorePoint`. It walks the tiers in the
|
||||
ruling's order: Tier 2 (second drive), Tier 1 (the app's own unit, `ListRestorePoints`, „helyi"),
|
||||
Tier 3 (`OffsiteInventoryList`, bounded by 15 s). It returns the first copy the caller accepts and
|
||||
stops there, so a fresh Tier-2 copy never reaches the network. An unreachable off-site repository
|
||||
counts as ABSENT, with a WARN. A box with no off-site target is plainly absent, with no WARN.
|
||||
- **The age rule is one rule.** stacks passes `freshRestorePoint` (`update.backup_max_age`) as the
|
||||
acceptance test, so the limit applies to whichever tier is chosen. The first FRESH copy wins, so a
|
||||
stale Tier-2 mirror never forces a backup while the app's own unit is minutes old.
|
||||
- **No copy anywhere → back up first**, then re-read every tier. The preflight refuses `no_backup`
|
||||
only when there is no copy on any tier AND `CanBackUpApp` says no backup can be taken now. The
|
||||
Hungarian sentence changed to say that; it no longer tells the customer to switch on the 2nd backup.
|
||||
- **`RunAppBackupNow` tolerates no Tier-2 target.** A Tier-2 failure after the unit capture is a WARN.
|
||||
The captured unit's manifest is marked proven current, because the capture's checksum skip leaves it
|
||||
untouched on a quiet app, and Tier 1 is aged by the newest artifact's mtime. Without that mark an
|
||||
app with no database and no volume would be refused forever — the ProvenCopyTime trap, one tier down.
|
||||
- **The hold names the tier.** `settings.RestoreHold.CopyTier`; the sentence now ends *„Visszaállítható
|
||||
a Mentések oldalon ebből a biztonsági mentésből: <második meghajtó | saját meghajtó | távoli mentés>,
|
||||
<dátum>."* A hold written by v0.237.0–v0.238.1 has no tier and keeps its original sentence
|
||||
(`UpdateHoldLegacyFmt`). The update journal records `proven_tier`, so a resumed update names the
|
||||
right copy.
|
||||
- **A successful off-site restore lifts an update hold.** That path never went through
|
||||
`RestoreFromRecoveryUnitAt`, so a hold naming „távoli mentés" could otherwise never be cleared.
|
||||
- **Unchanged:** the backups page still calls `Tier2UnitRestorePoint` for „Teljes visszaállítás".
|
||||
|
||||
**TESTS.** `internal/stacks/update_tiers_test.go` — G (Tier 2 chosen when present), H (own unit alone),
|
||||
I (off-site alone), K (nothing anywhere: backed up first and the update completes; a failing backup
|
||||
moves nothing), L (an existing copy still carries an app that cannot be backed up; control: without it
|
||||
the app is refused), M (a stale copy on each tier is backed up first; a stale Tier 2 does not block a
|
||||
fresh Tier 1; the limit is inclusive on one clock). `internal/backup/update_tiers_test.go` — the tier
|
||||
order and early stop, H/I at the source, J (unreachable → absent + WARN; no target → silent; a hanging
|
||||
repository ends at the bound), the hold text per tier and the legacy text, the tolerant pre-backup
|
||||
tail, `RunAppBackupNow` really calling it, `CanBackUpApp`, and the off-site restore clearing the hold.
|
||||
`cmd/controller/r475_wiring_test.go` — the tier numbers agree across packages; the adapter reads every
|
||||
tier and never `Tier2UnitRestorePoint`; the hold is told the tier. Slice 4's tests were moved onto the
|
||||
new seam (`TestSlice4_C` is now the L refusal).
|
||||
|
||||
**Red-proofs** (felhom.eu `documentation/audits/rulings-r472-r475-2026-09-13/`): **M** — age checked
|
||||
only for Tier 2: three M cases fail (a 30-hour-old own unit and off-site copy carry the update with no
|
||||
backup). **L** — refuse even with a copy: the L test fails. **Tail** — the proven-current mark misses:
|
||||
the tail test fails on the mtime. **Off-site clear** removed: the hold stays and the test fails.
|
||||
|
||||
## v0.238.1 — the nightly backup leaves an app alone WHILE it is being updated, not only once it is held (2026-09-13, slice 4 follow-up)
|
||||
|
||||
**MinAgent: 0.129.0** (unchanged)
|
||||
|
||||
Reference in New Issue
Block a user