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
+2 -1
View File
@@ -54,6 +54,7 @@ func (f *fakeStacks) stoppedNames() []string {
// fakeBackend drives the agent-side responses.
type fakeBackend struct {
due bool
dueAge *int64 // age of newest successful backup (nil = none); gate safety-valve input
dueErr error
startErr error
jobID string
@@ -64,7 +65,7 @@ type fakeBackend struct {
mu sync.Mutex
}
func (b *fakeBackend) Due(context.Context) (bool, error) { return b.due, b.dueErr }
func (b *fakeBackend) Due(context.Context) (bool, *int64, error) { return b.due, b.dueAge, b.dueErr }
func (b *fakeBackend) StartBackup(context.Context) (string, error) {
b.mu.Lock()
b.startCalls++