Carry an adopted node's mode and taken modules in every declaration, from one marshaller (hq ADR 0100)

This commit is contained in:
2026-09-22 17:17:54 +02:00
parent 1c32af6a22
commit a86a6c2974
6 changed files with 357 additions and 49 deletions
+21 -26
View File
@@ -4,7 +4,6 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"flag"
"fmt"
@@ -283,10 +282,10 @@ func pushCommand(ctx context.Context, args []string) error {
asked = append(asked, n.Name)
}
sending, refusals := composeEach(asked, func(node string) ([]map[string]any, error) {
sending, refusals := composeEach(asked, func(node string) (sendable, error) {
plan, settings, err := planFor(ctx, open, node)
if err != nil {
return nil, err
return sendable{}, err
}
// A module assigned here that this machine cannot host is said and left out, not fatal: the
// healthy modules beside it are still resolved and sent. Reported so it is not silently
@@ -300,7 +299,7 @@ func pushCommand(ctx context.Context, args []string) error {
sentDigest := map[string]string{}
for _, s := range sending {
body, err := json.Marshal(map[string]any{"declaration": 1, "resources": s.resources})
body, err := s.declared.Body()
if err != nil {
return err
}
@@ -318,7 +317,7 @@ func pushCommand(ctx context.Context, args []string) error {
return err
}
sentDigest[s.node] = digest
fmt.Printf("sent %s %d resource(s)\n", s.node, len(s.resources))
fmt.Printf("sent %s %d resource(s)\n", s.node, len(s.declared.Resources))
}
fmt.Printf("\n%d node(s) told\n", len(sending))
@@ -374,17 +373,17 @@ func pushCommand(ctx context.Context, args []string) error {
// (novox/hq ADR 0066). The earlier cut routed these through sendTo, which is
// all-or-nothing — so one swept machine's compose error failed the operator's named
// push and skipped its --wait, the very intolerance the main path exists to avoid.
sending, refused := composeEach(also, func(node string) ([]map[string]any, error) {
sending, refused := composeEach(also, func(node string) (sendable, error) {
plan, settings, err := planFor(ctx, open, node)
if err != nil {
return nil, err
return sendable{}, err
}
reportUnhostable(node, plan)
return declarationWith(ctx, open, node, plan, settings, gens, Allocating)
})
refusals = append(refusals, refused...)
for _, s := range sending {
body, err := json.Marshal(map[string]any{"declaration": 1, "resources": s.resources})
body, err := s.declared.Body()
if err != nil {
return err
}
@@ -398,7 +397,7 @@ func pushCommand(ctx context.Context, args []string) error {
if err := inv.RecordSent(ctx, record.ID, digestOf(body)); err != nil {
return err
}
fmt.Printf("sent %s %d resource(s)\n", s.node, len(s.resources))
fmt.Printf("sent %s %d resource(s)\n", s.node, len(s.declared.Resources))
}
// Every candidate this round is marked handled — the sent ones so they are not
// re-listed, and the refused ones so a machine that cannot be composed does not make
@@ -458,8 +457,8 @@ func waitForApplied(ctx context.Context, inv *inventory.Inventory, node, digest
// readyNode is one machine and the declaration it would be sent.
type readyNode struct {
node string
resources []map[string]any
node string
declared sendable
}
// composeEach works out what each named machine should be, and never lets one machine's answer
@@ -480,21 +479,21 @@ type readyNode struct {
// The all-or-nothing rule is kept where it means something — sendTo, which rotates a credential
// across two machines that must agree — and dropped here, where it never did.
func composeEach(names []string,
compose func(node string) ([]map[string]any, error)) ([]readyNode, []string) {
compose func(node string) (sendable, error)) ([]readyNode, []string) {
var sending []readyNode
var refusals []string
for _, name := range names {
resources, err := compose(name)
declared, err := compose(name)
if err != nil {
refusals = append(refusals, fmt.Sprintf("%s:\n%v", name, err))
continue
}
if len(resources) == 0 {
if len(declared.Resources) == 0 {
fmt.Printf("%s is assigned nothing — skipped\n", name)
continue
}
sending = append(sending, readyNode{name, resources})
sending = append(sending, readyNode{name, declared})
}
return sending, refusals
}
@@ -533,11 +532,7 @@ func sendTo(ctx context.Context, open *stores, names []string) error {
return err
}
type ready struct {
node string
resources []map[string]any
}
var sending []ready
var sending []readyNode
var refusals []string
for _, name := range names {
plan, settings, err := planFor(ctx, open, name)
@@ -546,12 +541,12 @@ func sendTo(ctx context.Context, open *stores, names []string) error {
continue
}
reportUnhostable(name, plan)
resources, err := declarationWith(ctx, open, name, plan, settings, gens, Allocating)
declared, err := declarationWith(ctx, open, name, plan, settings, gens, Allocating)
if err != nil {
refusals = append(refusals, fmt.Sprintf("%s:\n%v", name, err))
continue
}
sending = append(sending, ready{name, resources})
sending = append(sending, readyNode{name, declared})
}
if len(refusals) > 0 {
return fmt.Errorf("nothing was sent. %d machine(s) could not be resolved:\n\n%s",
@@ -565,7 +560,7 @@ func sendTo(ctx context.Context, open *stores, names []string) error {
defer server.Close()
for _, s := range sending {
body, err := json.Marshal(map[string]any{"declaration": 1, "resources": s.resources})
body, err := s.declared.Body()
if err != nil {
return err
}
@@ -579,7 +574,7 @@ func sendTo(ctx context.Context, open *stores, names []string) error {
if err := inv.RecordSent(ctx, record.ID, digestOf(body)); err != nil {
return err
}
fmt.Printf(" sent %s %d resource(s)\n", s.node, len(s.resources))
fmt.Printf(" sent %s %d resource(s)\n", s.node, len(s.declared.Resources))
}
return nil
}
@@ -610,11 +605,11 @@ func wouldSend(ctx context.Context, open *stores,
if err != nil {
continue
}
resources, err := declarationWith(ctx, open, n.Name, plan, settings, gens, Reading)
declared, err := declarationWith(ctx, open, n.Name, plan, settings, gens, Reading)
if err != nil {
continue
}
body, err := json.Marshal(map[string]any{"declaration": 1, "resources": resources})
body, err := declared.Body()
if err != nil {
return nil, err
}