Compose where a declaration was sent: secrets, credentials, acceptances, the hub's peers, adoption, assignments and the bus's user list (issue 1945, B2, B6)
Each act that changed what a node runs now writes that node's compositions, and its wants follow by the assigner's rules; a bus upgrade composes the bus's machines and answers the want the operator writes, since the assigner never moves the bus (ADR 0539 clauses 5, 7 and 15). A node-engine may ask its own backup holder before a data module moves.
This commit is contained in:
@@ -10,8 +10,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/novox/mesh-controller/internal/conditions"
|
||||
"github.com/novox/mesh-controller/internal/inventory"
|
||||
"github.com/novox/mesh-controller/internal/link"
|
||||
@@ -229,17 +231,85 @@ func busCommand(ctx context.Context, args []string) error {
|
||||
fmt.Printf("bus upgrade %d: %s %s → %s on %s; streams snapshotted at %s; %s\n", step.ID, b.module, step.From,
|
||||
short(b.to), strings.Join(moving, ", "), where, map[bool]string{true: "reversible: putting the old build back undoes it",
|
||||
false: "NOT reversible: the snapshot is the only way back"}[*reversible])
|
||||
sent, err := sendToEach(withBusStep(ctx), open, moving)
|
||||
// **Nothing is sent, and the assigner never moves the bus** (novox/hq ADR 0539 clause 15): the bus's machines are
|
||||
// composed at the build the mesh holds, and the operator writes each machine's want with the operator's credential,
|
||||
// recorded, naming that composition.
|
||||
wants, err := busWantsToWrite(withBusStep(ctx), open, b.module, moving)
|
||||
if err != nil {
|
||||
_ = inv.EndBusStep(ctx, step.ID, "failed", "the send was refused: "+err.Error())
|
||||
return fmt.Errorf("the bus's machine could not be sent its new build: %w — nothing was replaced", err)
|
||||
_ = inv.EndBusStep(ctx, step.ID, "failed", "the bus's composition could not be written: "+err.Error())
|
||||
return fmt.Errorf("the bus's machines could not be composed at its new build: %w — nothing was replaced", err)
|
||||
}
|
||||
fmt.Printf("composed %s at the bus's new build. Write each want below with the operator's credential, and record it "+
|
||||
"in the hand-act log; `bus-maintenance` is open until the bus answers healthy again — every stream, every durable "+
|
||||
"consumer, a round trip to the machines (H-bus) — within %s, or the step is said failed with its snapshot as the "+
|
||||
"way back. `bus` says how it went\n", strings.Join(moving, ", "), busStepBound)
|
||||
for _, w := range wants {
|
||||
fmt.Println(" " + w)
|
||||
}
|
||||
fmt.Printf("sent %s; `bus-maintenance` is open until the bus answers healthy again — every stream, every durable "+
|
||||
"consumer, a round trip to the machines (H-bus) — within %s, or the step is said failed with its snapshot "+
|
||||
"as the way back. `bus` says how it went\n", strings.Join(sent, ", "), busStepBound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// busWantsToWrite composes the bus's machines and answers, for each, the want the operator writes: its key in the
|
||||
// assigner's bucket and its value, naming the fingerprint the bus module is registered at and that machine's
|
||||
// composition of it.
|
||||
func busWantsToWrite(ctx context.Context, open *stores, module string, machines []string) ([]string, error) {
|
||||
if _, err := composeModules(ctx, open, machines); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fps, err := currentFingerprints(ctx, open.inventory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bus, err := aBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer bus.Close()
|
||||
js, err := jetstream.New(bus.Conn())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digests, err := js.KeyValue(ctx, sdkdelivery.ControllerComposedBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
digestOf := func(node string) (string, error) {
|
||||
key, err := sdkdelivery.ComposedKey(node, module, fps[module])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
e, err := digests.Get(ctx, key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: %w", key, err)
|
||||
}
|
||||
v, err := sdkdelivery.DecodeComposedDigest(e.Value())
|
||||
return v.Digest, err
|
||||
}
|
||||
return busWants(module, fps[module], machines, digestOf)
|
||||
}
|
||||
|
||||
// busWants is each machine's want for the bus module, as the operator writes it: `<bucket> <key> <value>`.
|
||||
func busWants(module, fp string, machines []string, digestOf func(node string) (string, error)) ([]string, error) {
|
||||
var out []string
|
||||
for _, node := range machines {
|
||||
digest, err := digestOf(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := sdkdelivery.WantKey(node, module)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := sdkdelivery.EncodeWant(sdkdelivery.Want{FP: fp, Composed: digest,
|
||||
Words: "the bus moved by a person's step (bus upgrade, ADR 0539 clause 15)"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%s %s %s", sdkdelivery.AssignerWantsBucket, key, value))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// busStatus is `bus`: what an upgrade would do, and the last step.
|
||||
func busStatus(ctx context.Context) error {
|
||||
open, err := openStores(ctx)
|
||||
|
||||
Reference in New Issue
Block a user