mesh/merge-gate head 378e65780d0b merged into trunk 791b92c875d3 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 378e65780d0b merged into trunk 791b92c875d3 — pass: its merge-check.sh passed
mesh/delivery delivered
101 lines
3.4 KiB
Go
101 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// A machine out of touch when a tier is sent is not sent to and not waited for (novox/hq ADR 0435 §1, issue 1161): the
|
|
// delivery goes on with the machines heard, says the others were away, and the away ones get the builds by the send
|
|
// they are given when heard again (ADR 0353, ADR 0360 §5). Away is the silent check's one verdict, machineFacts.silent
|
|
// over the watchman's presence (ADR 0433 §3), never a second rule; a sleeping machine is away once presence says so.
|
|
|
|
// awayOf is the machines out of touch at a moment: those the silent check calls silent (a sleeping machine
|
|
// is away once presence says so). A machine
|
|
// never heard from has not joined and is not away.
|
|
func awayOf(machines []machineFacts, now time.Time) map[string]bool {
|
|
out := map[string]bool{}
|
|
for _, m := range machines {
|
|
if m.silent(now) {
|
|
out[m.name] = true
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// judgeAway is the walk's reading of which machines are away as a tier is sent: every node with its last-heard time and
|
|
// the watchman's verdict on its presence key. A bus that cannot be asked leaves none away, which is what a walk did
|
|
// before: a machine is not passed on a reading that was not made.
|
|
var judgeAway = func(ctx context.Context, open *stores, now time.Time) (map[string]bool, error) {
|
|
nodes, err := open.inventory.Nodes(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
address, err := broker.BusAddress()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
js, err := broker.Dial(address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer js.Close()
|
|
machines := make([]machineFacts, len(nodes))
|
|
for i, n := range nodes {
|
|
machines[i] = machineFacts{name: n.Name, lastHeard: n.LastSeen}
|
|
}
|
|
if err := withPresence(ctx, js.Conn(), machines, now); err != nil {
|
|
return nil, err
|
|
}
|
|
return awayOf(machines, now), nil
|
|
}
|
|
|
|
// rememberAway keeps on the plan the machines of running that are away: sorted, each once.
|
|
func rememberAway(p *inventory.Plan, running []string, away map[string]bool) {
|
|
for _, n := range running {
|
|
if away[n] && !slices.Contains(p.Away, n) {
|
|
p.Away = append(p.Away, n)
|
|
}
|
|
}
|
|
sort.Strings(p.Away)
|
|
}
|
|
|
|
// runningHeard is the machines of running that the walk waits on for a module: those a send of the walk reached, and
|
|
// those it did not pass as away.
|
|
func runningHeard(p inventory.Plan, running []string, s *inventory.PlanModule) []string {
|
|
return slices.DeleteFunc(slices.Clone(running), func(n string) bool {
|
|
if !slices.Contains(p.Away, n) {
|
|
return false
|
|
}
|
|
_, sent := s.Rest[n]
|
|
return !sent
|
|
})
|
|
}
|
|
|
|
// awaySaying is what a plan says of the machines it passed as away, empty when it passed none.
|
|
func awaySaying(p inventory.Plan) string {
|
|
var said []string
|
|
for _, n := range p.Away {
|
|
said = append(said, n+" was away: sent when it is heard again")
|
|
}
|
|
return strings.Join(said, "; ")
|
|
}
|
|
|
|
// judgedAway reads which machines are away for a tier about to be sent, and says when it cannot.
|
|
func judgedAway(ctx context.Context, open *stores, now time.Time) map[string]bool {
|
|
away, err := judgeAway(ctx, open, now)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "which machines are away cannot be read, so none is passed over: %v\n", err)
|
|
return nil // empty-on-error: none is passed over on a reading that was not made; the error is said just above
|
|
}
|
|
return away
|
|
}
|