Files
mesh-catalog/modules/mesh-vault/cmd/mesh-vault/placed.go
T

164 lines
5.6 KiB
Go

package main
// Where modules stand, from the placement (novox/hq ADR 0549 §3, §5; ADR 0561; issue 774), the seat mesh-placement's
// state held by the assigner (ADR 0567). The controller's assignments mirror goes with its lease, so the vault reads which
// module stands on which node from the seat's placement first, and the assignments beside it while both stand: a module either names stands. The provisioner's plan,
// which only an assignment's value carries, is read from the assignments still: its home after the lease is a subtask of
// its own (issue 774).
import (
"errors"
"fmt"
"slices"
"sort"
"strings"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// PlacementRead is the seat mesh-placement's placement, as the vault's manifest reads it (mesh-sdk's contract).
const PlacementRead = delivery.PlacementRead
// PlacementReader reads the seat's placement: every key, and one key's value (nil when there is none).
type PlacementReader struct {
Keys func() ([]string, error)
Read func(key string) ([]byte, error)
}
// placements is every node's placement, by node, from the state's keys in either form (delivery.PlacementsOf; research
// 072 row 37); none when the vault reads no placement. A node whose keys do not read whole is read as they stand.
func (v *Vault) placements() (map[string]delivery.Placement, error) {
out := map[string]delivery.Placement{}
if v.Placement.Keys == nil || v.Placement.Read == nil {
return out, nil
}
keys, err := v.Placement.Keys()
if err != nil {
return nil, fmt.Errorf("the placement's keys could not be read: %w", err)
}
values := map[string][]byte{}
for _, k := range keys {
if _, _, _, err := delivery.ParsePlacementKey(k); err != nil {
continue
}
raw, err := v.Placement.Read(k)
if err != nil {
return nil, fmt.Errorf("the placement's key %s could not be read: %w", k, err)
}
if raw != nil {
values[k] = raw
}
}
out, _ = delivery.PlacementsOf(values)
return out, nil
}
// nodePlaced is what node's own key of the placement 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), or, for a node not
// converted yet, from its key in the form before, `placement.<node>`. found is false when it has neither.
func (v *Vault) nodePlaced(node string) (modules []string, decided delivery.NodeDecided, found bool, err error) {
key, err := delivery.PlacementNodeKey(node)
if err != nil {
return nil, decided, false, err
}
raw, err := v.Placement.Read(key)
if err != nil {
return nil, decided, false, err
}
if raw != nil {
n, err := delivery.DecodePlacementNode(raw)
if err != nil {
return nil, decided, false, fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
for _, r := range n.Modules {
modules = append(modules, r.Module)
}
return modules, n.Decided, true, nil
}
legacy, err := delivery.LegacyPlacementKey(node)
if err != nil {
return nil, decided, false, err
}
if raw, err = v.Placement.Read(legacy); err != nil || raw == nil {
return nil, decided, false, err
}
p, err := delivery.DecodePlacement(raw)
if err != nil {
return nil, decided, false, fmt.Errorf("the placement of %s is not readable: %w", node, err)
}
return p.Assigned(), p.Decided, true, 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 placement
// (nodePlaced); none when the vault reads no placement or the node has no key.
func (v *Vault) placedModules(node string) ([]string, error) {
if v.Placement.Read == nil {
return nil, nil
}
modules, _, _, err := v.nodePlaced(node)
return modules, err
}
// standsOn says module stands on node: placed there, or assigned there while the assignments stand. 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.Placement.Read == nil {
return false, errors.New("the vault reads neither the placement nor the assignments")
}
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 placement, and from the assignments while no placement names
// one. The vault adopts from one node alone.
func (v *Vault) busNode() (string, error) {
placed, err := v.placements()
if err != nil {
return "", err
}
var nodes []string
for node, p := range placed {
if slices.Contains(p.Assigned(), 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 assignments' 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 placement nor the assignments, 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, ", "))
}