mesh-grants: a node seat's event that names its holder's machine is heard from every machine, by the one rule its holder is granted it by (ADR 0618 §4, issue 774)
A watch of the node-engine's manifest-reads-changed subscribed the flat subject, which no engine says, so its consumer would hear nothing. mesh-sdk is pinned by a local replace to the group's head while developing. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -20,3 +20,5 @@ require (
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
)
|
||||
|
||||
replace git.novox.be/novox/mesh-sdk/go => ../../../0618-sdk/go
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103221822-85ba0a9a70d8 h1:XEbIVImp6yGjA91rp0iZBoDdL5KfamjOuDLwWvqh0hc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261103221822-85ba0a9a70d8/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -858,6 +858,8 @@ func healthChecks(t *testing.T) {
|
||||
{"the holder says no other seat's", selfCheck, "mesh.seat.mesh-conditions.event.condition-raised", false},
|
||||
{"a machine's event on a node seat is not composed here", Declared{Module: "pause", Holds: []Seat{{Name: "node-power",
|
||||
Scope: "node", Emits: []string{"paused.*"}}}}, "mesh.seat.node-power.event.paused.b", false},
|
||||
{"a flat event of a node seat held by a module is said flat", Declared{Module: "pause", Holds: []Seat{{Name: "node-power",
|
||||
Scope: "node", Emits: []string{"slept"}}}}, "mesh.seat.node-power.event.slept", true},
|
||||
} {
|
||||
m := MembershipFor("a", c.d, Placements{})
|
||||
listed := m.SeatTraffic != nil && slices.ContainsFunc(m.SeatTraffic.Publish, func(p string) bool { return p == c.subject })
|
||||
@@ -870,6 +872,28 @@ func healthChecks(t *testing.T) {
|
||||
MayPublish(perms, c.subject), c.may)
|
||||
}
|
||||
}
|
||||
// ADR 0618 §4: the node-engine says each of its seat's events with its own machine as the last token, and a watcher hears
|
||||
// that event from every machine, its consumer filtered the same way; a flat event of a node seat a module holds is
|
||||
// heard flat. One rule (machineEvent) places both sides.
|
||||
engine, err := PermissionsFor(Principal{Kind: KindNode, Node: "ace"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
watcher := Principal{Kind: KindModule, Node: "novox", Module: "mesh-delivery-assigner", Watches: []Seat{
|
||||
{Name: EngineSeat, Scope: "node", Emits: []string{"manifest-reads-changed"}, HeldByTheEngine: true},
|
||||
{Name: "node-power", Scope: "node", Emits: []string{"slept"}}}}
|
||||
heard, err := PermissionsFor(watcher)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
consumer, _ := ConsumerFor(watcher)
|
||||
said := "mesh.seat.node-engine.event.manifest-reads-changed.ace"
|
||||
if !MayPublish(engine, said) || MayPublish(engine, "mesh.seat.node-engine.event.manifest-reads-changed.novox") ||
|
||||
!MaySubscribe(heard, said) || !MaySubscribe(heard, "mesh.seat.node-power.event.slept") ||
|
||||
!slices.Contains(consumer.Filters, "mesh.seat.node-engine.event.manifest-reads-changed.*") {
|
||||
t.Errorf("the engine says %s: %v; the watcher hears it %v, the flat event %v; its consumer filters %v", said,
|
||||
MayPublish(engine, said), MaySubscribe(heard, said), MaySubscribe(heard, "mesh.seat.node-power.event.slept"), consumer.Filters)
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, u := range users {
|
||||
name := u.Username()
|
||||
|
||||
@@ -51,7 +51,9 @@ func EnginePackagesGrants(node string) []string {
|
||||
// principal may subscribe this seat's subjects of this node, and a caller is still granted each verb by name.
|
||||
func engineGrants(p Principal) (pub, sub []string) {
|
||||
sub = append(sub, "mesh.seat."+EngineSeat+".tool.*."+p.Node)
|
||||
pub = append(pub, "mesh.seat."+EngineSeat+".event.*."+p.Node)
|
||||
// Any event of the seat about its own node, by the one rule a watcher of it is composed by (machineEvent).
|
||||
every, _ := machineEvent(Seat{Name: EngineSeat, Scope: "node", HeldByTheEngine: true}, "*")
|
||||
pub = append(pub, seatSubject(Seat{Name: EngineSeat}, "event", onMachine(every, p.Node)))
|
||||
sub = append(sub, announcing(EngineSeat)...)
|
||||
pub = append(pub, discovering()...)
|
||||
tp, ts := seatTrafficGrants(SeatTrafficOf(asks.EngineAsker(p.Node), nil, p.Uses, nil))
|
||||
|
||||
@@ -73,6 +73,17 @@ func UserListFixtures() []UserListFixture {
|
||||
},
|
||||
RootFree: map[string]bool{"ace": true},
|
||||
}},
|
||||
// ADR 0618 §4: an event of the node-engine's seat names the engine's machine, so its watcher hears it from every
|
||||
// machine; a node seat held by a module says its other events flat, and is heard flat.
|
||||
{Name: "a node seat's events, by machine and flat", Records: Records{
|
||||
Nodes: []string{"ace", "novox"},
|
||||
Assigned: map[string][]Declared{
|
||||
"novox": {{Module: "mesh-delivery-assigner", Watches: []Seat{
|
||||
{Name: EngineSeat, Scope: "node", Emits: []string{"manifest-reads-changed"}, HeldByTheEngine: true},
|
||||
{Name: "node-power", Scope: "node", Emits: []string{"slept"}}}}},
|
||||
"ace": {{Module: "pause", Holds: []Seat{{Name: "node-power", Scope: "node", Emits: []string{"slept", "paused.*"}}}}},
|
||||
},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ type Seat struct {
|
||||
// PulledByKind says each accepted kind is a worker of its own on the seat's queue, filtered to that kind, which
|
||||
// every holder pulls in the order Accepts lists them: the build seats' (novox/hq ADR 0525 §2, ADR 0536 §4).
|
||||
PulledByKind bool
|
||||
// HeldByTheEngine says every node's engine holds the seat on its own account (catalogue.Seat.HeldByTheEngine, novox/hq
|
||||
// ADR 0563 §1): each of its events names the engine's machine as its last token (machineEvent).
|
||||
HeldByTheEngine bool
|
||||
}
|
||||
|
||||
// A Principal is one user of the bus. Its permissions are derived from what it declares and
|
||||
@@ -406,6 +409,25 @@ func NodeLoginsReads(node string) []string {
|
||||
// holder's machine (novox/hq ADR 0219): `paused.<node>`, the build agent saying whether it takes work.
|
||||
var perMachineEvents = map[string]bool{"paused.*": true}
|
||||
|
||||
// machineEvent is the one rule for where a node-scoped seat's event names its holder's machine as its last token (novox/hq
|
||||
// ADR 0219, ADR 0618 §4), as a pattern whose last token `*` is that machine, and whether e is such an event: every event
|
||||
// of a seat the node-engine holds (engineGrants), and each of perMachineEvents of any other node seat. The holder is
|
||||
// granted the pattern with its own machine in place of `*`, a watcher the pattern, so the two cannot differ.
|
||||
func machineEvent(s Seat, e string) (string, bool) {
|
||||
switch {
|
||||
case s.Scope != "node":
|
||||
return "", false
|
||||
case perMachineEvents[e]:
|
||||
return e, true
|
||||
case s.HeldByTheEngine:
|
||||
return e + ".*", true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// onMachine is a machineEvent pattern with node in place of its last `*`.
|
||||
func onMachine(pattern, node string) string { return strings.TrimSuffix(pattern, "*") + node }
|
||||
|
||||
// enrolmentPrefix is the space every enrolling node's user and inbox live under, so the one place the
|
||||
// controller may answer an enrolment is derived from the same constant the user is named from.
|
||||
const enrolmentPrefix = "enrol"
|
||||
@@ -879,6 +901,11 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
continue // composed by SeatTrafficOf below
|
||||
}
|
||||
for _, e := range w.Emits {
|
||||
// An event that names its holder's machine is heard from every machine (machineEvent): a flat subject would
|
||||
// match no event the holder says.
|
||||
if pattern, ok := machineEvent(w, e); ok {
|
||||
e = pattern
|
||||
}
|
||||
sub = append(sub, seatSubject(w, "event", e))
|
||||
}
|
||||
}
|
||||
@@ -921,8 +948,8 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
// **A machine says its own state and no other's** (novox/hq ADR 0219): on a node-scoped
|
||||
// seat, an event about the holder itself carries the machine as its last token, and
|
||||
// each holder is granted its own machine's alone.
|
||||
if s.Scope == "node" && p.Node != "" && perMachineEvents[e] {
|
||||
pub = append(pub, seatSubject(s, "event", strings.TrimSuffix(e, "*")+p.Node))
|
||||
if pattern, ok := machineEvent(s, e); ok && p.Node != "" {
|
||||
pub = append(pub, seatSubject(s, "event", onMachine(pattern, p.Node)))
|
||||
continue
|
||||
}
|
||||
pub = append(pub, seatSubject(s, "event", e))
|
||||
|
||||
@@ -64,7 +64,7 @@ func SeatTrafficOf(module string, holds, uses, watches []Seat) SeatTraffic {
|
||||
// runtime and the runtime is granted them. A machine's event on a node seat carries the machine, which
|
||||
// only the holder's own account is composed with, so it is left to that rule.
|
||||
for _, e := range s.Emits {
|
||||
if s.Scope == "node" && perMachineEvents[e] {
|
||||
if _, ok := machineEvent(s, e); ok {
|
||||
continue
|
||||
}
|
||||
t.Publish = append(t.Publish, seatSubject(s, "event", e))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -125,7 +125,10 @@ func declaredFor(m catalogue.Manifest, seats map[string]catalogue.SeatDeclaratio
|
||||
}
|
||||
}
|
||||
if s, isASeat := seats[emitter]; isASeat && catalogue.SeatSays(s.Emits, event) {
|
||||
watches = append(watches, buscompose.Seat{Name: s.Name, Emits: []string{event}})
|
||||
// Its scope, and whether the node-engine holds it, place an event that names its holder's machine (ADR 0618 §4).
|
||||
compiled, _ := catalogue.SeatNamed(s.Name)
|
||||
watches = append(watches, buscompose.Seat{Name: s.Name, Scope: s.Scope, Emits: []string{event},
|
||||
HeldByTheEngine: compiled.HeldByTheEngine})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user