Composer: the store's address is the image seat holder's listen open to the mesh (ADR 0632 §1; issue 1966)
The provision artifact-store retires, so the store is found by the holder of container-image-registry and the listen it opens to the mesh, on the port its node put it on; every name of the store, oci-artifact-registry's too, answers that address. The engine states form:seat-reach with its contract. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -9,10 +9,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// RegistryModule is the module that provides the mesh's artifact store.
|
||||
// RegistryModule is the module that holds the mesh's artifact store.
|
||||
//
|
||||
// **Named for the software, not the job.** The job is `artifact-store`, which is the provision this
|
||||
// module offers; the software is Distribution, the OCI reference implementation. Calling the module
|
||||
// **Named for the software, not the job.** The job is the artifact store, which this module holds as
|
||||
// the seat container-image-registry (novox/hq ADR 0632 §1); the software is Distribution, the OCI reference implementation. Calling the module
|
||||
// `registry` named neither — and promised that any registry could sit there, which is the false
|
||||
// genericity the naming rule forbids (novox/hq ADR 0040).
|
||||
const RegistryModule = "distribution"
|
||||
|
||||
@@ -109,7 +109,8 @@ func TestCompositionsAreTheDeclarationSplitByModule(t *testing.T) {
|
||||
Bundles: []catalogue.Bundle{{Name: "runtime", Source: catalogue.ArtifactStoreScheme + "node-tools/runtime/blobs/" + dg('a'),
|
||||
Digest: dg('a'), Language: "go", Binary: "node-tools"}}}
|
||||
store := catalogue.Manifest{Module: "artifacts", Version: "1",
|
||||
Serves: map[string]map[string]any{catalogue.ArtifactStoreProvision: {"port": 5100}}}
|
||||
Claims: []catalogue.Claim{{Name: "container-image-registry", Scope: catalogue.ScopeMesh}},
|
||||
Listens: []catalogue.Listening{{Name: "registry", Port: 5100, From: catalogue.FromMesh}}}
|
||||
lamp := catalogue.Manifest{Module: "lamp", Version: "1", Tools: []string{"on"},
|
||||
Resources: []map[string]any{{"id": "state", "type": "directory", "path": "/var/lib/lamp", "mode": "0700"}},
|
||||
Build: &catalogue.Build{Artifacts: []catalogue.Artifact{{Name: "tools", Kind: catalogue.ArtifactBundle,
|
||||
|
||||
@@ -606,13 +606,15 @@ func otherNodeNotResolvedIsNamed(t *testing.T) {
|
||||
// "reaches no artifact store", and nothing said why).
|
||||
func noStoreReachedSaysWhy(t *testing.T) {
|
||||
in := compose.Input{Catalogue: map[string]catalogue.Manifest{
|
||||
"distribution": {Module: "distribution", Serves: map[string]map[string]any{catalogue.ArtifactStoreProvision: {"port": 5000}}}},
|
||||
Nodes: []compose.Node{{Name: "g14", Assigned: []string{"adwaita"}}, {Name: "novox", Assigned: []string{"distribution"}}}}
|
||||
if got := storeSought(in); got != "the composition holds g14, novox; artifact-store is served by distribution on novox, none of it on the private network with a port" {
|
||||
"distribution": {Module: "distribution", Claims: []catalogue.Claim{{Name: "container-image-registry", Scope: catalogue.ScopeMesh}}},
|
||||
// A module serving the retired provision artifact-store is no store (ADR 0632 §1).
|
||||
"old-store": {Module: "old-store", Serves: map[string]map[string]any{catalogue.ArtifactStoreProvision: {"port": 5000}}}},
|
||||
Nodes: []compose.Node{{Name: "g14", Assigned: []string{"adwaita", "old-store"}}, {Name: "novox", Assigned: []string{"distribution"}}}}
|
||||
if got := storeSought(in); got != "the composition holds g14, novox; container-image-registry is held by distribution on novox, none of it on the private network with a listen open to the mesh" {
|
||||
t.Errorf("said %q", got)
|
||||
}
|
||||
in.Nodes = in.Nodes[:1]
|
||||
if got := storeSought(in); got != "the composition holds g14; no module placed there serves artifact-store" {
|
||||
if got := storeSought(in); got != "the composition holds g14; no module placed there holds container-image-registry" {
|
||||
t.Errorf("said %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1136,16 +1136,14 @@ func sortedKeys[V any](m map[string]V) []string {
|
||||
}
|
||||
|
||||
// storeSought says, for a node that reaches no artifact store, what the composition holds of one: the nodes it holds and
|
||||
// where each module serving the artifact store is placed, so the refusal names why (issue 1126).
|
||||
// where each module claiming the store's seat is placed, so the refusal names why (issue 1126, ADR 0632 §1).
|
||||
func storeSought(in compose.Input) string {
|
||||
var nodes, where []string
|
||||
for _, n := range in.Nodes {
|
||||
nodes = append(nodes, n.Name)
|
||||
for _, a := range n.Assigned {
|
||||
if m, ok := in.Catalogue[a]; ok {
|
||||
if _, serves := m.Serves[catalogue.ArtifactStoreProvision]; serves {
|
||||
where = append(where, a+" on "+n.Name)
|
||||
}
|
||||
if m, ok := in.Catalogue[a]; ok && claimsSeat(m, compose.StoreSeat) {
|
||||
where = append(where, a+" on "+n.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1153,10 +1151,20 @@ func storeSought(in compose.Input) string {
|
||||
sort.Strings(where)
|
||||
said := "the composition holds " + strings.Join(nodes, ", ")
|
||||
if len(where) == 0 {
|
||||
return said + "; no module placed there serves " + catalogue.ArtifactStoreProvision
|
||||
return said + "; no module placed there holds " + compose.StoreSeat
|
||||
}
|
||||
return said + "; " + catalogue.ArtifactStoreProvision + " is served by " + strings.Join(where, ", ") +
|
||||
", none of it on the private network with a port"
|
||||
return said + "; " + compose.StoreSeat + " is held by " + strings.Join(where, ", ") +
|
||||
", none of it on the private network with a listen open to the mesh"
|
||||
}
|
||||
|
||||
// claimsSeat says whether m claims seat at the mesh's scope.
|
||||
func claimsSeat(m catalogue.Manifest, seat string) bool {
|
||||
for _, cl := range m.Claims {
|
||||
if cs, ok := catalogue.SeatNamed(cl.Name); ok && cs.Name == seat && cl.At() == catalogue.ScopeMesh {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// keptOf is the operator export's file (catalogue.KeptID) of a module's composition as it runs, or nil.
|
||||
|
||||
@@ -577,8 +577,8 @@ func TestInput(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store := []byte(`{"module":"store","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],` +
|
||||
`"serves":{"artifact-store":{"port":5000}}}`)
|
||||
store := []byte(`{"module":"store","version":"1",` +
|
||||
`"claims":[{"name":"container-image-registry","scope":"mesh"}],"listens":[{"name":"registry","port":5000,"from":"mesh"}]}`)
|
||||
texts := map[string]string{}
|
||||
var packed bytes.Buffer
|
||||
zw := gzip.NewWriter(&packed)
|
||||
|
||||
@@ -119,8 +119,8 @@ func Provisioned() (*Bus, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store := []byte(`{"module":"store","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],` +
|
||||
`"serves":{"artifact-store":{"port":5000}}}`)
|
||||
store := []byte(`{"module":"store","version":"1",` +
|
||||
`"claims":[{"name":"container-image-registry","scope":"mesh"}],"listens":[{"name":"registry","port":5000,"from":"mesh"}]}`)
|
||||
postgres := []byte(`{"module":"postgres","version":"1","provides":[{"name":"postgres-database","scope":"mesh"}],` +
|
||||
`"serves":{"postgres-database":{"port":5432}}}`)
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ func aRunningModuleWaitsForItsBuild(t *testing.T) {
|
||||
tools := `{"module":"node-tools","version":"1","tool-runner":true,"own-secrets":{"broker":"/var/lib/mesh/node-tools/broker"},` +
|
||||
`"build":{"artifacts":[{"name":"runtime","kind":"bundle","language":"go","system":"arch","from":"cmd/node-tools"}]}}`
|
||||
lamp := `{"module":"lamp","version":"1","resources":[{"id":"service","type":"service","unit":"lamp.service","state":"running","boot":"enabled"}]}`
|
||||
store := `{"module":"artifacts","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],"serves":{"artifact-store":{"port":5100}},` +
|
||||
store := `{"module":"artifacts","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],"claims":[{"name":"container-image-registry","scope":"mesh"}],"listens":[{"name":"registry","port":5100,"from":"mesh"}],` +
|
||||
`"receives":{"artifact-store":"/var/lib/artifacts/received.json"}}`
|
||||
old, next := "src2:"+strings.Repeat("a", 64), "src2:"+strings.Repeat("b", 64)
|
||||
running := []delivery.Composed{{Node: "n1", Module: "node-tools", FP: old, Resources: []json.RawMessage{
|
||||
@@ -215,7 +215,7 @@ func aHeldModuleOfItsOwnAccountNeverRefusesTheNode(t *testing.T) {
|
||||
`"build":{"artifacts":[{"name":"tools","kind":"bundle","language":"typescript","entrypoints":["tools/index.js"]}]}}`
|
||||
}
|
||||
lamp := `{"module":"lamp","version":"1","resources":[{"id":"service","type":"service","unit":"lamp.service","state":"running","boot":"enabled"}]}`
|
||||
store := `{"module":"artifacts","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],"serves":{"artifact-store":{"port":5100}},` +
|
||||
store := `{"module":"artifacts","version":"1","provides":[{"name":"artifact-store","scope":"mesh"}],"claims":[{"name":"container-image-registry","scope":"mesh"}],"listens":[{"name":"registry","port":5100,"from":"mesh"}],` +
|
||||
`"receives":{"artifact-store":"/var/lib/artifacts/received.json"}}`
|
||||
for _, row := range []struct {
|
||||
name string
|
||||
|
||||
@@ -167,10 +167,10 @@ func (c *composer) places(nodeName string, resolved Resolved) (Placed, error) {
|
||||
|
||||
// Where this machine reaches each mesh seat's holder, for ${seat:<seat>:reach} (novox/hq ADR 0222).
|
||||
if store != "" {
|
||||
// Both names the store is reached through answer the one address (ENG-3), each with its holder from the holdings on
|
||||
// record, written beside every image routed through it (design 51, ENG-10).
|
||||
// Every name the store is reached through answers the one address (ENG-3, ADR 0632 §1), each with its holder from the
|
||||
// holdings on record, written beside every image routed through it (design 51, ENG-10).
|
||||
r.SeatReach, r.SeatHolder = map[string]string{}, map[string]string{}
|
||||
for _, seat := range []string{"mesh-artifact-store", "container-image-registry"} {
|
||||
for _, seat := range resolution.StoreSeats {
|
||||
r.SeatReach[seat] = store
|
||||
for _, h := range c.in.Holdings {
|
||||
if hs, ok := catalogue.SeatNamed(h.Claim); ok && hs.Name == seat && h.Scope == catalogue.ScopeMesh && h.Module != "" {
|
||||
@@ -326,18 +326,67 @@ func (c *composer) namesInTheMesh() map[string]string {
|
||||
return out
|
||||
}
|
||||
|
||||
// artifactStoreOnNetwork is the machine on this network that offers the artifact store, and the port that machine put it on.
|
||||
func (c *composer) artifactStoreOnNetwork(on map[string]bool) (node, port string, found bool, err error) {
|
||||
providers := map[string]catalogue.Manifest{}
|
||||
for name, m := range c.in.Catalogue {
|
||||
if _, offers := m.Serves[catalogue.ArtifactStoreProvision]; offers {
|
||||
providers[name] = m
|
||||
// StoreSeat is the seat whose holder is the artifact store (novox/hq ADR 0632 §1): the store's address is that holder's
|
||||
// listen open to the mesh, on the machine placement puts it on. The provision artifact-store it was found by before is
|
||||
// retired; no module serves it.
|
||||
const StoreSeat = "container-image-registry"
|
||||
|
||||
// storeHolders are the modules holding the store's seat: the holdings on record, and only where none is on record every
|
||||
// module of the catalogue claiming it at the mesh's scope, as a seat holder's reach is read (replicatedHolders).
|
||||
func (c *composer) storeHolders() map[string]catalogue.Manifest {
|
||||
out := map[string]catalogue.Manifest{}
|
||||
for _, h := range c.in.Holdings {
|
||||
if hs, ok := catalogue.SeatNamed(h.Claim); ok && hs.Name == StoreSeat && h.Scope == catalogue.ScopeMesh && h.Module != "" {
|
||||
if m, known := c.in.Catalogue[h.Module]; known {
|
||||
out[h.Module] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(providers) == 0 {
|
||||
if len(out) > 0 {
|
||||
return out
|
||||
}
|
||||
for name, m := range c.in.Catalogue {
|
||||
for _, cl := range m.Claims {
|
||||
if cs, ok := catalogue.SeatNamed(cl.Name); ok && cs.Name == StoreSeat && cl.At() == catalogue.ScopeMesh {
|
||||
out[name] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// storeListen is the store holder's listen every machine pulls from: its one listen open to the mesh. A holder opening
|
||||
// none, or more than one, says no address, by name: which of two ports is the store is no guess the composer makes.
|
||||
func storeListen(m catalogue.Manifest) (catalogue.Listening, error) {
|
||||
var open []catalogue.Listening
|
||||
for _, l := range m.Listens {
|
||||
if l.From == catalogue.FromMesh && l.At() == "tcp" {
|
||||
open = append(open, l)
|
||||
}
|
||||
}
|
||||
switch len(open) {
|
||||
case 1:
|
||||
return open[0], nil
|
||||
case 0:
|
||||
return catalogue.Listening{}, fmt.Errorf("%s holds %s and opens no tcp listen to the mesh: the artifact store has no "+
|
||||
"address (novox/hq ADR 0632 §1)", m.Module, StoreSeat)
|
||||
}
|
||||
var said []string
|
||||
for _, l := range open {
|
||||
said = append(said, fmt.Sprintf("%s (%d)", l.Name, l.Port))
|
||||
}
|
||||
return catalogue.Listening{}, fmt.Errorf("%s holds %s and opens %s to the mesh: the artifact store's address is its one "+
|
||||
"listen open to the mesh (novox/hq ADR 0632 §1)", m.Module, StoreSeat, strings.Join(said, ", "))
|
||||
}
|
||||
|
||||
// artifactStoreOnNetwork is the machine on this network that holds the store's seat, and the port that machine put the
|
||||
// holder's listen on (novox/hq ADR 0632 §1).
|
||||
func (c *composer) artifactStoreOnNetwork(on map[string]bool) (node, port string, found bool, err error) {
|
||||
holders := c.storeHolders()
|
||||
if len(holders) == 0 {
|
||||
return "", "", false, nil
|
||||
}
|
||||
// In a stated order, so two machines offering it would always answer the same one.
|
||||
// In a stated order, so two machines holding it would always answer the same one.
|
||||
machines := make([]string, 0, len(on))
|
||||
for machine := range on {
|
||||
machines = append(machines, machine)
|
||||
@@ -349,17 +398,23 @@ func (c *composer) artifactStoreOnNetwork(on map[string]bool) (node, port string
|
||||
return "", "", false, fmt.Errorf("reading what %s is assigned: %w", machine, err)
|
||||
}
|
||||
for _, a := range n.Assigned {
|
||||
m, offers := providers[a]
|
||||
if !offers {
|
||||
m, holds := holders[a]
|
||||
if !holds {
|
||||
continue
|
||||
}
|
||||
serves, _, err := c.servedOnNode(machine, m, catalogue.ArtifactStoreProvision)
|
||||
l, err := storeListen(m)
|
||||
if err != nil {
|
||||
return "", "", false, err
|
||||
}
|
||||
ports, _, err := c.portsGivenOn(machine, m)
|
||||
if err != nil {
|
||||
return "", "", false, fmt.Errorf("reading where %s puts the artifact store: %w", machine, err)
|
||||
}
|
||||
if p, ok := serves["port"]; ok {
|
||||
return machine, fmt.Sprintf("%v", p), true, nil
|
||||
at := l.Port
|
||||
if given, ok := ports[l.Port]; ok {
|
||||
at = given
|
||||
}
|
||||
return machine, fmt.Sprintf("%d", at), true, nil
|
||||
}
|
||||
}
|
||||
return "", "", false, nil
|
||||
|
||||
@@ -254,7 +254,8 @@ func TestTheNodeEnginesContractGatesWhatItIsSent(t *testing.T) {
|
||||
func TestTheArtifactStoreIsReachedOverTheNetworkOrOnLoopbackForItsHolder(t *testing.T) {
|
||||
in := aMesh(t)
|
||||
in.Catalogue["artifacts"] = catalogue.Manifest{Module: "artifacts", Version: "1",
|
||||
Serves: map[string]map[string]any{catalogue.ArtifactStoreProvision: {"port": 5100}}}
|
||||
Claims: []catalogue.Claim{{Name: "container-image-registry", Scope: catalogue.ScopeMesh}},
|
||||
Listens: []catalogue.Listening{{Name: "registry", Port: 5100, From: catalogue.FromMesh}}}
|
||||
anchor := nodeOf(t, &in, "anchor")
|
||||
anchor.Assigned = append(anchor.Assigned, "artifacts")
|
||||
if got := placed(t, in, "laptop").Rendering.ArtifactStore; got != "anchor.internal:5100" {
|
||||
@@ -290,6 +291,41 @@ func TestTheArtifactStoreIsReachedOverTheNetworkOrOnLoopbackForItsHolder(t *test
|
||||
}
|
||||
}
|
||||
|
||||
// The store's address is the image seat's holder's listen open to the mesh, on the port its node put it on, and every
|
||||
// name of the store answers it (novox/hq ADR 0632 §1): a module serving the retired provision artifact-store is no store,
|
||||
// and a holder opening two listens to the mesh says which is the store by no guess.
|
||||
func TestTheStoreIsFoundByTheImageSeatsHolderAndItsListen(t *testing.T) {
|
||||
in := aMesh(t)
|
||||
in.Catalogue["old"] = catalogue.Manifest{Module: "old", Version: "1",
|
||||
Serves: map[string]map[string]any{catalogue.ArtifactStoreProvision: {"port": 5100}}}
|
||||
anchor := nodeOf(t, &in, "anchor")
|
||||
anchor.Assigned = append(anchor.Assigned, "old")
|
||||
if got := placed(t, in, "laptop").Rendering.ArtifactStore; got != "" {
|
||||
t.Errorf("a module serving artifact-store is reached as the store at %q", got)
|
||||
}
|
||||
in.Catalogue["registry"] = catalogue.Manifest{Module: "registry", Version: "1",
|
||||
Claims: []catalogue.Claim{{Name: "container-image-registry", Scope: catalogue.ScopeMesh}},
|
||||
Listens: []catalogue.Listening{{Name: "local", Port: 5001, From: catalogue.FromMachine},
|
||||
{Name: "registry", Port: 5000, From: catalogue.FromMesh}}}
|
||||
anchor.Assigned = append(anchor.Assigned, "registry")
|
||||
anchor.Ports = map[string]map[int]int{"registry": {5000: 5200}}
|
||||
r := placed(t, in, "laptop").Rendering
|
||||
if r.ArtifactStore != "anchor.internal:5200" {
|
||||
t.Errorf("laptop reaches the store at %q, want the holder's listen open to the mesh on anchor's port", r.ArtifactStore)
|
||||
}
|
||||
for _, seat := range []string{"mesh-artifact-store", "container-image-registry", "oci-artifact-registry"} {
|
||||
if r.SeatReach[seat] != "anchor.internal:5200" {
|
||||
t.Errorf("${seat:%s:reach} is %q", seat, r.SeatReach[seat])
|
||||
}
|
||||
}
|
||||
m := in.Catalogue["registry"]
|
||||
m.Listens = append(m.Listens, catalogue.Listening{Name: "other", Port: 5300, From: catalogue.FromMesh})
|
||||
in.Catalogue["registry"] = m
|
||||
if _, err := Places(in, "laptop"); err == nil || !strings.Contains(err.Error(), "opens registry (5000), other (5300) to the mesh") {
|
||||
t.Errorf("a holder opening two listens to the mesh: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A zone a module declares is placed where the mesh put it: settled from that node's settings, at the node's private address, on
|
||||
// the port the answering listen is published on there (novox/hq ADR 0199); the same zone declared twice is refused, by name.
|
||||
func TestAZoneIsPlacedWhereTheMeshPutItAndTheSameZoneTwiceIsRefused(t *testing.T) {
|
||||
|
||||
@@ -36,9 +36,9 @@ func artifactsInto(resource map[string]any, module string, with mcat.Rendering)
|
||||
if with.ArtifactStore == "" {
|
||||
return fmt.Errorf(
|
||||
"%s's %v names %s, which is in the mesh's artifact store, and this mesh has no "+
|
||||
"artifact store on its network to fetch it from — nothing assigned offers "+
|
||||
"%s, or the machine offering it is not on the private network",
|
||||
module, resource["id"], written, mcat.ArtifactStoreProvision)
|
||||
"artifact store on its network to fetch it from — nothing assigned holds the seat "+
|
||||
"container-image-registry, or the machine holding it is not on the private network",
|
||||
module, resource["id"], written)
|
||||
}
|
||||
resource[key] = mcat.Routed(written, with.ArtifactStore)
|
||||
imageSeatInto(resource, key, written, with)
|
||||
|
||||
@@ -34,9 +34,18 @@ var ofSeatReach = regexp.MustCompile(`\$\{seat:([a-z0-9][a-z0-9-]*):reach\}`)
|
||||
|
||||
// reachedSeats is every seat ${seat:…:reach} answers for.
|
||||
//
|
||||
// Both names of the store are answered with one address (ADR 0605, issue 1966 ENG-3): the seat it was held through and the
|
||||
// seat the registries' holder serves the same port as.
|
||||
var reachedSeats = map[string]bool{"mesh-artifact-store": true, "container-image-registry": true}
|
||||
// Every name of the store is answered with one address (ADR 0605, issue 1966 ENG-3, ADR 0632 §1): the seat it was held
|
||||
// through and the two registry seats its holder serves on the same port, the image seat's holder's listen open to the mesh.
|
||||
var reachedSeats = map[string]bool{}
|
||||
|
||||
// StoreSeats are the seats the artifact store is reached through, each answered with the one address.
|
||||
var StoreSeats = []string{"mesh-artifact-store", "container-image-registry", "oci-artifact-registry"}
|
||||
|
||||
func init() {
|
||||
for _, s := range StoreSeats {
|
||||
reachedSeats[s] = true
|
||||
}
|
||||
}
|
||||
|
||||
// seatInto replaces a resource's ${seat:…} placeholders with where this machine put each seat's
|
||||
// holder — in a file's content, and in a value of a container's or a process's environment. The
|
||||
|
||||
@@ -111,15 +111,18 @@ func TestASeatsReachIsWhereThisMachineReachesItsHolder(t *testing.T) {
|
||||
t.Fatalf("an environment value was filled with %q", got)
|
||||
}
|
||||
|
||||
// And the seat the registries' holder serves the same port through answers the same address (ENG-3).
|
||||
// And the seats the registries' holder serves the same port through answer the same address (ENG-3, ADR 0632 §1).
|
||||
both := map[string]any{"type": "process", "name": "p", "env": map[string]any{
|
||||
"OLD": "${seat:mesh-artifact-store:reach}", "NEW": "${seat:container-image-registry:reach}"}}
|
||||
"OLD": "${seat:mesh-artifact-store:reach}", "NEW": "${seat:container-image-registry:reach}",
|
||||
"OCI": "${seat:oci-artifact-registry:reach}"}}
|
||||
with.SeatReach["container-image-registry"] = "anchor.internal:5100"
|
||||
with.SeatReach["oci-artifact-registry"] = "anchor.internal:5100"
|
||||
if err := seatInto(both, "x", with); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if env := both["env"].(map[string]any); env["OLD"] != "anchor.internal:5100" || env["NEW"] != "anchor.internal:5100" {
|
||||
t.Fatalf("the two names of the store answer %v", env)
|
||||
if env := both["env"].(map[string]any); env["OLD"] != "anchor.internal:5100" || env["NEW"] != "anchor.internal:5100" ||
|
||||
env["OCI"] != "anchor.internal:5100" {
|
||||
t.Fatalf("the three names of the store answer %v", env)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,10 @@ func TestTheNodeKeyCarriesTheNodesFacts(t *testing.T) {
|
||||
if !delivery.SameNodeRecord(*got.Record, wantRecord) {
|
||||
t.Errorf("record = %+v, want %+v", *got.Record, wantRecord)
|
||||
}
|
||||
// Among them, that it fills a seat's reach from the seat holder's listen (ADR 0632 §1).
|
||||
if !slices.Contains(got.Record.ManifestReads, catalogue.ReadForm+catalogue.ReferenceFormSeatReach) {
|
||||
t.Errorf("the engine does not state it reads a seat's reach from its holder: %v", got.Record.ManifestReads)
|
||||
}
|
||||
at := l.kv["n1.node"].rev
|
||||
q.SayFacts(context.Background(), rep)
|
||||
if l.kv["n1.node"].rev != at {
|
||||
|
||||
Reference in New Issue
Block a user