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).
101 lines
3.6 KiB
Go
101 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// What a machine's provisioner client said of a module's files (novox/hq ADR 0362, ADR 0349 §15, design 57 P6,
|
|
// issue 672), read where a walk's end and a gate count a machine's send applied: a module the send handed to the
|
|
// provisioner is applied there only once the client reported `prov.applied` on that build's templates as well.
|
|
|
|
// provAppliedOn is the client's newest report of a module on a machine, from the stream that keeps it
|
|
// (broker.ProvAppliedStream); false when it has said nothing of it. A variable so a test can say the client's words.
|
|
var provAppliedOn = func(ctx context.Context, node, module string) (inventory.ProvReport, bool, error) {
|
|
var raw []byte
|
|
var said bool
|
|
read := func(js nats.JetStreamContext) error {
|
|
var err error
|
|
raw, said, err = broker.LastProvApplied(js, node, module)
|
|
return err
|
|
}
|
|
var err error
|
|
if d := doctorFrom; d != nil && d.js != nil {
|
|
err = read(d.js.Context())
|
|
} else {
|
|
err = onTheBus(func(conn *nats.Conn) error {
|
|
js, err := conn.JetStream(nats.Context(ctx))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return read(js)
|
|
})
|
|
}
|
|
if err != nil || !said {
|
|
return inventory.ProvReport{}, false, err
|
|
}
|
|
var r inventory.ProvReport
|
|
if err := json.Unmarshal(raw, &r); err != nil {
|
|
return inventory.ProvReport{}, false, fmt.Errorf("what %s's provisioner client said of %s is not its report: %w",
|
|
node, module, err)
|
|
}
|
|
return r, true, nil
|
|
}
|
|
|
|
// provLookup answers the clients' reports for one reading; a report that cannot be read is not counted, which
|
|
// leaves the machine waiting, then silent, never applied — and is said.
|
|
func provLookup(ctx context.Context) inventory.ProvLookup {
|
|
return func(node, module string) (inventory.ProvReport, bool) {
|
|
r, said, err := provAppliedOn(ctx, node, module)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "what %s's provisioner client said of %s could not be read: %v\n", node, module, err)
|
|
return inventory.ProvReport{}, false
|
|
}
|
|
return r, said
|
|
}
|
|
}
|
|
|
|
// provisioningFailed is every module whose files a machine's provisioner client last said it failed to place, from
|
|
// the stream that keeps their reports.
|
|
func provisioningFailed(js nats.JetStreamContext) ([]inventory.ProvReport, error) {
|
|
all, err := broker.ProvAppliedReports(js)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []inventory.ProvReport
|
|
for subject, raw := range all {
|
|
var r inventory.ProvReport
|
|
if err := json.Unmarshal(raw, &r); err != nil {
|
|
return nil, fmt.Errorf("what was said on %s is not a provisioner client's report: %w", subject, err)
|
|
}
|
|
if r.Status == inventory.ProvFailed {
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// provisionedOutcome is a machine's report of one send as a delivery's end counts it (novox/hq ADR 0362 §1-5, issue
|
|
// 840): the engine's own report, combined with its provisioner client's for a send that handed modules to it. The
|
|
// outcome of a client's failure begins inventory.OutcomeProvisioningFailed; a report not yet applied on both sides
|
|
// is none (false). A send handing nothing is the engine's report unchanged. onSend says the report is about the send.
|
|
func provisionedOutcome(node string, sent inventory.SentDeclaration, r inventory.Reported, onSend bool,
|
|
prov inventory.ProvLookup) (string, bool) {
|
|
if len(sent.Provisioned) == 0 {
|
|
return r.Outcome, onSend
|
|
}
|
|
engine := inventory.AppliedReport{Outcome: r.Outcome}
|
|
if r.At != nil {
|
|
engine.At = *r.At
|
|
}
|
|
got, ok := inventory.ProvisionedSend(node, sent, engine, onSend, prov)
|
|
return got.Outcome, ok
|
|
}
|