The legacy key form placement.<node> is gone from the client, so the readers and their fixtures lose it. The check noplacement now also refuses the assigner's retired names in a manifest. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
150 lines
5.2 KiB
Go
150 lines
5.2 KiB
Go
package main
|
|
|
|
// Where modules stand, from the seat mesh-module-assigner's state assignments (novox/hq ADR 0549 §3, §5; ADR 0561;
|
|
// issue 774), held by the assigner (ADR 0567). The controller's mirror of bindings (Vault.Assignments) goes with its lease,
|
|
// so the vault reads which module stands on which node from the seat's assignments first, and the mirror beside it while
|
|
// both stand: a module either names stands. The provisioner's plan, which only a binding's value carries, is read from
|
|
// the mirror still: its home after the lease is a subtask of its own (issue 774).
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
assignerclient "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient"
|
|
placing "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// AssignerReader reads the seat's assignments: every key, and one key's value (nil when there is none).
|
|
type AssignerReader struct {
|
|
Keys func() ([]string, error)
|
|
Read func(key string) ([]byte, error)
|
|
}
|
|
|
|
// assignments is every node's assignments, by node, from the state's keys (assignerclient.AssignmentsOf; research
|
|
// 072 row 37); none when the vault reads no assignments. A node whose keys do not read whole is read as they stand.
|
|
func (v *Vault) assignments() (map[string]placing.Assignments, error) {
|
|
out := map[string]placing.Assignments{}
|
|
if v.Assigner.Keys == nil || v.Assigner.Read == nil {
|
|
return out, nil
|
|
}
|
|
keys, err := v.Assigner.Keys()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the mirror's keys could not be read: %w", err)
|
|
}
|
|
values := map[string][]byte{}
|
|
for _, k := range keys {
|
|
if _, _, _, err := assignerclient.ParseKey(k); err != nil {
|
|
continue
|
|
}
|
|
raw, err := v.Assigner.Read(k)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the assignments' key %s could not be read: %w", k, err)
|
|
}
|
|
if raw != nil {
|
|
values[k] = raw
|
|
}
|
|
}
|
|
out, _ = assignerclient.AssignmentsOf(values)
|
|
return out, nil
|
|
}
|
|
|
|
// nodePlaced is what node's own key of the assignments says: the modules placed there and what a person decided of it,
|
|
// read from its node key `<node>.node` alone (which names every module placed, research 072 row 37). found is false when
|
|
// it has none.
|
|
func (v *Vault) nodePlaced(node string) (modules []string, decided placing.NodeDecided, found bool, err error) {
|
|
key, err := assignerclient.NodeKey(node)
|
|
if err != nil {
|
|
return nil, decided, false, err
|
|
}
|
|
raw, err := v.Assigner.Read(key)
|
|
if err != nil {
|
|
return nil, decided, false, err
|
|
}
|
|
if raw != nil {
|
|
n, err := assignerclient.DecodeNode(raw)
|
|
if err != nil {
|
|
return nil, decided, false, fmt.Errorf("the assignments of %s are not readable: %w", node, err)
|
|
}
|
|
for _, r := range n.Modules {
|
|
modules = append(modules, r.Module)
|
|
}
|
|
return modules, n.Decided, true, nil
|
|
}
|
|
return nil, decided, false, nil
|
|
}
|
|
|
|
// placedOn says module is placed on node by the catalogue.
|
|
func (v *Vault) placedOn(node, module string) (bool, error) {
|
|
placed, err := v.placedModules(node)
|
|
return slices.Contains(placed, module), err
|
|
}
|
|
|
|
// placedModules is every module the catalogue places on node, read from the node's own key of the assignments
|
|
// (nodePlaced); none when the vault reads no assignments or the node has no key.
|
|
func (v *Vault) placedModules(node string) ([]string, error) {
|
|
if v.Assigner.Read == nil {
|
|
return nil, nil
|
|
}
|
|
modules, _, _, err := v.nodePlaced(node)
|
|
return modules, err
|
|
}
|
|
|
|
// standsOn says module stands on node: placed there, or bound there while the mirror stands. A read that fails is
|
|
// an error, never "not there".
|
|
func (v *Vault) standsOn(node, module string) (bool, error) {
|
|
placed, err := v.placedOn(node, module)
|
|
if err != nil || placed {
|
|
return placed, err
|
|
}
|
|
d, ok := v.Askers.(Declared)
|
|
if !ok || d.Read == nil {
|
|
if v.Assigner.Read == nil {
|
|
return false, errors.New("the vault reads neither the seat's assignments nor the mirror")
|
|
}
|
|
return false, nil
|
|
}
|
|
raw, err := standing(d.Read)(node + "." + module)
|
|
return raw != nil, err
|
|
}
|
|
|
|
// busNode is the node running the bus's module: from the seat's assignments, and from the mirror while no assignment names
|
|
// one. The vault adopts from one node alone.
|
|
func (v *Vault) busNode() (string, error) {
|
|
placed, err := v.assignments()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var nodes []string
|
|
for node, p := range placed {
|
|
if slices.Contains(assignerclient.Assigned(p), BusModule) {
|
|
nodes = append(nodes, node)
|
|
}
|
|
}
|
|
from := "placed"
|
|
if len(nodes) == 0 && v.Assignments.Keys != nil {
|
|
from = "assigned"
|
|
keys, err := v.Assignments.Keys()
|
|
if err != nil {
|
|
return "", fmt.Errorf("the mirror's keys could not be read: %w", err)
|
|
}
|
|
for _, k := range keys {
|
|
if node, module, ok := strings.Cut(k, "."); ok && module == BusModule {
|
|
nodes = append(nodes, node)
|
|
}
|
|
}
|
|
} else if len(nodes) == 0 && len(placed) == 0 {
|
|
return "", errors.New("the vault reads neither the seat's assignments nor the mirror, so it cannot tell which node holds the bus")
|
|
}
|
|
sort.Strings(nodes)
|
|
switch len(nodes) {
|
|
case 1:
|
|
return nodes[0], nil
|
|
case 0:
|
|
return "", fmt.Errorf("no node is %s the bus module %s", from, BusModule)
|
|
}
|
|
return "", fmt.Errorf("%d nodes are %s the bus module %s (%s): the vault adopts from one alone", len(nodes), from,
|
|
BusModule, strings.Join(nodes, ", "))
|
|
}
|