121 lines
4.5 KiB
Go
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
|
|
}
|