Files
felhom-controller/REPORT.md
T

8.5 KiB
Raw Blame History

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: SmartSummary extended to the full counter set; DiskInfo.Smart *SmartSummary; pure DiskVerdictFor (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. 6h disk-health-check emits disk_health_degraded on 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 buffered resched chan, a case <-job.resched in runDailyJob, Schedule read under the mutex.
  • internal/settings/settings.goBackupWindowStart field + Get/Set.
  • internal/quiesce/quiesce.go (+ new quiesce_window_test.go; fakes updated in quiesce_test.go / quiesce_8b2_test.go) — Backend.Due now returns the backup age; Options.WindowStartFn/Cadence; scheduledRunAllowed predicate + Budapest helper; gate on the scheduled path in runOnce.
  • internal/web/backup_handlers.go (+ new backup_window_test.go), handlers.go, server.go route, 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), this REPORT.md.

Note: a stray gofmt -w internal/ cmd/ reformatted ~46 unrelated (non-gofmt-clean) files; all were restored to origin/main so 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 rows 22:00 / 23:00 / 23:45, rendszermentés kb. 00:0004: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:3008: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 localapi binds 192.168.0.162, which doesn't exist there), so the quiesce loop's /backup/due call 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/due response 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.