v0.168.0: customer-configurable backup window (Mentési időablak)

ONE setting (window start W) drives every nightly leg at fixed, never-stored
offsets: DB dump at W, tier-2 at W+60m, off-box at W+105m (wrap-safe). Precedence
settings > controller.yaml db_dump_schedule > 02:30.

- scheduler.UpdateDaily: retime a daily job at runtime (no restart) via a per-job
  buffered resched chan + a select case in runDailyJob.
- new pure package internal/backupwindow (LegTimes/GateWindow/EffectiveWindow).
- quiesce disk-tier window gate: scheduled cycles run only inside [W+2h,W+6h) with a
  safety valve (age>cadence+24h runs regardless); manual TriggerNow never gated.
  Backend.Due now also returns the backup age (from the agent's own /backup/due).
- backup page: Mentési időablak card (time input + derived leg/gate rows); POST
  /backups/window validates -> saves -> UpdateDaily x3 -> flash.

Tests: 5 groups, all red-proofed. Agent/cadence//backup/due untouched.
This commit is contained in:
2026-07-24 20:55:44 +02:00
parent e33c1aeabc
commit 82c67e32e1
19 changed files with 831 additions and 31 deletions
+36
View File
@@ -1,5 +1,41 @@
## Changelog
### v0.168.0 — Customer-configurable backup window ("Mentési időablak") (2026-07-24)
No agent coupling; MinAgent unchanged (the disk-tier gate is controller-side; the agent's cadence-based
`/backup/due` is untouched). New pure package `internal/backupwindow`; touches scheduler, settings,
quiesce, the backup page, and main.go wiring.
**One setting drives every nightly leg.** A single customer control — **"Mentési időablak kezdete"**
(default = the effective DB-dump time, historically "02:30") — from which every leg derives at FIXED,
never-stored offsets, so misordering is impossible: DB dump at **W**, tier-2 mirror at **W+60m**,
off-box at **W+105m** (wrap-safe across midnight). Precedence: settings > controller.yaml
`db_dump_schedule` > "02:30".
- **Scheduler seam `UpdateDaily(name, timeStr) bool`** (+ a per-daily-job buffered `resched` channel and
a new select case in `runDailyJob`): a saved window fans out to all three legs and takes effect at the
next scheduling pass **without a restart**. Unknown/non-daily name or invalid time → WARN + false, job
untouched.
- **Disk-tier (whole-guest PBS/vzdump) window gate.** The quiesce loop's scheduled cycles now run only
inside **[W+2h, W+6h)** (wrap-safe, Europe/Budapest wall-clock), with a **safety valve**: if the newest
successful backup is older than cadence+24h (or none exists), the cycle runs regardless of the window —
a box powered on only outside its window never starves. Gate denials log at DEBUG with the window.
**Manual triggers ("Mentés most" / `TriggerNow`) are NEVER gated** (they bypass `runOnce`). The
`Backend.Due` seam now also returns the backup age (from the agent's own `/backup/due` answer) for the
valve; the agent, its cadence, and `/backup/due` semantics are unchanged.
- **Backup page (Áttekintés):** a compact "Mentési időablak" card — time input (value = effective
window) + "Mentés" button, and the derived rows (adatbázis-mentés / helyi másolat / távoli mentés
times, and the "teljes rendszermentés kb. W+2hW+6h között" line). POST `/backups/window` validates →
saves → `UpdateDaily`×3 → PRG redirect with a Hungarian flash. Behind RequireAuth + CsrfProtect like
its siblings.
- Derived leg/gate times are **computed, never persisted**; no per-leg settings; the offsets are not
exposed in the UI.
Tests (5 groups, all red-proofed): `LegTimes`/`GateWindow` incl. midnight wrap + invalid-rejected;
`EffectiveWindow` precedence table; `UpdateDaily` mutate+signal + unknown/invalid + goroutine consumes
the reschedule; `scheduledRunAllowed` truth table (inside/outside/valve/wrap/nil-age) + Loop integration
(defer outside / run inside / valve runs / manual never gated); handler valid-save + invalid-rejected.
### v0.167.1 — Center the sidebar logo (2026-07-24)
CSS one-liner + test. `.sidebar-logo` gains `margin: 0 auto` so the 140px logo is horizontally