Merge pull request 'Name the account whose own manager runs a unit (hq ADR 0254, issue 318)' (#55) from fix/318-a-wait-for-a-person-is-not-a-failure into main
This commit was merged in pull request #55.
This commit is contained in:
+12
-3
@@ -1565,7 +1565,7 @@ func healthAsReported(st liveness.Statement, ns *network.Statement) *link.Health
|
||||
for _, r := range st.Resources {
|
||||
h.Resources = append(h.Resources, link.ResourceHealth{Module: r.Module, Resource: r.ID, Kind: r.Kind,
|
||||
Target: r.Target, State: r.State, Reason: r.Reason, Since: r.Since.UTC(), Streak: r.Streak,
|
||||
Restarts: r.Restarts, Check: r.CheckOf(), Needs: r.NeedsOf()})
|
||||
Restarts: r.Restarts, Check: r.CheckOf(), Needs: r.NeedsOf(), Account: userOf(r.Scope, r.User)})
|
||||
}
|
||||
if ns != nil && ns.State != "" {
|
||||
h.Network = &link.NetworkHealth{State: ns.State, Since: ns.Since.UTC(), Parts: []link.NetworkPart{}}
|
||||
@@ -1600,7 +1600,7 @@ func withUnits(h *link.Health, us *units.Statement) *link.Health {
|
||||
}
|
||||
h.Resources = append(h.Resources, link.ResourceHealth{Module: f.Module, Resource: f.Resource,
|
||||
Kind: link.KindUnit, Target: f.Unit, State: link.StateUnhealthy, Reason: reason, Since: f.Since.UTC(),
|
||||
Streak: f.Streak})
|
||||
Streak: f.Streak, Account: userOf(f.Scope, f.User)})
|
||||
}
|
||||
return h
|
||||
}
|
||||
@@ -1615,11 +1615,20 @@ func withAccounts(h *link.Health, as *accounts.Statement) *link.Health {
|
||||
for _, v := range as.Accounts {
|
||||
h.Resources = append(h.Resources, link.ResourceHealth{Module: v.Module, Resource: v.ID,
|
||||
Kind: link.KindAccount, Target: v.Name, State: v.State, Reason: v.Reason, Since: v.Since.UTC(),
|
||||
Streak: v.Streak})
|
||||
Streak: v.Streak, Account: v.Name})
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// userOf is the account whose own service manager a unit is in (novox/hq ADR 0254), empty for the
|
||||
// machine's own manager.
|
||||
func userOf(scope, user string) string {
|
||||
if scope == declaration.ScopeUser {
|
||||
return user
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// failedUnitWords is a failed unit as the console says it.
|
||||
func failedUnitWords(f units.Failed) string {
|
||||
whose := "no module places it"
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-host/internal/accounts"
|
||||
"github.com/novox/mesh-host/internal/declaration"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
"github.com/novox/mesh-host/internal/liveness"
|
||||
"github.com/novox/mesh-host/internal/units"
|
||||
@@ -17,7 +19,7 @@ func TestTheStatementCarriesTheFailedUnits(t *testing.T) {
|
||||
at := time.Date(2026, 10, 8, 12, 0, 0, 0, time.UTC)
|
||||
us := &units.Statement{At: at, State: units.Degraded, Failed: []units.Failed{
|
||||
{Unit: "openrazer-daemon.service", Scope: units.ScopeUser, Load: "loaded", Result: "exit-code",
|
||||
Module: "openrazer", Resource: "openrazer.daemon", Via: units.ViaPackage, Since: at, Streak: 2},
|
||||
User: "operator", Module: "openrazer", Resource: "openrazer.daemon", Via: units.ViaPackage, Since: at, Streak: 2},
|
||||
{Unit: "storage-media.mount", Scope: units.ScopeSystem, Load: "loaded", Result: "timeout", Since: at, Streak: 2},
|
||||
}}
|
||||
h := withUnits(healthAsReported(liveness.Statement{At: at}, nil), us)
|
||||
@@ -30,6 +32,10 @@ func TestTheStatementCarriesTheFailedUnits(t *testing.T) {
|
||||
!strings.Contains(r.Reason, "account's own service manager") || !strings.Contains(r.Reason, "exit-code") {
|
||||
t.Fatalf("the module's failed unit: %+v", r)
|
||||
}
|
||||
// Whose manager it fails in, so the controller can tell it from a fault (novox/hq ADR 0254).
|
||||
if r.Account != "operator" {
|
||||
t.Fatalf("the unit in the account's own manager does not name the account: %q", r.Account)
|
||||
}
|
||||
if h.Units == nil || h.Units.State != units.Degraded || len(h.Units.Failed) != 1 ||
|
||||
h.Units.Failed[0].Unit != "storage-media.mount" || h.Units.Failed[0].Result != "timeout" {
|
||||
t.Fatalf("the machine's own: %+v", h.Units)
|
||||
@@ -41,3 +47,31 @@ func TestTheStatementCarriesTheFailedUnits(t *testing.T) {
|
||||
t.Fatal("a judge not yet given a declaration said units")
|
||||
}
|
||||
}
|
||||
|
||||
// Every resource that runs in an account's own service manager names the account, and so does the account
|
||||
// a module put in a group (novox/hq ADR 0254): the controller reads a unit that cannot run before a new login
|
||||
// by the two together. What the machine's own manager runs names none.
|
||||
func TestTheStatementNamesTheAccountsManager(t *testing.T) {
|
||||
at := time.Date(2026, 10, 8, 12, 0, 0, 0, time.UTC)
|
||||
st := liveness.Statement{At: at, Resources: []liveness.State{
|
||||
{Resource: liveness.Resource{Module: "openrazer", ID: "openrazer.daemon", Kind: "service",
|
||||
Target: "openrazer-daemon.service", Scope: declaration.ScopeUser, User: "operator"}, State: liveness.Unhealthy,
|
||||
Reason: "down", Since: at},
|
||||
{Resource: liveness.Resource{Module: "docker", ID: "docker.engine", Kind: "service", Target: "docker.service",
|
||||
Scope: declaration.ScopeSystem}, State: liveness.Healthy, Since: at},
|
||||
}}
|
||||
as := &accounts.Statement{At: at, Accounts: []accounts.Verdict{{Account: accounts.Account{Module: "openrazer",
|
||||
ID: "openrazer.operator", Name: "operator", Groups: []string{"openrazer"}}, State: accounts.Unhealthy,
|
||||
Reason: accounts.ReasonRelogin + ": operator is in the group openrazer", Since: at}}}
|
||||
h := withAccounts(healthAsReported(st, nil), as)
|
||||
got := map[string]string{}
|
||||
for _, r := range h.Resources {
|
||||
got[r.Resource] = r.Account
|
||||
}
|
||||
want := map[string]string{"openrazer.daemon": "operator", "docker.engine": "", "openrazer.operator": "operator"}
|
||||
for id, account := range want {
|
||||
if a, said := got[id]; !said || a != account {
|
||||
t.Errorf("%s names the account %q (said %v); want %q", id, a, said, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +350,11 @@ type ResourceHealth struct {
|
||||
// provision it exercises, for the controller to hold what it finds under the provider (to-be 48 §6).
|
||||
Check string `json:"check,omitempty"`
|
||||
Needs string `json:"needs,omitempty"`
|
||||
// Account is the account whose own service manager runs it, for a unit or a service in an account's
|
||||
// manager, and the account itself for a resource of kind KindAccount (novox/hq ADR 0254): what lets the
|
||||
// controller tell a unit that cannot run before a new login from one that is broken. Empty for anything
|
||||
// the machine's own manager or a container runtime runs.
|
||||
Account string `json:"account,omitempty"`
|
||||
}
|
||||
|
||||
// HealthSaid is the health event: a machine's statement between its reports, on HealthSubject.
|
||||
|
||||
Reference in New Issue
Block a user