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)
|
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 (
|
const (
|
||||||
EnvironmentSeat = "node-environment"
|
EnvironmentSeat = "node-environment"
|
||||||
LoginShellSeat = "node-login-shell"
|
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.
|
// 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
|
// 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.
|
// display server's holder, as a shell's slots are placed by the login shell's.
|
||||||
sessionFiles = []string{"xinitrc", "xresources"}
|
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.
|
// contributionTargets is every name a contribution's `for` may take.
|
||||||
func contributionTargets() []string {
|
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,
|
// 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) {
|
if oneOf(sessionFiles, target) {
|
||||||
return DisplayServerSeat
|
return DisplayServerSeat
|
||||||
}
|
}
|
||||||
|
if oneOf(powerMoments, target) {
|
||||||
|
return PowerSeat
|
||||||
|
}
|
||||||
return LoginShellSeat
|
return LoginShellSeat
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +200,9 @@ func (m Manifest) shellProblems() []string {
|
|||||||
for i, c := range m.Shell {
|
for i, c := range m.Shell {
|
||||||
if !oneOf(contributionTargets(), c.For) {
|
if !oneOf(contributionTargets(), c.For) {
|
||||||
problems = append(problems, fmt.Sprintf(
|
problems = append(problems, fmt.Sprintf(
|
||||||
"%s's shell code %d is for %q; the shells are %s, and the session's files %s",
|
"%s's shell code %d is for %q; the shells are %s, the session's files %s, and the power "+
|
||||||
m.Module, i+1, c.For, strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", ")))
|
"moments %s", m.Module, i+1, c.For, strings.Join(knownShells, ", "),
|
||||||
|
strings.Join(sessionFiles, ", "), strings.Join(powerMoments, ", ")))
|
||||||
}
|
}
|
||||||
if !oneOf(knownSlots, c.Slot) {
|
if !oneOf(knownSlots, c.Slot) {
|
||||||
problems = append(problems, fmt.Sprintf(
|
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) {
|
if !two || !oneOf(contributionTargets(), target) || !oneOf(knownSlots, slot) {
|
||||||
problems = append(problems, fmt.Sprintf(
|
problems = append(problems, fmt.Sprintf(
|
||||||
"%s's resource %v names %s; shell code is ${shell:<shell>:<slot>}, the shell one of "+
|
"%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],
|
"%s, the session's file one of %s or the power moment one of %s, and the slot one of %s",
|
||||||
strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", "),
|
m.Module, r["id"], c[0], strings.Join(knownShells, ", "), strings.Join(sessionFiles, ", "),
|
||||||
strings.Join(knownSlots, ", ")))
|
strings.Join(powerMoments, ", "), strings.Join(knownSlots, ", ")))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
seat := placerOf(target)
|
seat := placerOf(target)
|
||||||
@@ -275,6 +285,13 @@ func placeholderProblems(m Manifest, r map[string]any) []string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
refusedFor[seat] = true
|
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 {
|
if seat == DisplayServerSeat {
|
||||||
problems = append(problems, fmt.Sprintf(
|
problems = append(problems, fmt.Sprintf(
|
||||||
"%s's resource %v names %s and %s does not claim %s; every module's %s is placed by "+
|
"%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.
|
// node may call; the holder places every module's shell code in its slots.
|
||||||
{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
{Name: LoginShellSeat, Scope: ScopeNode, Decision: "novox/hq ADR 0204",
|
||||||
Serves: loginShellVerbs()},
|
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
|
// 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.
|
// model change, not a rename, so it stays until that is built.
|
||||||
{Name: "the-private-network", Scope: ScopeNode, Decision: "novox/hq ADR 0110"},
|
{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
|
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-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
|
// 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.
|
// once the retired mesh-build-machine row goes, when no registered manifest claims it any more.
|
||||||
if len(Seats()) != 32 {
|
if len(Seats()) != 33 {
|
||||||
t.Errorf("the mesh defines %d seats rather than 32; the set is closed, so a change here is "+
|
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())
|
"a decision (novox/hq ADR 0110): %s", len(Seats()), seatNames())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user