mesh-grants reads placement alone and hears no controller event; this repository's manifests depend on the controller only by the lease
ADR 0549 §5, ADR 0564 §9, ADR 0571 §10: the part no longer reads mesh-controller.assignments (the union with placement goes; placement not read is a failed read that places nothing) nor consumes the controller's condition-raised and condition-cleared, the keeper's being the one source. ADR 0571 §13's test for this repository replaces the guard of ADR 0549 §6 and its list ../assignments-readers, now empty and deleted: at phase write no module.json here depends on mesh-controller beyond the lease and the three kept to the write (catalogue.ManifestRefusals). Tests: in TestGrants the fixture places through the seat's placement (assign, unassign), the subtest of the stale assignments becomes 774/S29/AC1 (placement alone gives the fixture's three memberships with no read, consume or call of the controller; the fixture is the suite's own, whose assignments named the same modules, not a copy of live memberships), and the assignments-readers subtest becomes 774/S24/AC1. No new test function. (issue 774, 774/S29, 774/S24) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
# The modules of this repository that still read the controller's assignments (mesh-controller.assignments), each with
|
||||
# why. mesh-grants' test (TestGrants, "assignments readers") refuses any other module.json that reads them, and an entry
|
||||
# here whose module no longer does: the list only shrinks, and it is empty in the first change after the controller's
|
||||
# lease is written to a holder that is no controller (novox/hq ADR 0549 §3, §6; issue 774). Each module named here
|
||||
# uses the assignments only while the controller holds the lease (buscompose.LeaseHandedAway), placement alone after it.
|
||||
mesh-delivery-assigner unioned with the placement until the lease write (assign.Placed with handedAway); placement alone after
|
||||
mesh-grants unioned with the placement until the lease write (ReadInputs); placement alone after
|
||||
@@ -319,8 +319,38 @@ func (b *fakeBus) refuse(module, raw, problem string) {
|
||||
Problems: []string{problem}})
|
||||
}
|
||||
|
||||
// assign places module on node in the seat's placement, the one place the part reads it (ADR 0549 §5; issue 774).
|
||||
func (b *fakeBus) assign(node, module string) {
|
||||
b.put(grants.ReadAssignments, node+"."+module, map[string]any{"node": node, "module": module, "assigned": "2026-10-25T20:00:00Z"})
|
||||
var pl delivery.Placement
|
||||
if raw, ok := b.states[grants.ReadPlacement]["placement."+node]; ok {
|
||||
pl, _ = delivery.DecodePlacement(raw)
|
||||
}
|
||||
pl.Node = node
|
||||
for _, m := range pl.Modules {
|
||||
if m.Module == module {
|
||||
return
|
||||
}
|
||||
}
|
||||
pl.Modules = append(pl.Modules, delivery.PlacedModule{Module: module})
|
||||
raw, err := delivery.EncodePlacement(pl)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if b.states[grants.ReadPlacement] == nil {
|
||||
b.states[grants.ReadPlacement] = map[string][]byte{}
|
||||
}
|
||||
b.states[grants.ReadPlacement]["placement."+node] = raw
|
||||
}
|
||||
|
||||
// unassign takes module off node in the seat's placement.
|
||||
func (b *fakeBus) unassign(node, module string) {
|
||||
pl, err := delivery.DecodePlacement(b.states[grants.ReadPlacement]["placement."+node])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pl.Modules = slices.DeleteFunc(pl.Modules, func(m delivery.PlacedModule) bool { return m.Module == module })
|
||||
raw, _ := delivery.EncodePlacement(pl)
|
||||
b.states[grants.ReadPlacement]["placement."+node] = raw
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -350,7 +380,6 @@ func fixture() *fakeBus {
|
||||
b.assign("a", "nats")
|
||||
b.assign("a", "mod-x")
|
||||
b.assign("b", "mod-y")
|
||||
b.put(grants.ReadAssignments, "b.gone", map[string]any{"node": "b", "module": "gone", "assigned": "2026-10-01T00:00:00Z", "ended": "2026-10-02T00:00:00Z"})
|
||||
b.put(grants.ReadHave, "a.node", map[string]any{"v": 1})
|
||||
b.put(grants.ReadHave, "b.node", map[string]any{"v": 1})
|
||||
b.put(grants.ReadHave, "a.nats", delivery.Have{V: delivery.Version, Phase: delivery.PhaseRunning, Health: delivery.HealthOK,
|
||||
@@ -512,8 +541,8 @@ func TestGrants(t *testing.T) {
|
||||
key, _ := delivery.BuilderBuildKey("mod-x", fp)
|
||||
b.put(grants.ReadBuilds, key, map[string]any{"v": delivery.Version, "state": "done", "manifest": digest})
|
||||
}, []string{"a.mod-x", "a.nats", "b.mod-y", "controller", "node.a", "node.b"}, "enrolment tokens"},
|
||||
{"placement refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadAssignments] = true },
|
||||
[]string{"controller", "node.a", "node.b"}, grants.ReadAssignments},
|
||||
{"placement refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadPlacement] = true },
|
||||
[]string{"controller", "node.a", "node.b"}, grants.ReadPlacement},
|
||||
} {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
b := fixture()
|
||||
@@ -1101,8 +1130,10 @@ func TestGrants(t *testing.T) {
|
||||
t.Errorf("read of the keys per module: placed %v, gaps %v", got.Placed, got.Gaps)
|
||||
}
|
||||
b.refused[grants.ReadPlacement] = true
|
||||
if read := grants.ReadInputs(b, &grants.Manifests{}); slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) {
|
||||
t.Errorf("placement not on the bus is a failed read: %v", read.Gaps)
|
||||
// The placement alone places (issue 774, 774/S29): not read, nothing is placed, and it is a failed read.
|
||||
if read := grants.ReadInputs(b, &grants.Manifests{}); !slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) ||
|
||||
len(read.Assigned) != 0 {
|
||||
t.Errorf("placement not on the bus is no failed read, or something is placed: %v, %v", read.Gaps, read.Assigned)
|
||||
}
|
||||
// Issue 1945, the user list of 2026-10-27 09:21: a placement's holds are the holders on record, not every holder.
|
||||
// A mesh seat no placement holds keeps the module claiming it as its holder, wherever placement puts it.
|
||||
@@ -1374,7 +1405,7 @@ func TestGrants(t *testing.T) {
|
||||
// settled is the placement of the lists agreeing, every hash held, and the loaded list as edit leaves it.
|
||||
settled := func(edit func(modX *grants.User)) func() {
|
||||
return func() {
|
||||
b.states[grants.ReadAssignments] = nil
|
||||
b.states[grants.ReadPlacement] = nil
|
||||
b.assign("a", "nats")
|
||||
b.assign("a", "mod-x")
|
||||
b.assign("b", "mod-y")
|
||||
@@ -1585,7 +1616,7 @@ func TestGrants(t *testing.T) {
|
||||
b.loaded[i].Subscribe = append(slices.Clone(b.loaded[i].Subscribe), seatGrant)
|
||||
}
|
||||
}
|
||||
b.put(grants.ReadAssignments, "a.mod-y", map[string]any{"node": "a", "module": "mod-y", "assigned": "2026-10-27T08:20:00Z"})
|
||||
b.assign("a", "mod-y")
|
||||
b.put(grants.ReadAccounts, "a.mod-y", map[string]any{"user": "a.mod-y", "hash": "$2a$10$x", "generation": 1})
|
||||
said = nil
|
||||
res := p.Compose()
|
||||
@@ -1607,7 +1638,7 @@ func TestGrants(t *testing.T) {
|
||||
b.refuseUsers = true
|
||||
b.mu.Unlock()
|
||||
b.mu.Lock()
|
||||
delete(b.states[grants.ReadAssignments], "a.mod-y")
|
||||
b.unassign("a", "mod-y")
|
||||
b.mu.Unlock()
|
||||
if res := p.Compose(); res.Sealed == nil || !strings.Contains(res.Sealed.Why, "the grants of the list in force cannot be read") {
|
||||
t.Errorf("a pass whose loaded list cannot be read = %+v", res.Sealed)
|
||||
@@ -2045,11 +2076,11 @@ func TestGrants(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan struct{})
|
||||
go func() { f.Run(ctx); close(done) }()
|
||||
waitOpened(t, w, grants.ReadAssignments)
|
||||
waitOpened(t, w, grants.ReadPlacement)
|
||||
if r := f.Refused(); r[grants.ReadModules] == "" {
|
||||
waitFor(t, func() bool { return f.Refused()[grants.ReadModules] != "" })
|
||||
}
|
||||
w.change(grants.ReadAssignments, "a.mod-x")
|
||||
w.change(grants.ReadPlacement, "placement.a")
|
||||
waitOpened(t, w, grants.ReadModules)
|
||||
waitFor(t, func() bool { return len(f.Refused()) == 0 })
|
||||
select {
|
||||
@@ -2242,83 +2273,64 @@ func TestGrants(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
// The guard of ADR 0549 §6 for this repository, as mesh-catalog's checks/placement is for the catalogue: no module.json
|
||||
// here reads the controller's assignments unless ../assignments-readers names it with why, and that list only shrinks.
|
||||
t.Run("assignments readers: only the named modules read the controller's assignments, and the list only shrinks", func(t *testing.T) {
|
||||
raw, err := os.ReadFile("../assignments-readers")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
allowed := map[string]bool{}
|
||||
for _, line := range strings.Split(string(raw), "\n") {
|
||||
if f := strings.Fields(line); len(f) > 1 && !strings.HasPrefix(f[0], "#") {
|
||||
allowed[f[0]] = true
|
||||
} else if len(f) == 1 {
|
||||
t.Errorf("../assignments-readers names %s with no why", f[0])
|
||||
}
|
||||
}
|
||||
// ADR 0571 §13's test for this repository (issue 774, 774/S24/AC1), replacing the guard of ADR 0549 §6 and its list
|
||||
// ../assignments-readers: at phase write no module.json here depends on mesh-controller beyond the lease and the three
|
||||
// kept to the write (mesh-sdk catalogue.ManifestRefusals, the judge grants_lease_check runs).
|
||||
t.Run("no module of this repository depends on the controller beyond what the lease write keeps", func(t *testing.T) {
|
||||
paths, _ := filepath.Glob("../*/module.json")
|
||||
reads := map[string]bool{}
|
||||
manifests := map[string]catalogue.Manifest{}
|
||||
for _, path := range paths {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var m struct {
|
||||
Module string `json:"module"`
|
||||
Reads []string `json:"reads"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
m, err := catalogue.Decode(raw)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", path, err)
|
||||
}
|
||||
if slices.Contains(m.Reads, grants.ReadAssignments) {
|
||||
reads[m.Module] = true
|
||||
if !allowed[m.Module] {
|
||||
t.Errorf("%s reads %s: read where modules run from the seat mesh-placement's placement (ADR 0549 §3); the lease write "+
|
||||
"is refused while a module reads the assignments (ADR 0549 §6)", m.Module, grants.ReadAssignments)
|
||||
}
|
||||
}
|
||||
manifests[m.Module] = m
|
||||
}
|
||||
for m := range allowed {
|
||||
if !reads[m] {
|
||||
t.Errorf("../assignments-readers names %s, which no longer reads %s: take it off the list", m, grants.ReadAssignments)
|
||||
}
|
||||
if len(manifests) < 8 {
|
||||
t.Fatalf("read %d manifests, want every module of the repository", len(manifests))
|
||||
}
|
||||
for _, why := range catalogue.ManifestRefusals(manifests, catalogue.PhaseWrite) {
|
||||
t.Error(why)
|
||||
}
|
||||
})
|
||||
|
||||
// 774 (the L3 review, ADR 0549 §3, §6): once the controller's lease is handed away the assignments are frozen and place
|
||||
// nothing; the seat's placement alone does. A module left only in the stale assignments loses its account and its
|
||||
// grant names no account of it; before the lease write the two are still unioned.
|
||||
t.Run("placement alone once the lease is handed away: a module only in the stale assignments has no account or grant of it", func(t *testing.T) {
|
||||
// 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the placement alone, with no read of the
|
||||
// controller, are the ones the union of placement and assignments gave on the same input: the fixture's (two nodes,
|
||||
// the bus and mod-x on a, mod-y on b, as placed on the live mesh's own shape of nats beside its modules), whose
|
||||
// assignments named the same modules. Neither the controller's assignments nor its condition events are read.
|
||||
t.Run("placement alone places, with the memberships the union gave", func(t *testing.T) {
|
||||
b := fixture()
|
||||
b.loaded = shadowOf(t, b)
|
||||
p := partOn(b)
|
||||
p.Bus = grants.BusAddress{URL: "nats://a.mesh:4222", Fingerprint: "ab"}
|
||||
pl, _ := delivery.EncodePlacement(delivery.Placement{Node: "a", Modules: []delivery.PlacedModule{{Module: "nats"}, {Module: "mod-x"}}})
|
||||
b.states[grants.ReadPlacement] = map[string][]byte{"placement.a": pl}
|
||||
for _, u := range []string{"a.mod-x", "a.nats", "b.mod-y"} {
|
||||
b.put(grants.ReadAccounts, u, map[string]any{"user": u, "hash": "$2a$10$x", "generation": 1})
|
||||
}
|
||||
fp := "src2:" + strings.Repeat("ab", 32)
|
||||
key, _ := delivery.PartGrantKey("mod-y", fp)
|
||||
grantOf := func() []string {
|
||||
g, _ := delivery.DecodeControllerGrant(b.own[grants.StateGrants][key])
|
||||
return g.Accounts
|
||||
}
|
||||
b.put(grants.ReadLease, grants.LeaseKey, map[string]any{"instance": "novox/1/2026-10-28", "epoch": 60})
|
||||
p.Compose()
|
||||
if b.own[grants.StateAccounts]["b.mod-y"] == nil || !slices.Equal(grantOf(), []string{"b.mod-y"}) {
|
||||
t.Fatalf("under the controller's lease the assignments no longer count: account %v, grant %v",
|
||||
b.own[grants.StateAccounts]["b.mod-y"] != nil, grantOf())
|
||||
want := map[string]string{"a.nats": "", "a.mod-x": "", "b.mod-y": ""}
|
||||
for key := range want {
|
||||
raw, _, found, _ := b.ownGet(grants.StateMemberships, key)
|
||||
if !found {
|
||||
t.Fatalf("no membership of %s from the placement", key)
|
||||
}
|
||||
want[key] = string(raw)
|
||||
}
|
||||
b.put(grants.ReadLease, grants.LeaseKey, map[string]any{"handed-to": "the operator's hand-over"})
|
||||
in := grants.ReadInputs(b, &grants.Manifests{})
|
||||
if len(in.Assigned["b"]) != 0 || !slices.Equal(in.Assigned["a"], []string{"mod-x", "nats"}) || in.BusNode != "a" {
|
||||
t.Errorf("after the lease write the stale assignments still place: %v, bus %q", in.Assigned, in.BusNode)
|
||||
if got := slices.Sorted(maps.Keys(b.own[grants.StateMemberships])); !slices.Equal(got, []string{"a.mod-x", "a.nats", "b.mod-y"}) {
|
||||
t.Errorf("memberships %v", got)
|
||||
}
|
||||
res := p.Compose()
|
||||
if res.Accounts == nil || !slices.Equal(res.Accounts.Deleted, []string{"b.mod-y"}) || slices.Contains(grantOf(), "b.mod-y") {
|
||||
t.Fatalf("a module only in the stale assignments kept its account or grant: %+v, grant %v", res.Accounts, grantOf())
|
||||
for _, r := range grants.Reads {
|
||||
if strings.HasPrefix(r, "mesh-controller.") && r != grants.ReadLease {
|
||||
t.Errorf("the part reads %s", r)
|
||||
}
|
||||
}
|
||||
for _, c := range grants.Consumes {
|
||||
if strings.HasPrefix(c, "mesh-controller.") {
|
||||
t.Errorf("the part consumes %s", c)
|
||||
}
|
||||
}
|
||||
for _, c := range b.calls {
|
||||
if strings.Contains(c, "mesh-controller") {
|
||||
t.Errorf("the part called %s", c)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2360,7 +2372,7 @@ func TestGrants(t *testing.T) {
|
||||
t.Errorf("the wanted accounts are read by %v", readers)
|
||||
}
|
||||
// A read that fails retires nothing; once read, the user placement dropped is retired.
|
||||
delete(b.states[grants.ReadAssignments], "b.mod-y")
|
||||
b.unassign("b", "mod-y")
|
||||
b.refused[grants.ReadResolved] = true
|
||||
if res := p.Compose(); res.Accounts == nil || len(res.Accounts.Deleted) != 0 || b.own[grants.StateAccounts]["b.mod-y"] == nil {
|
||||
t.Fatalf("a pass whose read failed deleted %+v", res.Accounts)
|
||||
|
||||
@@ -22,7 +22,7 @@ type Watcher interface {
|
||||
// wants (the manifest each machine is wanted to run), and the vault's hashes of the bus accounts.
|
||||
//
|
||||
// And its own state lease, whose key removal-due's expiry marker starts the day-later removal (ADR 0582 clause 6).
|
||||
var Followed = []string{ReadAssignments, ReadModules, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadPlacement, ReadLease, ReadTakeover,
|
||||
var Followed = []string{ReadModules, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadPlacement, ReadLease, ReadTakeover,
|
||||
OwnLease}
|
||||
|
||||
// OwnLease is the part's own state lease as the follower watches it, `<module>.<state>`.
|
||||
|
||||
@@ -24,13 +24,12 @@ import (
|
||||
|
||||
// The states of other parts the grants part reads, as its manifest's reads name them (`<module>.<state>`).
|
||||
const (
|
||||
ReadAssignments = "mesh-controller.assignments"
|
||||
ReadModules = "mesh-delivery-indexer.modules"
|
||||
ReadManifests = "mesh-delivery-indexer.manifests"
|
||||
ReadHave = "node-engine.have"
|
||||
ReadResolved = "mesh-config.resolved"
|
||||
ReadWants = "mesh-delivery-assigner.wants"
|
||||
ReadBuilds = "mesh-delivery-builder.builds"
|
||||
ReadModules = "mesh-delivery-indexer.modules"
|
||||
ReadManifests = "mesh-delivery-indexer.manifests"
|
||||
ReadHave = "node-engine.have"
|
||||
ReadResolved = "mesh-config.resolved"
|
||||
ReadWants = "mesh-delivery-assigner.wants"
|
||||
ReadBuilds = "mesh-delivery-builder.builds"
|
||||
// ReadAccounts is the vault's bcrypt hashes of the bus accounts it holds, by bus user (ADR 0547 clause 9).
|
||||
ReadAccounts = "mesh-vault.bus-accounts"
|
||||
// ReadPlacement is the seat mesh-placement's placement (ADR 0567 §1, §9): each node's modules and the seats
|
||||
@@ -38,10 +37,9 @@ const (
|
||||
ReadPlacement = delivery.PlacementRead
|
||||
)
|
||||
|
||||
// Reads is every state the part reads, in the manifest's order. ReadAssignments stays (and in Followed) only while the
|
||||
// controller holds the lease, when the assignments are unioned with the placement (ReadInputs); after the lease write
|
||||
// they place nothing, and the first change after it takes them out (../assignments-readers names the part until then).
|
||||
var Reads = []string{ReadAssignments, ReadModules, ReadManifests, ReadResolved, ReadHave, ReadWants, ReadBuilds,
|
||||
// Reads is every state the part reads, in the manifest's order. Not the controller's assignments: the seat's placement
|
||||
// alone places modules (novox/hq ADR 0549 §5, ADR 0571 §10; issue 774).
|
||||
var Reads = []string{ReadModules, ReadManifests, ReadResolved, ReadHave, ReadWants, ReadBuilds,
|
||||
ReadAccounts, ReadPlacement, ReadLease, ReadTakeover}
|
||||
|
||||
// The bus module's tools the part calls, as its manifest's invokes name them: read-only, each.
|
||||
@@ -83,18 +81,6 @@ var standingGaps = []Gap{
|
||||
{Input: "announced states", Why: "the controller does not announce its states-announced readable, so every read is composed from `reads`, as when announcements cannot be read"},
|
||||
}
|
||||
|
||||
// assignment is a key of the controller's assignments as composing needs it (mesh-controller internal/chain Assignment):
|
||||
// a value that names no time assigned (a module pulled in), has ended, or waits in a state, is no assignment.
|
||||
type assignment struct {
|
||||
Node string `json:"node"`
|
||||
Module string `json:"module"`
|
||||
Assigned time.Time `json:"assigned,omitzero"`
|
||||
Ended time.Time `json:"ended,omitzero"`
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
func (a assignment) stands() bool { return !a.Assigned.IsZero() && a.Ended.IsZero() && a.State == "" }
|
||||
|
||||
// resolved is a key of mesh-config's resolved values, `<node>.<module>.<key>`.
|
||||
type resolved struct {
|
||||
Node string `json:"node"`
|
||||
@@ -105,10 +91,9 @@ type resolved struct {
|
||||
|
||||
// Inputs is what the shadow composes from, read from the bus.
|
||||
type Inputs struct {
|
||||
// Nodes is every machine seen: in a node agent's have or a standing assignment.
|
||||
// Nodes is every machine seen: in a node agent's have or the placement.
|
||||
Nodes []string
|
||||
// Assigned is each machine's standing assignments and what the seat's placement puts there, sorted: while both
|
||||
// stand (ADR 0549 §5) a module either names keeps its membership.
|
||||
// Assigned is what the seat's placement puts on each machine, sorted (issue 774: never the controller's assignments).
|
||||
Assigned map[string][]string
|
||||
// Placed is each node's placement as the seat mesh-placement holds it (ADR 0567 §1); empty before any is read.
|
||||
// Where it is, a seat held once for the mesh is held where placement says, not wherever a module claiming it runs.
|
||||
@@ -248,26 +233,11 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
NoFingerprint: map[string]string{}, Running: map[string]catalogue.Manifest{},
|
||||
Values: map[string]any{}, Gaps: append([]Gap(nil), standingGaps...)}
|
||||
nodes := map[string]bool{}
|
||||
// **After the lease write the assignments place nothing** (novox/hq ADR 0549 §3, §6): they are frozen then, so a
|
||||
// module the catalogue takes off a node would keep its account, grant, membership and user-list entry. Once the lease
|
||||
// is handed away and the placement is read, the placement alone places modules, as the assigner's Placed does with
|
||||
// the same lease reading (buscompose.LeaseHandedAway); before, or with no placement on the bus, the two are unioned.
|
||||
byAssignment, busByAssignment := map[string][]string{}, ""
|
||||
for _, raw := range each(r, ReadAssignments, &in.Gaps) {
|
||||
var a assignment
|
||||
if json.Unmarshal(raw, &a) != nil || !a.stands() || a.Node == "" || a.Module == "" {
|
||||
continue
|
||||
}
|
||||
byAssignment[a.Node] = append(byAssignment[a.Node], a.Module)
|
||||
if a.Module == BusModule {
|
||||
busByAssignment = a.Node
|
||||
}
|
||||
}
|
||||
// The seat's placement (ADR 0567 §9): what it places is composed beside the assignments, and the seats held once
|
||||
// for the mesh are held where it says. Not on the bus yet is a standing gap, never a failed read, so the list is still
|
||||
// composed and sealed from the assignments alone.
|
||||
// **The seat's placement alone places modules** (novox/hq ADR 0549 §5, ADR 0567 §9; issue 774): the controller's
|
||||
// assignments are read no more, and the seats held once for the mesh are held where it says. Not read is a failed
|
||||
// read: nothing is placed, so after take-over the list composed past it is not sealed.
|
||||
if keys, err := r.Keys(ReadPlacement); err != nil {
|
||||
in.Gaps = append(in.Gaps, Gap{Input: ReadPlacement, Why: "not read yet: the assignments alone place modules (" + err.Error() + ")"})
|
||||
in.Gaps = append(in.Gaps, Gap{Input: ReadPlacement, Why: "refused or absent: nothing is placed (" + err.Error() + ")", Failed: true})
|
||||
} else {
|
||||
// Each node's keys in either form, assembled by the seat's one reader (delivery.PlacementsOf; research 072
|
||||
// row 37): its node key and a key per module, or its legacy key `placement.<node>`.
|
||||
@@ -294,17 +264,6 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
in.Placed[node] = p
|
||||
}
|
||||
}
|
||||
away, _ := leaseHandedAway(r)
|
||||
if away && len(in.Placed) > 0 {
|
||||
in.Gaps = append(in.Gaps, Gap{Input: ReadAssignments, Why: "not used: the controller's lease is handed away, " +
|
||||
"so the seat's placement alone places modules (ADR 0549 §3, §6)"})
|
||||
} else {
|
||||
for node, ms := range byAssignment {
|
||||
nodes[node] = true
|
||||
in.Assigned[node] = append(in.Assigned[node], ms...)
|
||||
}
|
||||
in.BusNode = busByAssignment
|
||||
}
|
||||
for node, p := range in.Placed {
|
||||
nodes[node] = true
|
||||
for _, m := range p.Assigned() {
|
||||
@@ -328,7 +287,7 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
nodes[node] = true
|
||||
}
|
||||
}
|
||||
in.Gaps = append(in.Gaps, Gap{Input: "machines", Why: "read from the node agents' haves and the assignments: a machine with neither is not seen"})
|
||||
in.Gaps = append(in.Gaps, Gap{Input: "machines", Why: "read from the node agents' haves and the placement: a machine with neither is not seen"})
|
||||
for n := range nodes {
|
||||
in.Nodes = append(in.Nodes, n)
|
||||
}
|
||||
|
||||
@@ -49,8 +49,9 @@ func HeardFrom(event string) string {
|
||||
const EventAccountIssued = "mesh-vault." + buscompose.AccountIssuedEvent
|
||||
|
||||
// Consumes is every event the part hears, as its manifest's consumes name them.
|
||||
var Consumes = []string{EventConditionRaised, EventConditionCleared, EventKeeperRaised, EventKeeperCleared, EventAccountIssued,
|
||||
EventBucketExpirySet}
|
||||
//
|
||||
// Not the controller's condition events: the keeper's alone (novox/hq ADR 0564 §9, issue 774).
|
||||
var Consumes = []string{EventKeeperRaised, EventKeeperCleared, EventAccountIssued, EventBucketExpirySet}
|
||||
|
||||
// IsAccountIssued says an event heard is the vault's account-issued, by its name or its bus subject.
|
||||
func IsAccountIssued(event string) bool {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
],
|
||||
"reads": [
|
||||
"mesh-config.resolved",
|
||||
"mesh-controller.assignments",
|
||||
"mesh-delivery-assigner.wants",
|
||||
"mesh-delivery-builder.builds",
|
||||
"mesh-delivery-indexer.manifests",
|
||||
@@ -26,8 +25,6 @@
|
||||
"mesh-conditions.takeover"
|
||||
],
|
||||
"consumes": [
|
||||
"mesh-controller.condition-raised",
|
||||
"mesh-controller.condition-cleared",
|
||||
"mesh-conditions.condition-raised",
|
||||
"mesh-conditions.condition-cleared",
|
||||
"mesh-vault.account-issued",
|
||||
|
||||
Reference in New Issue
Block a user