Fix 9.7a: the grants part resolves a read naming a seat to its holder
Builds: design 47 G A-116 (§636, §616) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -4964,6 +4964,39 @@ func TestTheGrantsPartKeepsWhatEachNodeRuns(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A module reading a seat holder's state, `seat:<seat>.<state>`, is granted the read of the module holding the seat on record
|
||||
// (design 47 G A-116).
|
||||
func TestASeatNamedReadIsResolvedToItsHolder(t *testing.T) {
|
||||
for _, row := range []struct {
|
||||
name, holder string
|
||||
reads, want []string
|
||||
}{
|
||||
{"resolved to the holder on record", "prov-one", []string{"seat:mesh-provisioner.plans", "mesh-vault.node-logins"},
|
||||
[]string{"prov-one.plans", "mesh-vault.node-logins"}},
|
||||
{"a new holder is read with no change of the manifest", "prov-two", []string{"seat:mesh-provisioner.provisions"},
|
||||
[]string{"prov-two.provisions"}},
|
||||
{"no holder on record stays as written", "", []string{"seat:mesh-provisioner.plans"}, []string{"seat:mesh-provisioner.plans"}},
|
||||
} {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
in := grants.Inputs{Nodes: []string{"a"}, Assigned: map[string][]string{"a": {"mod-t"}},
|
||||
Manifests: map[string]catalogue.Manifest{"mod-t": {Module: "mod-t", Version: "1", RunsAs: "mod-t", Reads: row.reads,
|
||||
OwnSecrets: catalogue.OwnSecrets{"broker": {Path: "/var/lib/mod-t/broker"}}}}}
|
||||
if row.holder != "" {
|
||||
in.Placed = map[string]meshmoduleassigner.Assignments{"a": {Node: "a",
|
||||
Holds: []meshmoduleassigner.Holding{{Claim: "mesh-provisioner", Scope: "mesh", Module: row.holder}}}}
|
||||
}
|
||||
recs, _ := grants.ListRecords(in)
|
||||
var got []string
|
||||
for _, d := range recs.Assigned["a"] {
|
||||
got = d.Reads
|
||||
}
|
||||
if !slices.Equal(got, row.want) {
|
||||
t.Errorf("reads = %v, want %v", got, row.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A module naming a seat's view, `seat:<seat>.views.<state>`, is granted the view of the module holding the seat on record, so a
|
||||
// new holder is granted with no change of its manifest (design 47 G §616).
|
||||
func TestASeatNamedViewIsResolvedToItsHolder(t *testing.T) {
|
||||
|
||||
@@ -88,6 +88,7 @@ func records(in Inputs, union bool) (buscompose.Records, []Gap) {
|
||||
}
|
||||
declare := func(m catalogue.Manifest) buscompose.Declared {
|
||||
m.Consumes = resolveSeatViews(m.Consumes, holders)
|
||||
m.Reads = resolveSeatReads(m.Reads, holders)
|
||||
d := declaredFor(m, seats, declarers)
|
||||
if len(in.Placed) > 0 {
|
||||
d.Holds = heldWherePlaced(d.Holds, holders, node, m.Module)
|
||||
@@ -212,6 +213,36 @@ func resolveSeatViews(consumes []string, holders []catalogue.Held) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// resolveSeatReads resolves each `seat:<seat>.<state>` of a module's reads to `<holder>.<state>`, the module holding the seat
|
||||
// on record, as resolveSeatViews does a view: a new holder is read with no change of the manifest (design 47 G A-116). An entry
|
||||
// whose seat has no holder on record stays as written, and grants nothing. Pure.
|
||||
func resolveSeatReads(reads []string, holders []catalogue.Held) []string {
|
||||
var out []string
|
||||
for i, r := range reads {
|
||||
rest, isSeat := strings.CutPrefix(r, seatViewConsume)
|
||||
seatName, state, named := strings.Cut(rest, ".")
|
||||
if !isSeat || !named || state == "" || strings.Contains(state, ".") {
|
||||
if out != nil {
|
||||
out = append(out, r)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if out == nil {
|
||||
out = slices.Clone(reads[:i])
|
||||
}
|
||||
holder := slices.IndexFunc(holders, func(h catalogue.Held) bool { return sameSeat(h.Claim, seatName) })
|
||||
if holder < 0 {
|
||||
out = append(out, r)
|
||||
continue
|
||||
}
|
||||
out = append(out, holders[holder].Module+"."+state)
|
||||
}
|
||||
if out == nil {
|
||||
return reads
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// consumedOf splits what a manifest consumes as the composer grants it: a seat's event on the seat's own subject (its
|
||||
// watches), every other by its emitter's (novox/hq ADR 0118). A placed module and the node-engine's own manifest are split
|
||||
// alike.
|
||||
|
||||
Reference in New Issue
Block a user