diff --git a/go/catalogue/broker_seat_test.go b/go/catalogue/broker_seat_test.go index c2b5af4..11f5569 100644 --- a/go/catalogue/broker_seat_test.go +++ b/go/catalogue/broker_seat_test.go @@ -17,7 +17,11 @@ func TestRenamingASeatDidNotRenameTheInterfaceItDelivers(t *testing.T) { for _, pair := range []struct{ seat, delivers string }{ {"git", "git"}, {"npm-package-registry", "npm-package-registry"}, - {"mesh-artifact-store", "artifact-store"}, + // The store's seats deliver nothing since the provision artifact-store retired (novox/hq ADR 0632 §2): the + // store's address is its holder's listen, read through the seat. + {"mesh-artifact-store", ""}, + {"container-image-registry", ""}, + {"oci-artifact-registry", ""}, {"mesh-store", "postgres-database"}, {"mesh-broker", "mesh-bus"}, } { diff --git a/go/catalogue/manifest.go b/go/catalogue/manifest.go index 94046c1..765bdcf 100644 --- a/go/catalogue/manifest.go +++ b/go/catalogue/manifest.go @@ -1331,12 +1331,10 @@ const ( ArtifactPackage = "package" ) -// ArtifactStoreProvision is the name a module offers when it is the mesh's store for what modules -// ship — images, and archives, which are directories from a repository packed as blobs. -// -// Named here because a rule depends on it: what provides this cannot be delivered through it -// (novox/hq 04-ISSUES/029). A string compared in one place is a convention; a string a rule turns -// on is a fact, and it should be written once. +// ArtifactStoreProvision is the name the mesh's store for what modules ship (images, and archives, +// which are directories from a repository packed as blobs) was offered under until the provision +// retired (novox/hq ADR 0632 §2): no seat delivers it and no module provides it. It stays as the +// scheme of the older reference form, artifact-store://, which done keys keep (ArtifactStoreScheme). const ArtifactStoreProvision = "artifact-store" // Listening is one endpoint a module serves: a port it accepts connections on, and what may be said diff --git a/go/catalogue/manifest_reads.go b/go/catalogue/manifest_reads.go index 8da9384..ed39e75 100644 --- a/go/catalogue/manifest_reads.go +++ b/go/catalogue/manifest_reads.go @@ -5,6 +5,7 @@ import ( _ "embed" "encoding/json" "reflect" + "regexp" "slices" "sort" "strings" @@ -47,6 +48,16 @@ const ReferenceFormSeat = "seat-reference" // artifactReferenceForms are the reference forms of a build's artifacts this contract reads beyond the baseline's. var artifactReferenceForms = []string{ReferenceFormSeat} +// ReferenceFormSeatReach names how a node-engine built with this contract fills `${seat::reach}`, as the reader +// capability `form:seat-reach` (novox/hq ADR 0632 §1): from the seat holder placement names and the listen its manifest +// opens to the mesh, the store's three seats among them, never from a module serving the retired provision +// artifact-store. An engine before it filled the store's reach only from that provision, so a manifest naming a seat's +// reach needs it and is held from a node whose engine does not state it (ManifestNeeds). +const ReferenceFormSeatReach = "seat-reach" + +// ofSeatReach is a manifest's value naming a seat's reach, `${seat::reach}`. +var ofSeatReach = regexp.MustCompile(`\$\{seat:[a-z0-9][a-z0-9-]*:reach\}`) + // manifestTopWords are the top-level keys Manifest.UnmarshalJSON reads apart from the fields of manifestFields: each // read whole, its inside a map of the module's own names (requirements, local names) and values, no words of the // contract's beneath it. `requires` and `config` are read as their fields are (manifestNeeds). @@ -73,6 +84,7 @@ func ManifestReads() []string { for _, f := range artifactReferenceForms { set[ReadForm+f] = true } + set[ReadForm+ReferenceFormSeatReach] = true for _, s := range DefaultSeats() { set[ReadSeat+s.Name] = true } @@ -139,6 +151,9 @@ func ManifestNeeds(raw []byte) ([]string, error) { used[ReadForm+f] = true } } + if ofSeatReach.Match(raw) { + used[ReadForm+ReferenceFormSeatReach] = true + } if _, has := top[ConfigField]; has || takesConfigKey.Match(raw) { used[ReadForm+"config"] = true } diff --git a/go/catalogue/own_manifest_test.go b/go/catalogue/own_manifest_test.go index 2096a9f..ebfd61b 100644 --- a/go/catalogue/own_manifest_test.go +++ b/go/catalogue/own_manifest_test.go @@ -51,7 +51,7 @@ func TestOwnManifestUsesNoWordTheRunningControllerRefuses(t *testing.T) { } for _, want := range []string{"word:tool-runner", "word:core-credentials", "word:bus-state", "word:bus-state.storage", // The reference form a registry seat answers (ADR 0605 §9), and the seats the registry milestone adds. - ReadForm + ReferenceFormSeat, "seat:go-package-registry", "seat:container-image-registry", "seat:oci-artifact-registry"} { + ReadForm + ReferenceFormSeat, ReadForm + ReferenceFormSeatReach, "seat:go-package-registry", "seat:container-image-registry", "seat:oci-artifact-registry"} { if !slices.Contains(added, want) { t.Errorf("%s is in the baseline, or not read: added since it are %v", want, added) } @@ -79,6 +79,9 @@ func TestOwnManifestUsesNoWordTheRunningControllerRefuses(t *testing.T) { "word:provides.identity.max", "word:resources"}, nil}, {"a bus-state entry of requires", `{"module":"b","version":"1","requires":["mesh-bus",{"name":"jobs","storage":"kv"}]}`, []string{"word:requires", "word:bus-state.storage"}, []string{"word:bus-state", "word:bus-state.name", "word:bus-state.storage"}}, + // A seat's reach, filled from the seat holder's listen (ADR 0632 §1): an engine before it lacks the reading. + {"a seat's reach", `{"module":"s","version":"1","resources":[{"id":"f","type":"file","content":"${seat:container-image-registry:reach}"}]}`, + []string{"form:seat", "form:seat-reach"}, []string{"form:seat-reach"}}, {"a word this contract does not know", `{"module":"u","version":"1","newer-word":{"inside":1}}`, []string{"word:module"}, nil}, } { needs, err := ManifestNeeds([]byte(row.raw)) diff --git a/go/catalogue/seats_test.go b/go/catalogue/seats_test.go index 71c02ad..bd69cd5 100644 --- a/go/catalogue/seats_test.go +++ b/go/catalogue/seats_test.go @@ -61,6 +61,8 @@ func TestUseSeatsReplacesTheSetButNeverEmptiesIt(t *testing.T) { // A verb whose input the whole definition corrected: compose-candidate's input is the boolean the engine and the // checker read, where the compiled list said a word (engine_compose_candidate_test.go reads it). retyped := map[string]bool{"node-engine.compose-candidate": true} + // A seat whose provision retired delivers nothing since: the store's, artifact-store (ADR 0632 §2). + retired := map[string]bool{"mesh-artifact-store": true} norm := func(v any) any { b, _ := json.Marshal(v) var out any @@ -75,6 +77,9 @@ func TestUseSeatsReplacesTheSetButNeverEmptiesIt(t *testing.T) { t.Errorf("%s left the generated set", want.Name) continue } + if retired[want.Name] { + want.Delivers = "" + } g := golden{Seat: got} for _, v := range got.Serves { if v.Optional {