Merge pull request 'node-power: the power seat, and code for its moments (hq ADR 0211)' (#268) from feat/0211-node-power into main
This commit is contained in:
@@ -98,3 +98,24 @@ func TestTwoModulesDeclaringOnePackageAreRefusedBeforeAnythingIsRecorded(t *test
|
||||
t.Errorf("an unrelated assignment is refused for a collision already there: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeForAPowerMomentDependsOnThePowerSeat(t *testing.T) {
|
||||
for _, moment := range powerMoments {
|
||||
m := mod("laptop", nil, nil, nil)
|
||||
m.Shell = []ShellCode{{For: moment, Slot: "normal", Code: "true"}}
|
||||
if got := DependsOn(m); !reflect.DeepEqual(got, []string{PowerSeat}) {
|
||||
t.Errorf("code for %s depends on %v, want %s", moment, got, PowerSeat)
|
||||
}
|
||||
if p := m.shellProblems(); len(p) != 0 {
|
||||
t.Errorf("code for %s is refused: %v", moment, p)
|
||||
}
|
||||
}
|
||||
m := mod("laptop", nil, nil, nil)
|
||||
m.Shell = []ShellCode{{For: "after-lunch", Slot: "normal", Code: "true"}}
|
||||
if p := m.shellProblems(); len(p) == 0 {
|
||||
t.Error("code for a moment that does not exist was accepted")
|
||||
}
|
||||
if s, ok := SeatNamed(PowerSeat); !ok || s.Scope != ScopeNode {
|
||||
t.Errorf("%s is not a node seat of the mesh's own: %+v %v", PowerSeat, s, ok)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import (
|
||||
const (
|
||||
EnvironmentSeat = "node-environment"
|
||||
LoginShellSeat = "node-login-shell"
|
||||
// PowerSeat is the seat whose holder places code for the power moments (novox/hq ADR 0211).
|
||||
PowerSeat = "node-power"
|
||||
)
|
||||
|
||||
// Where an environment entry on PATH goes: before the account's existing PATH, or after it.
|
||||
@@ -73,11 +75,15 @@ var (
|
||||
// code the session's start runs, `xresources` X resources merged at its start. Placed by the
|
||||
// display server's holder, as a shell's slots are placed by the login shell's.
|
||||
sessionFiles = []string{"xinitrc", "xresources"}
|
||||
// powerMoments are the moments of a machine's power a module may run code at (novox/hq ADR
|
||||
// 0211 §3): POSIX code run as root by node-power's holder, in module order, each piece bounded.
|
||||
powerMoments = []string{"after-boot", "before-sleep", "after-wake", "before-shutdown", "on-mains", "on-battery"}
|
||||
)
|
||||
|
||||
// contributionTargets is every name a contribution's `for` may take.
|
||||
func contributionTargets() []string {
|
||||
return append(append([]string(nil), knownShells...), sessionFiles...)
|
||||
out := append(append([]string(nil), knownShells...), sessionFiles...)
|
||||
return append(out, powerMoments...)
|
||||
}
|
||||
|
||||
// placerOf is the seat whose holder places a contribution for this target (novox/hq ADR 0204,
|
||||
@@ -86,6 +92,9 @@ func placerOf(target string) string {
|
||||
if oneOf(sessionFiles, target) {
|
||||
return DisplayServerSeat
|
||||
}
|
||||
if oneOf(powerMoments, target) {
|
||||
return PowerSeat
|
||||
}
|
||||
return LoginShellSeat
|
||||
}
|
||||
|
||||
@@ -191,8 +200,9 @@ func (m Manifest) shellProblems() []string {
|
||||
for i, c := range m.Shell {
|
||||
if !oneOf(contributionTargets(), c.For) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s's shell code %d is for %q; the shells are %s, and the session's files %s",
|
||||
m.Module, i+1, c.For, strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", ")))
|
||||
"%s's shell code %d is for %q; the shells are %s, the session's files %s, and the power "+
|
||||
"moments %s", m.Module, i+1, c.For, strings.Join(knownShells, ", "),
|
||||
strings.Join(sessionFiles, ", "), strings.Join(powerMoments, ", ")))
|
||||
}
|
||||
if !oneOf(knownSlots, c.Slot) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
@@ -265,9 +275,9 @@ func placeholderProblems(m Manifest, r map[string]any) []string {
|
||||
if !two || !oneOf(contributionTargets(), target) || !oneOf(knownSlots, slot) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s's resource %v names %s; shell code is ${shell:<shell>:<slot>}, the shell one of "+
|
||||
"%s or the session's file one of %s, and the slot one of %s", m.Module, r["id"], c[0],
|
||||
strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", "),
|
||||
strings.Join(knownSlots, ", ")))
|
||||
"%s, the session's file one of %s or the power moment one of %s, and the slot one of %s",
|
||||
m.Module, r["id"], c[0], strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", "),
|
||||
strings.Join(powerMoments, ", "), strings.Join(knownSlots, ", ")))
|
||||
continue
|
||||
}
|
||||
seat := placerOf(target)
|
||||
@@ -275,6 +285,13 @@ func placeholderProblems(m Manifest, r map[string]any) []string {
|
||||
continue
|
||||
}
|
||||
refusedFor[seat] = true
|
||||
if seat == PowerSeat {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s's resource %v names %s and %s does not claim %s; every module's code for a power "+
|
||||
"moment is placed by the power seat's holder alone (novox/hq ADR 0211)",
|
||||
m.Module, r["id"], c[0], m.Module, seat))
|
||||
continue
|
||||
}
|
||||
if seat == DisplayServerSeat {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s's resource %v names %s and %s does not claim %s; every module's %s is placed by "+
|
||||
|
||||
@@ -172,6 +172,10 @@ var defaultSeats = append([]Seat{
|
||||
// node may call; the holder places every module's shell code in its slots.
|
||||
{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
||||
Serves: loginShellVerbs()},
|
||||
// A machine's power (novox/hq ADR 0211): its holder owns logind's power handling, places the
|
||||
// code modules contribute for the power moments, and publishes the machine's power states as
|
||||
// its events. Every machine has one — every machine boots and shuts down. No verbs yet.
|
||||
{Name: PowerSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0211"},
|
||||
// Deferred (novox/hq ADR 0121): renaming to mesh-private-network is a scope + server/client
|
||||
// model change, not a rename, so it stays until that is built.
|
||||
{Name: "the-private-network", Scope: ScopeNode, Decision: "novox/hq ADR 0110"},
|
||||
|
||||
@@ -46,12 +46,13 @@ func TestTheSeatsAreAClosedSetAndEachNamesItsDecision(t *testing.T) {
|
||||
delivered[s.Delivers] = s.Name
|
||||
}
|
||||
}
|
||||
// Thirty-two since the graphical session's eleven (novox/hq ADR 0208); twenty-one with
|
||||
// Thirty-three with node-power (novox/hq ADR 0211); thirty-two since the graphical session's
|
||||
// eleven (ADR 0208); twenty-one with
|
||||
// node-package-manager and node-container-runtime (ADR 0207); nineteen with node-environment and
|
||||
// node-login-shell (ADR 0203, ADR 0204); seventeen with node-build-agent (ADR 0190). One fewer
|
||||
// once the retired mesh-build-machine row goes, when no registered manifest claims it any more.
|
||||
if len(Seats()) != 32 {
|
||||
t.Errorf("the mesh defines %d seats rather than 32; the set is closed, so a change here is "+
|
||||
if len(Seats()) != 33 {
|
||||
t.Errorf("the mesh defines %d seats rather than 33; the set is closed, so a change here is "+
|
||||
"a decision (novox/hq ADR 0110): %s", len(Seats()), seatNames())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user