8.5 KiB
REPORT — v0.169.0/.1: Disk-health card + degradation notification ("Lemezek állapota")
Part of the disk-health train (agent v0.94.0 + controller v0.169.0/.1 + hub v0.73.1). Appended above the previous report (kept below).
Baselines: controller e164fef (v0.168.0) → c97975c (v0.169.0) → dbf6313 (v0.169.1). Clean at start.
What shipped:
agentapi:SmartSummaryextended to the full counter set;DiskInfo.Smart *SmartSummary; pureDiskVerdictFor(shared by card + check) +Label+DegradedAttributes. Verdicts: FAILING→Hiba; PASSED+any(reallocated/pending/offline/critical/media >0 or percentage_used ≥90)→Figyelmeztetés; PASSED clean→Rendben; nil/UNKNOWN→Nincs adat (never alarms).web: dashboard "Lemezek állapota" card (physical disks only — pbs/lvm/nfs/cifs excluded in v0.169.1) via a 60s /disks TTL cache; unreachable agent → "Nincs adat", page never blocks. 6hdisk-health-checkemitsdisk_health_degradedon a degradation only (in-memory baseline; first run silent; recovery/ UNKNOWN never notify; multi-attr → one event).notify.NotifyDiskHealthDegraded(warn/critical). No global banner (deliberate).
Tests + red-proofs (all recorded): verdict table (red-proof: ≥90→>90 → boundary FAIL); notifier emit (type/severity/subject); check first-run-silent (red-proof: disable the guard → first run notifies FAIL); degradation-once; recovery-silent; UNKNOWN-excluded; FAILING→critical; nil-smart card graceful; TTL cache. Full controller suite: 27 packages pass, 0 fail; build + vet clean; template/emoji/mojibake gates OK.
Deploy + live validation (guest 9201): built + deployed 0.169.0 → Up (healthy); dashboard rendered
the card. §13(2): first render showed 4 rows (pbs/lvm carried a default UNKNOWN SMART) → shipped
v0.169.1 to exclude logical/network types; re-verified the card now shows exactly 2 physical rows
(local, USB), both "Nincs adat" — the correct real verdict on the virtualized demo hardware.
§13(3) notification path (proven with the hub, see hub REPORT): a disk_health_degraded POST — the
exact wire call notify.PushEvent makes — was 400-rejected by hub v0.73.0 and 200-accepted +
emailed by hub v0.73.1. Method: direct endpoint POST via curl (no fixture code committed → nothing to
revert; the demo's real disks stay "Nincs adat" so normal operation is quiet).
Awaiting operator: browser visual check of the card layout (rendered HTML strings confirmed). A real FAILING/Figyelmeztetés verdict can't occur on the virtualized demo hardware (no real SMART) — the verdict logic + transitions are covered by unit tests + the live event-pipeline proof.
REPORT — v0.168.0: Customer-configurable backup window ("Mentési időablak")
Date: 2026-07-24 · Deployed & verified live on guest 9201; box left configured at 02:30.
1. Baseline
| Repo | main @ start |
version | → |
|---|---|---|---|
| felhom-controller | e33c1ae |
v0.167.1 | v0.168.0 (committed 82c67e3) |
Verified against live Gitea at task start (HEAD == origin == live). No agent coupling; MinAgent unchanged.
2. What shipped
ONE customer setting — "Mentési időablak kezdete" (window start W) — drives every nightly leg at
FIXED, never-stored offsets so misordering is impossible: DB dump at W, tier-2 at W+60m, off-box
at W+105m (wrap-safe). The whole-guest (agent PBS/vzdump) cycle is gated to [W+2h, W+6h) with a
safety valve. Precedence: settings > controller.yaml db_dump_schedule > "02:30". Changes apply
without restart.
3. Files
- New
internal/backupwindow/(backupwindow.go + _test.go) — pure time math:ParseHHMM/FmtHHMM/LegTimes/GateWindow/EffectiveWindow/Valid; offsets are constants (derived, never stored). internal/scheduler/scheduler.go(+ new _test.go) —UpdateDaily(name,timeStr) bool, per-daily-job bufferedreschedchan, acase <-job.reschedinrunDailyJob, Schedule read under the mutex.internal/settings/settings.go—BackupWindowStartfield + Get/Set.internal/quiesce/quiesce.go(+ new quiesce_window_test.go; fakes updated in quiesce_test.go / quiesce_8b2_test.go) —Backend.Duenow returns the backup age;Options.WindowStartFn/Cadence;scheduledRunAllowedpredicate + Budapest helper; gate on the scheduled path inrunOnce.internal/web/backup_handlers.go(+ new backup_window_test.go),handlers.go,server.goroute,templates/backups.html— the "Mentési időablak" card + POST/backups/window.cmd/controller/main.go— the three Daily legs + backup-cache + initial-cache + quiesce loop wired from the effective window.- Docs:
CHANGELOG.md,CONTEXT.md,controller/README.md,REUSE.md(UpdateDaily + backupwindow + 3rd Budapest-loader duplication note), thisREPORT.md.
Note: a stray
gofmt -w internal/ cmd/reformatted ~46 unrelated (non-gofmt-clean) files; all were restored toorigin/mainso the commit is surgical — only the files above are in the diff.
4. Tests + red-proofs (all recorded)
go build ./... && go vet ./... && go test ./... — 27 packages pass, 0 fail. Template/design gates
(template_id / emoji / mojibake / native-confirm) all OK.
| Group | Test(s) | Red-proof (mutate → FAIL → restore) |
|---|---|---|
| A | LegTimes (02:30/23:30/22:00/00:00, wrap), LegTimes_InvalidRejected, GateWindow |
drop the FmtHHMM modulo → LegTimes("23:30") yields "24:30"/"25:15" → FAIL confirmed |
| B | EffectiveWindow precedence table (settings>yaml>"02:30", corrupt falls through) |
— |
| C | UpdateDaily_MutatesAndSignals, _UnknownAndInvalid, _GoroutineConsumesReschedule |
remove case <-job.resched → signal stays buffered → immediacy test FAIL confirmed |
| D | scheduledRunAllowed truth table (inside/boundary/outside/valve/wrap/nil-age) + Loop integration (defer outside / run inside / valve runs / manual TriggerNow never gated) |
invert the valve >→< → starving-box + no-valve cases FAIL confirmed |
| E | BackupWindowSave_Valid (settings + 3× UpdateDaily + flash), _Invalid (nothing stored + error flash) |
skip the Valid() check → invalid value gets stored → FAIL confirmed |
Existing quiesce/scheduler/web suites still pass unchanged.
5. Deploy + live validation (guest 9201)
Built felhom-controller:0.168.0 (145M) from the clean pushed tree; deployed via bootstrap →
Up (healthy), Controller elindult (0.168.0). Startup registered the legs at the default window:
db-dump 02:30 · tier2-backup 03:30 · offbox-backup 04:15 (CEST). Method: endpoint-level (in-guest
curl to 172.17.0.2:8080, Host: felhom.demo-felhom.eu; login 302 + scraped session CSRF).
Save a window (POST /backups/window, window_start=22:00) → 303:
- Page re-rendered: input
value="22:00", derived rows22:00 / 23:00 / 23:45, rendszermentéskb. 00:00–04:00 között(W+2h..W+6h, wrap-safe). - Logs (NO restart):
db-dump rescheduled 02:30 → 22:00,tier2-backup 03:30 → 23:00,offbox-backup 04:15 → 23:45,backup window set to 22:00 (legs 22:00/23:00/23:45).
Restore (window_start=02:30) → 303: page shows 02:30 / 03:30 / 04:15 + kb. 04:30–08:30 között;
logs show the three legs rescheduled back. Box left at 02:30.
6. NOT live-exercised on 9201 (stated method)
- The disk-tier window-gate DEBUG denial could not be triggered live: the demo host agent is DOWN
for the remote-site window (its
localapibinds192.168.0.162, which doesn't exist there), so the quiesce loop's/backup/duecall errors before the gate is reached. The gate + safety valve + the manual-never-gated property are fully covered by Group D (truth table + Loop integration). Re-verify the live DEBUG line once the agent is reachable again. - Browser visual check of the "Mentési időablak" card layout is Viktor's acceptance step (the rendered HTML strings are confirmed; the pixels are not).
7. Observations (out of scope — documented only)
- Saving explicitly writes
backup_window_start:"02:30"— functionally identical to the empty/default state, so the box is correctly "at 02:30" either way. - Cadence for the safety valve defaults to 24h controller-side (the agent's
/backup/dueresponse carries no cadence field); if cadence policy ever becomes hub/agent-driven, thread it through instead of the constant. - Three Budapest-timezone loaders now exist (scheduler, web, quiesce) — noted in REUSE.md §6; kept local to avoid a scheduler↔quiesce import edge.