The seats, holdings, source, build-source, delivery group check, check request, backup plan, adoption (take and converge) and the network graph now read the rest of the mesh through restOfTheMesh and compose.Graph. compose_callers_test.go scans the package and names any function other than the old composition's own that still calls planFor, declarationWith, composedAndValidated, generators, renderingFor, theRestOfTheMesh or whoResolves; issue 1389 deletes those.
164 lines
5.6 KiB
Go
164 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/overlay"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// recordDerivedHolders writes down who holds each mesh-scoped seat that nobody was ever recorded
|
|
// as holding.
|
|
//
|
|
// **A seat held by derivation is a seat held by accident of being alone** (novox/hq
|
|
// 04-ISSUES/170). ADR 0131 lets a holder on record settle a seat, and lets any other assignment
|
|
// whose module could hold it stand beside the holder, eligible and silent. But a seat nobody
|
|
// ever handed over has no record, so its holder is whichever assignment happened to be the sole
|
|
// claimant — and the day a second one is assigned, both claim, both are refused, and the first
|
|
// one's whole machine stops resolving. That is what assigning a second postgres did to the
|
|
// control plane's own store.
|
|
//
|
|
// So the mesh writes the derived answer down before it acts on an assignment: for every
|
|
// mesh-scoped seat with exactly one resolved holder and nothing on record, that holder is
|
|
// recorded as the standing one — the same record `seat <name> --to <node>/<module>` makes by
|
|
// hand, made from what the mesh already resolved. A seat with two derived claimants is left
|
|
// alone: that is the ambiguity a person settles, and recording either would be guessing.
|
|
//
|
|
// Node-scoped seats are untouched: a record is one holder per seat, and a node-scoped seat has
|
|
// one holder per machine (ADR 0121), so there is nothing for a record to settle there.
|
|
func recordDerivedHolders(ctx context.Context, open *stores) ([]string, error) {
|
|
inv := open.inventory
|
|
shelf, err := inv.Catalogue(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// exclude nobody: every node's claims, resolved with the holdings on record.
|
|
world, err := restOfTheMesh(ctx, open, shelf, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
recorded, err := inv.Holdings(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// A record is a row against a seat the store knows. A seat it does not — a mesh whose seats
|
|
// were never seeded, a seat a module declares for itself — stays held by derivation, as it
|
|
// always was; a missing row is not a reason an assignment fails.
|
|
known, err := inv.Seats(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
recordable := map[string]bool{}
|
|
for _, s := range known {
|
|
recordable[s.Name] = true
|
|
}
|
|
onRecord := map[string]bool{}
|
|
for _, h := range recorded {
|
|
if s, ok := catalogue.SeatNamed(h.Claim); ok {
|
|
onRecord[s.Name] = true
|
|
}
|
|
}
|
|
holders := map[string][]catalogue.Held{}
|
|
for _, h := range world.Held {
|
|
if h.Scope != catalogue.ScopeMesh {
|
|
continue
|
|
}
|
|
s, ok := catalogue.SeatNamed(h.Claim)
|
|
if !ok || onRecord[s.Name] || !recordable[s.Name] {
|
|
continue
|
|
}
|
|
holders[s.Name] = append(holders[s.Name], h)
|
|
}
|
|
names := make([]string, 0, len(holders))
|
|
for name := range holders {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
var said []string
|
|
for _, name := range names {
|
|
if len(holders[name]) != 1 {
|
|
continue
|
|
}
|
|
h := holders[name][0]
|
|
if err := inv.HoldSeat(ctx, name, catalogue.ScopeMesh, h.Node, h.Module); err != nil {
|
|
return said, err
|
|
}
|
|
said = append(said, fmt.Sprintf(
|
|
"recorded %s on %s as the standing holder of %s, which it held only by being alone",
|
|
h.Module, h.Node, name))
|
|
}
|
|
return said, nil
|
|
}
|
|
|
|
// replicatedHolders is, for each replicated mesh seat, every machine on the private network holding
|
|
// it — by internal name, at its private address (novox/hq ADR 0223). What a machine's resolver file
|
|
// lists for `mesh-dns-resolver`; the rendering puts the machine itself first when it is one.
|
|
//
|
|
// **The holders on record, and only the sole claimant when there are none** — the same answer the
|
|
// resolver gives about who holds (ADR 0131, issue 170). An assignment standing beside the holders,
|
|
// eligible and silent, is not listed: it becomes a holder by `seat <name> --add`, an act, never by
|
|
// being assigned. Two claimants with nothing on record are refused at resolution, so neither is
|
|
// listed here. A holder off the private network is left out: a resolver named at an address nothing
|
|
// answers is a lookup that waits out its timeout on every name.
|
|
func replicatedHolders(ctx context.Context, inv *inventory.Inventory,
|
|
shelf map[string]catalogue.Manifest) (map[string]map[string]string, error) {
|
|
var replicated []catalogue.Seat
|
|
for _, s := range catalogue.Seats() {
|
|
if s.Replicated && s.Scope == catalogue.ScopeMesh {
|
|
replicated = append(replicated, s)
|
|
}
|
|
}
|
|
if len(replicated) == 0 {
|
|
return nil, nil
|
|
}
|
|
recorded, err := inv.Holdings(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
places, err := onTheNetwork(ctx, inv, shelf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
address := map[string]string{}
|
|
for _, p := range places {
|
|
address[p.Name] = p.Address
|
|
}
|
|
entries, err := inv.Catalogued(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := map[string]map[string]string{}
|
|
for _, seat := range replicated {
|
|
var nodes []string
|
|
for _, h := range recorded {
|
|
if hs, ok := catalogue.SeatNamed(h.Claim); ok && hs.Name == seat.Name && h.Scope == seat.Scope {
|
|
nodes = append(nodes, h.Node)
|
|
}
|
|
}
|
|
if len(nodes) == 0 {
|
|
var derived []string
|
|
for _, e := range entries {
|
|
for _, c := range e.Manifest.Claims {
|
|
if cs, ok := catalogue.SeatNamed(c.Name); ok && cs.Name == seat.Name && c.At() == seat.Scope {
|
|
derived = append(derived, e.On...)
|
|
}
|
|
}
|
|
}
|
|
if len(derived) == 1 {
|
|
nodes = derived
|
|
}
|
|
}
|
|
at := map[string]string{}
|
|
for _, n := range nodes {
|
|
if address[n] != "" {
|
|
at[overlay.InternalName(n)] = address[n]
|
|
}
|
|
}
|
|
out[seat.Name] = at
|
|
}
|
|
return out, nil
|
|
}
|