Merge branch 'feat/47-G-fix9-routeproxy' into feat/47-G-join9
This commit is contained in:
@@ -5529,6 +5529,19 @@ func StateProblems(m catalogue.Manifest) []string {
|
||||
}
|
||||
}
|
||||
for _, r := range m.Reads {
|
||||
if seat, state, isSeat, err := catalogue.SeatRead(r); isSeat {
|
||||
// A read of a seat holder's state (design 47 G A-116, A-128): the seat declares the state; the holder is resolved
|
||||
// where it is read.
|
||||
switch s, known := catalogue.SeatNamed(seat); {
|
||||
case err != nil:
|
||||
problems = append(problems, fmt.Sprintf("%s reads %v", m.Module, err))
|
||||
case !known:
|
||||
problems = append(problems, fmt.Sprintf("%s reads %q, a state of the seat %q, "+catalogue.UndeclaredSeat, m.Module, r, seat))
|
||||
case !slices.Contains(s.States, state):
|
||||
problems = append(problems, fmt.Sprintf("%s reads %q, and the seat %s declares no state %q (design 47 G A-128)", m.Module, r, seat, state))
|
||||
}
|
||||
continue
|
||||
}
|
||||
module, _, err := catalogue.ReadState(r)
|
||||
if err != nil {
|
||||
problems = append(problems, fmt.Sprintf("%s reads %v", m.Module, err))
|
||||
|
||||
@@ -6416,3 +6416,22 @@ func TestRulesOther(t *testing.T) {
|
||||
t.Run("TakesSettingsIsRefusedWhereItCannotMeanAnything", caseTakesSettingsIsRefusedWhereItCannotMeanAnything)
|
||||
t.Run("TheInstallationCheckReadsADefault", caseTheInstallationCheckReadsADefault)
|
||||
}
|
||||
|
||||
// A read of a seat holder's state, `seat:<seat>.<state>`, is accepted where the seat declares the state and refused, by name,
|
||||
// where it does not or the seat is unknown (design 47 G A-116, A-128).
|
||||
func TestASeatNamedReadIsJudgedAgainstTheSeatsStates(t *testing.T) {
|
||||
was := catalogue.Seats()
|
||||
t.Cleanup(func() { catalogue.UseSeats(was) })
|
||||
catalogue.UseSeats([]catalogue.Seat{{Name: "mesh-provisioner", Scope: catalogue.ScopeMesh, Decision: "test", States: []string{"plans"}}})
|
||||
for _, row := range []struct{ read, says string }{
|
||||
{"seat:mesh-provisioner.plans", ""},
|
||||
{"seat:mesh-provisioner.nothing", `declares no state "nothing"`},
|
||||
{"seat:mesh-unknown.plans", "mesh-unknown"},
|
||||
{"seat:mesh-provisioner", "does not name a seat and its state"},
|
||||
} {
|
||||
problems := strings.Join(StateProblems(catalogue.Manifest{Module: "reader", Reads: []string{row.read}}), "\n")
|
||||
if row.says == "" && problems != "" || row.says != "" && !strings.Contains(problems, row.says) {
|
||||
t.Errorf("%s: problems %q, want %q", row.read, problems, row.says)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -213,6 +214,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