Drop what a send carried from the node row (migration 0109) and read an image list from the compositions (release 6, issue 1945 B8)
This commit is contained in:
@@ -87,7 +87,7 @@ func reportsReaching(t *testing.T, open *stores, reachable []link.Reach, held ..
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := open.inventory.RecordSent(ctx, record.ID, digestOf(body), nil); err != nil {
|
||||
if err := open.inventory.RecordSentForTest(ctx, record.ID, digestOf(body)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := (link.Enrolment{Inventory: open.inventory}).Heard(ctx, link.Report{
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestALastSendIsReadFromTheSendAndTheReport(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := open.inventory.RecordSent(ctx, record.ID, "carried", map[string]string{overlay.Name: "c-overlay"}); err != nil {
|
||||
if err := open.inventory.RecordSentForTest(ctx, record.ID, "carried"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := assign(ctx, open, "laptop", "keeper"); err != nil {
|
||||
|
||||
@@ -59,7 +59,7 @@ func pushedBy(t *testing.T, open *stores, node string) string {
|
||||
builds[m.Module] = current[m.Module].Commit
|
||||
}
|
||||
}
|
||||
if err := open.inventory.RecordSent(ctx, record.ID, digest, builds); err != nil {
|
||||
if err := open.inventory.RecordSentForTest(ctx, record.ID, digest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return digest
|
||||
|
||||
@@ -245,9 +245,6 @@ func readNode(ctx context.Context, open *stores, n inventory.Node, place invento
|
||||
if out.Sequence, err = inv.Sequence(ctx, n.ID); err != nil {
|
||||
return out, err
|
||||
}
|
||||
if out.Epoch, err = inv.SentEpoch(ctx, n.ID); err != nil {
|
||||
return out, err
|
||||
}
|
||||
// Every node-engine of release 6 reads a module per resource and a build per module.
|
||||
out.ReadsModules = true
|
||||
stated, has, err := inv.HealthOf(ctx, n.Name)
|
||||
|
||||
@@ -93,9 +93,7 @@ func probeDeclarations(ctx context.Context, d *doctor) ([]conditions.Observation
|
||||
// With the order it was last sent, so the validator reads the envelope a machine is sent
|
||||
// — its epoch included (novox/hq to-be 45 §6).
|
||||
var serr error
|
||||
if declared.Sequence, serr = open.inventory.Sequence(ctx, n.ID); serr == nil {
|
||||
declared.Epoch, serr = open.inventory.SentEpoch(ctx, n.ID)
|
||||
}
|
||||
declared.Sequence, serr = open.inventory.Sequence(ctx, n.ID)
|
||||
if serr != nil {
|
||||
return nil, serr // the store, not the machine: the probe could not run
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -413,18 +415,12 @@ func imagesCommand(ctx context.Context, args []string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// And what it was last sent, as `plan --diff` compares against.
|
||||
prev, err := open.inventory.SentSummary(ctx, node)
|
||||
// And what its compositions name, which the node applies by its wants (ADR 0539 §2): read from its bucket on the bus.
|
||||
sent, kept, err := keptImages(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sent []sentResource
|
||||
if len(prev) > 0 {
|
||||
if err := json.Unmarshal(prev, &sent); err != nil {
|
||||
return fmt.Errorf("%s: what it was last sent is kept in a form this version does not read: %w", node, err)
|
||||
}
|
||||
}
|
||||
answer, err := imagesOf(node, body, sent, len(prev) > 0)
|
||||
answer, err := imagesOf(node, body, sent, kept)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -437,15 +433,58 @@ func imagesCommand(ctx context.Context, args []string) error {
|
||||
fmt.Printf("%s (%s; %s)\n", d.Image, strings.Join(d.In, ", "), strings.Join(d.Resources, ", "))
|
||||
}
|
||||
if !answer.SentKnown {
|
||||
fmt.Printf("%s: which images it was last sent is not kept — the next push keeps it\n", node)
|
||||
fmt.Printf("%s: no composition of it is kept on the bus yet, so which images it applies is not known\n", node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sentResource is what the store kept of one resource a machine was last sent, as far as `images` reads it: the container's
|
||||
// image. Nothing writes the summary any more (ADR 0539 §5); a machine's images are read from its compositions.
|
||||
// sentResource is a container among the resources of a node's compositions, as `images` reads it: the image the node applies.
|
||||
type sentResource struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
// imagesOfCompositions are the containers of the compositions' resources, in the shape `images` compares with what would be
|
||||
// declared; the bool says a composition was kept at all.
|
||||
func imagesOfCompositions(values [][]byte) ([]sentResource, bool, error) {
|
||||
var out []sentResource
|
||||
for _, value := range values {
|
||||
c, err := sdkdelivery.DecodeComposed(value)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("a composition is not readable: %w", err)
|
||||
}
|
||||
for _, raw := range c.Resources {
|
||||
var r struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &r); err != nil {
|
||||
return nil, false, fmt.Errorf("a resource of a composition is not readable: %w", err)
|
||||
}
|
||||
if r.Type == "container" {
|
||||
out = append(out, sentResource{ID: r.ID, Type: r.Type, Image: r.Image})
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, len(values) > 0, nil
|
||||
}
|
||||
|
||||
// keptImages reads a node's compositions from its bucket on the bus and answers their containers.
|
||||
func keptImages(ctx context.Context, node string) ([]sentResource, bool, error) {
|
||||
bus, err := aBus()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("a node's compositions are kept on the bus, and this process has none: %w", err)
|
||||
}
|
||||
defer bus.Close()
|
||||
js, err := jetstream.New(bus.Conn())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
values, err := keptCompositionsOf(ctx, js, node)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return imagesOfCompositions(values)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
@@ -229,3 +230,35 @@ func TestImagesAreEveryContainerImageOfTheDeclarationAndOfWhatWasSent(t *testing
|
||||
t.Errorf("with nothing sent yet: %+v %v", none, err)
|
||||
}
|
||||
}
|
||||
|
||||
// **`images` reads what a node applies from its compositions** (ADR 0539 §2, issue 1945 B8): the containers among the
|
||||
// resources of the values kept in its bucket, whatever module and fingerprint; none kept is not known.
|
||||
func TestImagesAreReadFromTheCompositionsAMachineKeeps(t *testing.T) {
|
||||
file := json.RawMessage(`{"id":"web.config","type":"file","module":"web","path":"/etc/web","content":"x"}`)
|
||||
box := json.RawMessage(`{"id":"web.server","type":"container","name":"web","module":"web","image":"registry/web@sha256:` + strings.Repeat("a", 64) + `"}`)
|
||||
other := json.RawMessage(`{"id":"db.server","type":"container","name":"db","module":"db","image":"registry/db@sha256:` + strings.Repeat("b", 64) + `"}`)
|
||||
encode := func(module string, fp string, resources ...json.RawMessage) []byte {
|
||||
v, err := sdkdelivery.EncodeComposed(sdkdelivery.Composed{Node: "anchor", Module: module, FP: fp, Resources: resources})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
web := encode("web", sdkdelivery.SourcePrefix2+strings.Repeat("1", 64), file, box)
|
||||
db := encode("db", sdkdelivery.SourcePrefix2+strings.Repeat("2", 64), other)
|
||||
|
||||
got, kept, err := imagesOfCompositions([][]byte{web, db})
|
||||
if err != nil || !kept || len(got) != 2 {
|
||||
t.Fatalf("images %+v kept %v: %v", got, kept, err)
|
||||
}
|
||||
answer, err := imagesOf("anchor", nil, got, kept)
|
||||
if err != nil || len(answer.Images) != 2 || !answer.SentKnown {
|
||||
t.Fatalf("the answer %+v: %v", answer, err)
|
||||
}
|
||||
if none, kept, err := imagesOfCompositions(nil); err != nil || kept || len(none) != 0 {
|
||||
t.Fatalf("no composition kept: %+v %v %v", none, kept, err)
|
||||
}
|
||||
if _, _, err := imagesOfCompositions([][]byte{[]byte("nope")}); err == nil {
|
||||
t.Fatal("an unreadable composition was read as holding no image")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user