merge: only a hibernating instance may render as asleep (#30)
console: only a hibernating instance may render as asleep
4 files changed,
+29
−3
internal/console/metricsview.go+13 −2internal/console/server.go+5 −0internal/console/server_test.go+7 −1internal/console/templates/app.html+4 −0
diff --git a/internal/console/metricsview.go b/internal/console/metricsview.go| index a6defb365..be27dcb31 100644 |
| --- a/internal/console/metricsview.go |
| +++ b/internal/console/metricsview.go |
| @@ -403,11 +403,22 @@ type InstanceMetrics struct { |
| MemBytes *int64 |
| SparkCPU, SparkMem string |
| + |
| + // Hibernate is the route's hibernation setting, copied in by the caller |
| + // (handleApp) — the sweep cannot see it. It only matters when Reporting |
| + // is false, where it decides what the absence of readings may claim: a |
| + // hibernating instance is presumed asleep, anything else is NOT — it may |
| + // be serving right now from outside per-instance accounting (a bespoke |
| + // unit like keymail-relay.service, or an exec child of the agent), and |
| + // the template must not assert a sleep state nobody observed. |
| + Hibernate bool |
| } |
| // Reporting is the template's one question: did any box report this |
| -// instance's unit in the last sweep? False is the hibernating/absent case — |
| -// rendered as "asleep — no recent readings", never as zeros. (Reporting with |
| +// instance's unit in the last sweep? False is the no-readings case, whose |
| +// rendering splits on Hibernate: hibernating instances read "asleep — no |
| +// recent readings", everything else the state-neutral "no per-instance |
| +// readings" — never zeros either way. (Reporting with |
| // nil readings is a different, honest state: the unit is up but that metric |
| // was unreadable, which renders as the usual em-dash.) |
| func (m InstanceMetrics) Reporting() bool { return m.Box != "" } |
diff --git a/internal/console/server.go b/internal/console/server.go| index a24650380..2dd00ad47 100644 |
| --- a/internal/console/server.go |
| +++ b/internal/console/server.go |
| @@ -1237,15 +1237,20 @@ func (s *Server) handleApp(w http.ResponseWriter, r *http.Request) { |
| // with (the half-migrated state AccountMismatch describes) yields no |
| // metrics rather than someone else's. |
| hosts := make([]string, 0, len(views[0].Routes)) |
| + hib := make(map[string]bool, len(views[0].Routes)) |
| for _, rt := range views[0].Routes { |
| if rt.Account != acct.Sqid { |
| continue |
| } |
| hosts = append(hosts, rt.Host) |
| + hib[rt.Host] = rt.Hibernate |
| } |
| if am, err := s.OwnerAppMetrics(r.Context(), hosts); err != nil { |
| log.Printf("console: app metrics %s: %v", app, err) |
| } else { |
| + for i := range am { |
| + am[i].Hibernate = hib[am[i].Host] |
| + } |
| p.AppMetrics = am |
| } |
| if events, err := s.store.Events(acct.ID, app, eventsCap); err != nil { |
diff --git a/internal/console/server_test.go b/internal/console/server_test.go| index 9c5d054a9..29955b01a 100644 |
| --- a/internal/console/server_test.go |
| +++ b/internal/console/server_test.go |
| @@ -1370,6 +1370,11 @@ func TestAppPageOwnerMetricsBoundary(t *testing.T) { |
| Status: "ready", App: "kass", Backing: registry.BackingUnit}) |
| fx.addRoute(t, registry.Route{Host: "asleep.example.com", Kind: "instance", |
| Status: "ready", App: "kass", Backing: registry.BackingUnit, Hibernate: true}) |
| + // The keymail.dev shape: awake and serving, but from a process no |
| + // carlos-app@ cgroup covers (bespoke unit / exec child), so the sweep |
| + // has nothing for it. Non-hibernating, so "asleep" would be a lie. |
| + fx.addRoute(t, registry.Route{Host: "unmetered.example.com", Kind: "instance", |
| + Status: "ready", App: "kass"}) |
| ctx := context.Background() |
| at := time.Now().UTC().Add(-time.Minute) // inside metricsFreshWindow |
| @@ -1398,7 +1403,8 @@ func TestAppPageOwnerMetricsBoundary(t *testing.T) { |
| } |
| for _, want := range []string{ |
| "Resources", "owned.example.com", "11%", "1.0 KB", |
| - "asleep.example.com", "asleep — no recent readings", |
| + "asleep.example.com</code> — asleep — no recent readings", |
| + "unmetered.example.com</code> — no per-instance readings", |
| } { |
| if !strings.Contains(body, want) { |
| t.Fatalf("app page missing %q in:\n%s", want, body) |
diff --git a/internal/console/templates/app.html b/internal/console/templates/app.html| index 8684587a9..44a0cf899 100644 |
| --- a/internal/console/templates/app.html |
| +++ b/internal/console/templates/app.html |
| @@ -77,7 +77,11 @@ |
| </div> |
| </section> |
| {{else}} |
| +{{if .Hibernate}} |
| <p class="small"><code>{{.Host}}</code> — asleep — no recent readings</p> |
| +{{else}} |
| +<p class="small"><code>{{.Host}}</code> — no per-instance readings</p> |
| +{{end}} |
| {{end}} |
| {{end}} |
| {{end}} |