agent v0.86.0: DR-tier-by-default — capability inactive state (GatedBy/GateActive, pbsdr gate via DRConfigured) + F-3 root-run provision parent ownership
Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN
This commit is contained in:
@@ -53,7 +53,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.63.0"
|
||||
var version = "0.86.0"
|
||||
|
||||
// runGuestHook is the PVE hook body (`felhom-agent guest-hook <vmid> <phase>`). On pre-start it
|
||||
// creates placeholder dirs for any absent bind-mount source so the guest always boots (the C1 net);
|
||||
@@ -399,10 +399,12 @@ func poolReadStatus(ctx context.Context, px *proxmox.Client) capability.Status {
|
||||
|
||||
// logCapabilities logs the privileged-capability self-check at startup: one INFO summary, plus an
|
||||
// ERROR per degraded capability naming the gated feature (so a missing grant is loud at cutover,
|
||||
// not days later). It never exits — serve-degraded.
|
||||
// not days later). Inactive (config-gated off, plumbing healthy — v0.86.0) is counted in the
|
||||
// summary but never error-logged: disabled ≠ broken. It never exits — serve-degraded.
|
||||
func logCapabilities(statuses []capability.Status, logger *slog.Logger) {
|
||||
ok, total, degraded := capability.Summarize(statuses)
|
||||
logger.Info("capabilities self-check", "ok", ok, "total", total, "degraded", len(degraded))
|
||||
logger.Info("capabilities self-check",
|
||||
"ok", ok, "total", total, "degraded", len(degraded), "inactive", total-ok-len(degraded))
|
||||
for _, d := range degraded {
|
||||
logger.Error("capability DEGRADED — privileged grant missing (feature impaired until fixed)",
|
||||
"capability", d.Name, "feature", d.Feature, "reason", d.Reason, "critical", d.Critical)
|
||||
@@ -459,13 +461,27 @@ func runDaemon(cfg config.Config, logger *slog.Logger, logRing *applog.Ring) int
|
||||
// startup (loud on any denial) and attach the snapshot to every hub report; the hub owns the
|
||||
// ok→degraded alert. Serve-degraded — a missing grant never blocks startup.
|
||||
capProber := capability.Prober{Runner: &proxmox.ExecRunner{Mode: proxmox.RunnerDirect}}
|
||||
// DR-tier gate (v0.86.0, DR-tier-by-default): the pbsdr-* capabilities are config-gated — on a
|
||||
// box whose DR tier is not configured (no descriptor ever / descriptor disabled) a HEALTHY
|
||||
// probe reports "inactive (disabled by configuration)" instead of ok; broken plumbing (binary
|
||||
// missing / grant denied) stays DEGRADED regardless (an un-migrated box must never look
|
||||
// deliberately off). Late-bound: the pbsdr manager is constructed further down; probes run at
|
||||
// report time. nil (pre-assignment) fails ACTIVE — the historical behavior.
|
||||
var drConfigured func() bool
|
||||
capProber.GateActive = func(gate string) bool {
|
||||
if gate == capability.GatePBSDR && drConfigured != nil {
|
||||
return drConfigured()
|
||||
}
|
||||
return true
|
||||
}
|
||||
// A1 (v0.62.0): compose the PVE pool-read check AROUND the sudo prober (an API read does not
|
||||
// belong inside the sudo-policy probe). Non-critical: a degraded pool read means the stale-lock
|
||||
// reaper fail-safes (locks stay uncleared) — visible on the hub report, no operator page.
|
||||
probeAll := func(ctx context.Context) []capability.Status {
|
||||
return append(capProber.Probe(ctx), poolReadStatus(ctx, px))
|
||||
}
|
||||
logCapabilities(probeAll(context.Background()), logger)
|
||||
// (The startup self-check log runs AFTER the pbsdr manager is wired below, so its snapshot
|
||||
// already carries the gated view — v0.86.0.)
|
||||
collector.SetCapabilityProber(probeAll)
|
||||
loop := hub.NewLoop(collector, client, time.Duration(hcfg.PollSeconds)*time.Second, logger)
|
||||
interval := time.Duration(hcfg.PollSeconds) * time.Second
|
||||
@@ -796,8 +812,14 @@ func runDaemon(cfg config.Config, logger *slog.Logger, logRing *applog.Ring) int
|
||||
pbsdrLoop = pbsdr.NewLoop(pdMgr, 60*time.Second, logger)
|
||||
desiredSyncer.AddConsumer(pbsdrLoop) // raw desired-state → the pbs_dr block
|
||||
collector.SetPBSDRReporter(pbsdrLoop)
|
||||
// Capability gate wiring (v0.86.0): the prober's GatePBSDR now answers from the bridge
|
||||
// (descriptor state, marker-backed across restarts) — see the capProber block above.
|
||||
drConfigured = pdMgr.DRConfigured
|
||||
logger.Info("pbsdr: bridge enabled (hub-driven; no-op until a pbs_dr descriptor arrives)")
|
||||
}
|
||||
// Startup capability self-check — after the pbsdr gate wiring so the logged snapshot matches
|
||||
// what the first report will carry (inactive vs degraded is already resolved here).
|
||||
logCapabilities(probeAll(context.Background()), logger)
|
||||
|
||||
// Run reconcile, the hub loop, the storage watchdog, the restore-test scheduler, the PBS
|
||||
// verify loop, (optionally) the local-API server, and (optionally) the LAN resolver loop
|
||||
|
||||
Reference in New Issue
Block a user