v0.39.0 — DR-recipe completion: live PBS coord + drop role/restic_repo_coord from v1 drive shape
Live PBS coord: new internal/pbs/live_reporter.go (LiveSnapshotReporter implements
hub.PBSReporter via the cheap Client.Snapshots() list with last-known-good fallback,
bounded by an 8s timeout, list-only — never triggers a verify). Closes the gap where
the recipe's pbs block was omitted whenever the verify-loop SnapshotStore was empty
(one-shot collect + the first ~6h after a daemon restart). SnapshotStore.Get added
(per-datastore LKG). Wired into the collector in both runDaemon and runSelftestHub;
the verify loop keeps Recording into the SAME shared store via one hoisted pbsTargets.
v1 host-half drive shape: dropped drives[].role (hub/operator-owned manifest concept,
not host-derivable) and drives[].restic_repo_coord (named a backup tier that doesn't
exist). Drive shape is now {durable_id, mount_path, intent, fs_type?, total_bytes}.
Hub reads drives as json.RawMessage → no hub struct change; goldens re-pinned
byte-identical (agent + hub copies).
Tests: live_reporter_test.go (T1 load-bearing coord-without-verify + T2..T6),
TestDRRecipeHostHalf_V1DriveShape; each companion demonstrated to fail pre-fix then
reverted. go build/vet/test green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ import (
|
||||
|
||||
// version is the agent version. Overridable at build time with
|
||||
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
||||
var version = "0.38.0"
|
||||
var version = "0.39.0"
|
||||
|
||||
// runGuestHook is the PVE pre-start hook body (`felhom-agent guest-hook <vmid> <phase>`). On the
|
||||
// pre-start phase it creates placeholder dirs for any absent bind-mount source so the guest always boots
|
||||
@@ -289,10 +289,17 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
// latest restore-test result; the collector reads it via the BackupReporter /
|
||||
// RestoreTestReporter seams; the cadence scheduler writes it.
|
||||
backupStore := backup.NewStore()
|
||||
// PBS snapshot inventory + verify-state (slice 6 Phase B): the verify loop writes it; the
|
||||
// collector reads it via the PBSReporter seam.
|
||||
// PBS snapshot inventory + verify-state (slice 6 Phase B): the verify loop writes the shared
|
||||
// store; the collector reads it via the PBSReporter seam. DR-recipe completion (v0.39.0): the
|
||||
// collector now reads through a LiveSnapshotReporter that lists snapshots LIVE each collect
|
||||
// (cheap GET, last-known-good fallback) so the host-report's pbs coord is present whenever PBS is
|
||||
// reachable — even seconds after a restart, before the 6 h verify loop has run. The verify loop
|
||||
// keeps Recording into the SAME store (shared last-known-good); targets are resolved once and
|
||||
// shared by both. pbsTargets is hoisted so the reporter and the verify loop use one closure.
|
||||
pbsStore := pbs.NewSnapshotStore()
|
||||
collector := hub.NewCollector(px, hub.SystemctlProber{}, observer, backupStore, backupStore, pbsStore, cfg.Hub.HostID, version, logger)
|
||||
pbsTargets := pbsTargetsFromPVE(cfg, px, logger)
|
||||
pbsReporter := pbs.NewLiveSnapshotReporter(pbsTargets, pbsStore, pbs.DefaultLiveSnapshotTimeout, logger)
|
||||
collector := hub.NewCollector(px, hub.SystemctlProber{}, observer, backupStore, backupStore, pbsReporter, cfg.Hub.HostID, version, logger)
|
||||
loop := hub.NewLoop(collector, client, time.Duration(hcfg.PollSeconds)*time.Second, logger)
|
||||
interval := time.Duration(hcfg.PollSeconds) * time.Second
|
||||
|
||||
@@ -430,7 +437,7 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
// verify-state. It is maintenance/reporting (NOT gated/journaled). Auto-discovers pbs
|
||||
// storages from the PVE config each cycle; disabled cleanly (cadence<0) without crashing.
|
||||
pbsLoop := pbs.NewVerifyLoop(pbs.VerifyLoopOptions{
|
||||
Targets: pbsTargetsFromPVE(cfg, px, logger),
|
||||
Targets: pbsTargets, // the same closure the live reporter uses (shared store, one resolver)
|
||||
Store: pbsStore,
|
||||
Cadence: cfg.Backup.PBSVerifyCadence(),
|
||||
Logger: logger,
|
||||
@@ -807,7 +814,12 @@ func runSelftestHub(ctx context.Context, cfg config.Config, logger *slog.Logger)
|
||||
return 1
|
||||
}
|
||||
observer := storage.NewObserver(px, storage.NewProcHostReader(), newHostOps(cfg, logger), logger)
|
||||
collector := hub.NewCollector(px, hub.SystemctlProber{}, observer, nil, nil, nil, cfg.Hub.HostID, version, logger)
|
||||
// Wire the LIVE PBS reporter here too (v0.39.0): selftest=hub is a separate one-shot process — no
|
||||
// verify loop runs — so a nil reporter previously yielded pbs_snapshots:[] and an absent dr_recipe
|
||||
// pbs coord. The live reporter lists snapshots directly (fresh store, last-known-good fallback) so
|
||||
// the selftest reflects exactly what a freshly-restarted daemon's first collect emits.
|
||||
pbsReporter := pbs.NewLiveSnapshotReporter(pbsTargetsFromPVE(cfg, px, logger), pbs.NewSnapshotStore(), pbs.DefaultLiveSnapshotTimeout, logger)
|
||||
collector := hub.NewCollector(px, hub.SystemctlProber{}, observer, nil, nil, pbsReporter, cfg.Hub.HostID, version, logger)
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
Reference in New Issue
Block a user