hub: dead-host roll-up honesty - customer status folds worst expected host
Customer status (dashboard row, /configs list, detail header + strip) is
now worst(controllerDerived, hostStatusOf(each expected host)) via the ONE
staleness definition (Server.hostStatus, hosts.go - shared with the
HostStalenessChecker; no second threshold). Any host down/stale caps the
customer at WARN with a cause chip naming the host ("host down: <id>");
pending (never-reported) hosts worsen only once the customer has reported
(onboarding exclusion). The three previously-inlined controller-status
chains collapse into controllerStatus() (rollup.go). Display + derivation
only - checker alerting untouched.
Live shape pinned (drill-1 / Peti cluster): host down 23h + controller
report minutes old rendered a GREEN row - TestRollup_DeadHostMasking now
fails that exact outcome. Red-proof: short-circuiting foldHostStatus to
controller-only flips C + two D subtests red ("dashboard row is GREEN
over a 23h-dead host").
This commit is contained in:
+21
-22
@@ -53,6 +53,7 @@ type customerListEntry struct {
|
||||
HasConfig bool
|
||||
IsBlocked bool
|
||||
OverallStatus string // ok, warn, down, disabled, pending, "" if no reports
|
||||
HostCause string // v0.53.0 roll-up: "" or "host down|stale|pending: <id>"
|
||||
ControllerVersion string
|
||||
TimeSinceReport time.Duration
|
||||
ConfigCreatedAt time.Time
|
||||
@@ -95,20 +96,13 @@ func (s *Server) handleConfigList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
for _, c := range customers {
|
||||
status := "ok"
|
||||
if c.HealthStatus == "disabled" {
|
||||
status = "disabled"
|
||||
} else if c.TimeSinceReport > time.Hour {
|
||||
status = "down"
|
||||
} else if c.TimeSinceReport > 30*time.Minute || c.HealthStatus == "warn" {
|
||||
status = "warn"
|
||||
} else if c.HealthStatus == "fail" {
|
||||
status = "down"
|
||||
}
|
||||
// Controller-derived status + the v0.53.0 dead-host roll-up (rollup.go).
|
||||
status, hostCause := s.foldHostStatus(c.CustomerID, controllerStatus(&c), true)
|
||||
|
||||
if entry, ok := merged[c.CustomerID]; ok {
|
||||
// Config exists — enrich with report data
|
||||
entry.OverallStatus = status
|
||||
entry.HostCause = hostCause
|
||||
entry.ControllerVersion = c.ControllerVersion
|
||||
entry.TimeSinceReport = c.TimeSinceReport
|
||||
if entry.CustomerName == "" {
|
||||
@@ -120,12 +114,21 @@ func (s *Server) handleConfigList(w http.ResponseWriter, r *http.Request) {
|
||||
CustomerID: c.CustomerID,
|
||||
CustomerName: c.CustomerName,
|
||||
OverallStatus: status,
|
||||
HostCause: hostCause,
|
||||
ControllerVersion: c.ControllerVersion,
|
||||
TimeSinceReport: c.TimeSinceReport,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Config-only customers (no reports yet): the roll-up still applies — a down/stale host
|
||||
// must not hide behind the muted no-reports dash; only never-reported hosts are excluded.
|
||||
for _, e := range merged {
|
||||
if e.OverallStatus == "" {
|
||||
e.OverallStatus, e.HostCause = s.foldHostStatus(e.CustomerID, "", false)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2 floor: resolve each customer's effective floor (override else global) + below-floor flag.
|
||||
globalFloor := s.store.GetGlobalMinControllerVersion()
|
||||
for _, e := range merged {
|
||||
@@ -198,21 +201,15 @@ func (s *Server) handleCustomerUnified(w http.ResponseWriter, r *http.Request, c
|
||||
json.Unmarshal([]byte(cfg.ConfigJSON), &overrides)
|
||||
}
|
||||
|
||||
// Overall status
|
||||
// Overall status: controller-derived + the v0.53.0 dead-host roll-up (rollup.go). The
|
||||
// blocked override stays LAST (administrative state wins the token); the host cause chip
|
||||
// renders regardless so the header says WHICH host is the problem.
|
||||
overallStatus := "pending"
|
||||
if customer != nil {
|
||||
if customer.HealthStatus == "disabled" {
|
||||
overallStatus = "disabled"
|
||||
} else if customer.TimeSinceReport > time.Hour {
|
||||
overallStatus = "down"
|
||||
} else if customer.TimeSinceReport > 30*time.Minute || customer.HealthStatus == "warn" {
|
||||
overallStatus = "warn"
|
||||
} else if customer.HealthStatus == "fail" {
|
||||
overallStatus = "down"
|
||||
} else {
|
||||
overallStatus = "ok"
|
||||
}
|
||||
overallStatus = controllerStatus(customer)
|
||||
}
|
||||
var hostCause string
|
||||
overallStatus, hostCause = s.foldHostStatus(customerID, overallStatus, customer != nil)
|
||||
if cfg != nil && cfg.Status == "blocked" {
|
||||
overallStatus = "blocked"
|
||||
}
|
||||
@@ -290,6 +287,7 @@ func (s *Server) handleCustomerUnified(w http.ResponseWriter, r *http.Request, c
|
||||
Customer *store.CustomerSummary
|
||||
Report map[string]interface{}
|
||||
OverallStatus string
|
||||
HostCause string // v0.53.0 roll-up: "" or "host down|stale|pending: <id>"
|
||||
|
||||
LatestVersion string
|
||||
UpdateAvailable bool
|
||||
@@ -386,6 +384,7 @@ func (s *Server) handleCustomerUnified(w http.ResponseWriter, r *http.Request, c
|
||||
Customer: customer,
|
||||
Report: report,
|
||||
OverallStatus: overallStatus,
|
||||
HostCause: hostCause,
|
||||
|
||||
LatestVersion: latestVersion,
|
||||
UpdateAvailable: updateAvailable,
|
||||
|
||||
Reference in New Issue
Block a user