Files
mesh-controller/internal/inventory/provisioned.go
T
mesh-tools 97a2d841c7 Land feature 57's controller work on its release branch
The controller plans config, templates and moved providers' provisions,
hands bus accounts to the vault and judges the new manifest words, on the
feature's SDK and node-engine validator (feature 57, config and provisioning).
2026-10-15 21:27:33 +00:00

121 lines
4.5 KiB
Go

package inventory
import (
"fmt"
"sort"
"time"
)
// A machine has applied a send of a provisioned module only once its files are placed (novox/hq ADR 0362, design
// 57 P6, issue 672): for a module the send handed to the provisioner (SentDeclaration.Provisioned), the machine's
// own report that it applied the send is half of it, and its provisioner client's `prov.applied` for the build's
// templates the other half.
//
// - both reported, applied: the send is applied, at the later of the two (ADR 0362 §5: delivery time runs to it);
// - the client reported it failed on that build: a failed apply, named with its reason (§3);
// - the client has not reported it applied on that build (not yet, waiting for a value, an older build): not yet
// applied, and once ApplySilentAfter has passed since the send the walk names the machine silent (§4);
// - a send that handed nothing: the machine's own report alone, as before.
// ProvReport is a provisioner client's newest report of one module on its machine, as `prov.applied` says it
// (mesh-catalog mesh-provisioner-client Report): applied, waiting, failed with the reason, or removed; the digest
// of the templates it filled from; and when it said it.
type ProvReport struct {
Node string `json:"node"`
Module string `json:"module"`
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
WaitsFor []string `json:"waits-for,omitempty"`
Templates string `json:"templates,omitempty"`
At time.Time `json:"at"`
}
// The words a client's report says.
const (
ProvApplied = "applied"
ProvWaiting = "waiting"
ProvFailed = "failed"
ProvRemoved = "removed"
)
// ProvLookup answers a client's newest report of a module on a machine; false when it has said nothing of it.
type ProvLookup func(node, module string) (ProvReport, bool)
// OutcomeProvisioningFailed begins the outcome of a send whose module's files the provisioner failed to place.
const OutcomeProvisioningFailed = "provisioning failed"
// ProvisionedSend is a machine's report of a send, with its provisioner client's reports of every module the send
// handed to it: what a walk counts as that machine's report. engine and reported are the machine's own report and
// whether it said one. A failed client report is the send's outcome, whatever the machine said; none, or one not
// yet applied on the build sent, leaves the send unreported.
func ProvisionedSend(node string, send SentDeclaration, engine AppliedReport, reported bool, prov ProvLookup) (AppliedReport, bool) {
if len(send.Provisioned) == 0 {
return engine, reported
}
// The machine's own failure, or its wait for a busy container, is its report whatever the client says.
if reported && (engine.Outcome != OutcomeApplied || engine.Waiting) {
return engine, true
}
modules := make([]string, 0, len(send.Provisioned))
for m := range send.Provisioned {
modules = append(modules, m)
}
sort.Strings(modules)
at, placed := engine.At, true
for _, m := range modules {
r, said := ProvReport{}, false
if prov != nil {
r, said = prov(node, m)
}
switch {
case said && r.Status == ProvFailed && aboutTheSend(r, send, send.Provisioned[m]):
return AppliedReport{At: r.At, Outcome: fmt.Sprintf("%s (%s: %s)", OutcomeProvisioningFailed, m,
orNoReason(r.Reason))}, true
case said && r.Status == ProvApplied && r.Templates == send.Provisioned[m]:
if r.At.After(at) {
at = r.At
}
default:
placed = false
}
}
if !reported || !placed {
return AppliedReport{}, false
}
engine.At = at
return engine, true
}
// Only is the send narrowed to what it handed to the provisioner for the named modules: a delivery waits on the
// modules it carries, never on another module the same machine's declaration hands (ADR 0362 §1-2, issue 840).
// A send handing nothing is returned as it is.
func (s SentDeclaration) Only(modules ...string) SentDeclaration {
if len(s.Provisioned) == 0 {
return s
}
kept := map[string]string{}
for _, m := range modules {
if d, ok := s.Provisioned[m]; ok {
kept[m] = d
}
}
s.Provisioned = kept
return s
}
// aboutTheSend says a client's failure is about what the send handed: on the build's templates, or, said before it
// had any to name (its templates could not be fetched), no earlier than the send.
func aboutTheSend(r ProvReport, send SentDeclaration, digest string) bool {
if r.Templates != "" {
return r.Templates == digest
}
return send.At.IsZero() || !r.At.Before(send.At)
}
func orNoReason(s string) string {
if s == "" {
return "no reason given"
}
return s
}