J8b: catalogue reads the holder's upstream naming, and a done key may name an upstream copy by it
An upstream copy kept by the artifact store is named by the holder, upstream/<host>/<path>@<digest>, in the seat form (container-image-registry/upstream/...) and the older one (artifact-store://upstream/...). InRegistrySeat reads it, InUpstream reads its host and path, and referenceProblem accepts it for an upstream artifact, so readers stop reading it on their own. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -89,9 +89,18 @@ func SeatReference(seat, module, artifact, digest string) string {
|
||||
// InRegistrySeat reads a reference in the form a registry seat answers, `<seat>/<module>/<artifact>@sha256:<digest>`:
|
||||
// the seat it came through and the rest. A seat's name holds no dot or port, so no image of an outside registry reads
|
||||
// as one, and a kept `artifact-store://` reference is not one.
|
||||
//
|
||||
// It also reads the holder's own naming of an upstream copy through container-image-registry,
|
||||
// `container-image-registry/upstream/<host>/<path>@sha256:<digest>`, whose rest InUpstream reads.
|
||||
func InRegistrySeat(reference string) (seat, path string, ok bool) {
|
||||
first, rest, found := strings.Cut(reference, "/")
|
||||
if !found || !strings.Contains(rest, "@sha256:") || strings.Count(rest, "/") != 1 {
|
||||
if !found || !strings.Contains(rest, "@sha256:") {
|
||||
return "", "", false
|
||||
}
|
||||
if _, _, upstream := InUpstream(rest); upstream && first == ContainerImageRegistrySeat {
|
||||
return first, rest, true
|
||||
}
|
||||
if strings.Count(rest, "/") != 1 {
|
||||
return "", "", false
|
||||
}
|
||||
for _, s := range RegistrySeats {
|
||||
@@ -102,6 +111,27 @@ func InRegistrySeat(reference string) (seat, path string, ok bool) {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// UpstreamPrefix opens the holder's own name for an upstream copy it keeps: `upstream/<host>/<path>@<digest>`, after
|
||||
// `container-image-registry/` in the seat's form and after `artifact-store://` in the older one.
|
||||
const UpstreamPrefix = "upstream/"
|
||||
|
||||
// InUpstream reads a path in the holder's naming of an upstream copy, `upstream/<host>/<path>@sha256:<digest>` (the path
|
||||
// InRegistrySeat or InArtifactStore gives back): the upstream registry host and the image's path there, with its digest.
|
||||
// A host is read as the runtime reads one (a dot, a port, or localhost).
|
||||
func InUpstream(path string) (host, image string, ok bool) {
|
||||
rest, found := strings.CutPrefix(path, UpstreamPrefix)
|
||||
if !found {
|
||||
return "", "", false
|
||||
}
|
||||
host, image, found = strings.Cut(rest, "/")
|
||||
name, digest, pinned := strings.Cut(image, "@")
|
||||
if !found || !isRegistryHost(host) || name == "" || strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") ||
|
||||
strings.Contains(name, "//") || !pinned || !strings.HasPrefix(digest, "sha256:") || strings.Contains(digest, "/") {
|
||||
return "", "", false
|
||||
}
|
||||
return host, image, true
|
||||
}
|
||||
|
||||
// Routed is a kept reference as a machine fetches it, through the artifact store at `address`
|
||||
// (host:port). A reference that is not a kept one is what it was.
|
||||
func Routed(reference, address string) string {
|
||||
|
||||
@@ -37,6 +37,9 @@ func TestAReferenceIsRecordedWithoutTheStoresAddress(t *testing.T) {
|
||||
"oci-artifact-registry/gitea/config@" + digest: "http://10.0.0.1:5000/v2/gitea/config/blobs/" + digest,
|
||||
"registry.example/gitea/server@" + digest: "registry.example/gitea/server@" + digest,
|
||||
"mesh-artifact-store/gitea/server@" + digest: "mesh-artifact-store/gitea/server@" + digest,
|
||||
// An upstream copy under the holder's own naming, in both forms.
|
||||
"container-image-registry/upstream/quay.io:443/org/app@" + digest: "10.0.0.1:5000/upstream/quay.io:443/org/app@" + digest,
|
||||
ArtifactStoreScheme + "upstream/quay.io:443/org/app@" + digest: "10.0.0.1:5000/upstream/quay.io:443/org/app@" + digest,
|
||||
} {
|
||||
if got := Routed(reference, "10.0.0.1:5000"); got != want {
|
||||
t.Errorf("Routed(%q) = %q, want %q", reference, got, want)
|
||||
@@ -49,4 +52,19 @@ func TestAReferenceIsRecordedWithoutTheStoresAddress(t *testing.T) {
|
||||
t.Errorf("SeatReference(%s) = %q, read back as %q %q %v", seat, ref, got, path, ok)
|
||||
}
|
||||
}
|
||||
// The holder's naming of an upstream copy reads through the image seat only, and InUpstream reads its host and path.
|
||||
for reference, want := range map[string]bool{
|
||||
"container-image-registry/upstream/quay.io:443/org/app@" + digest: true,
|
||||
"oci-artifact-registry/upstream/quay.io:443/org/app@" + digest: false,
|
||||
"container-image-registry/upstream/org/app@" + digest: false,
|
||||
"container-image-registry/upstream/quay.io/app": false,
|
||||
} {
|
||||
_, path, ok := InRegistrySeat(reference)
|
||||
if ok != want {
|
||||
t.Errorf("InRegistrySeat(%q) read %v, want %v", reference, ok, want)
|
||||
}
|
||||
if host, image, up := InUpstream(path); ok && (!up || host != "quay.io:443" || image != "org/app@"+digest) {
|
||||
t.Errorf("InUpstream(%q) = %q %q %v", path, host, image, up)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,16 @@ func (a BuiltArtifact) referenceProblem() string {
|
||||
if r == "" || a.Kind == "" {
|
||||
return ""
|
||||
}
|
||||
// An upstream copy may be named by the holder's own naming, upstream/<host>/<path>@<digest>, in either form: the
|
||||
// holder names it, not the module that asked for it (catalogue.InUpstream).
|
||||
if path, kept := catalogue.InArtifactStore(r); kept && a.Kind == "upstream" {
|
||||
if _, image, ok := catalogue.InUpstream(path); ok {
|
||||
if _, digest, _ := strings.Cut(image, "@"); digest != a.Digest {
|
||||
return "does not name its digest " + a.Digest
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if seat, path, ok := catalogue.InRegistrySeat(r); ok {
|
||||
want := catalogue.ContainerImageRegistrySeat
|
||||
if a.Kind == "bundle" || a.Kind == "archive" {
|
||||
|
||||
@@ -29,6 +29,9 @@ func TestADoneKeyNamesEachArtifactsReferenceInTheFormOfItsKind(t *testing.T) {
|
||||
artifactOf("files", "archive", d, "oci-artifact-registry/mail/files@"+d),
|
||||
artifactOf("server", "image", d, "container-image-registry/mail/server@"+d),
|
||||
artifactOf("nats", "upstream", d, "container-image-registry/nats/nats@"+d),
|
||||
// An upstream copy under the holder's own naming, upstream/<host>/<path>, in either form (ADR 0624 §1).
|
||||
artifactOf("app", "upstream", d, "container-image-registry/upstream/quay.io:443/org/app@"+d),
|
||||
artifactOf("app", "upstream", d, "artifact-store://upstream/quay.io:443/org/app@"+d),
|
||||
}
|
||||
for _, a := range good {
|
||||
b := doneBuild()
|
||||
@@ -53,6 +56,10 @@ func TestADoneKeyNamesEachArtifactsReferenceInTheFormOfItsKind(t *testing.T) {
|
||||
artifactOf("server", "image", d, "container-image-registry/mail/server@"+other),
|
||||
artifactOf("server", "image", d, "container-image-registry/mail/other@"+d),
|
||||
artifactOf("lib", "package", d, "container-image-registry/mail/lib@"+d),
|
||||
artifactOf("app", "upstream", d, "container-image-registry/upstream/quay.io:443/org/app@"+other),
|
||||
artifactOf("app", "upstream", d, "artifact-store://upstream/quay.io:443/org/app@"+other),
|
||||
artifactOf("app", "upstream", d, "oci-artifact-registry/upstream/quay.io:443/org/app@"+d),
|
||||
artifactOf("server", "image", d, "container-image-registry/upstream/quay.io:443/org/server@"+d),
|
||||
}
|
||||
for _, a := range bad {
|
||||
b := doneBuild()
|
||||
|
||||
Reference in New Issue
Block a user