package catalogue import ( "fmt" "strings" ) // What the mesh built, named by what it is rather than by where it was pushed. // // **An image is recorded by its digest and its path; the registry's address is a route to it** // (novox/hq 04-ISSUES/102). A build used to be recorded as `://@sha256:…` // — the reference the builder pushed to, kept whole — and every declaration carried that literal. // Move the registry's port, or the registry, and every fresh pull of a mesh image fails: a new // node, a recreate after eviction. The digest is the identity; the address is the node's setting // for the module that serves the artifact store, and it is read from there when a reference is // composed, never written into a record. // // So a kept reference has a scheme of the mesh's own, named after the provision that answers it: // // artifact-store:///@sha256: an image // artifact-store:////blobs/sha256: an archive // // No runtime knows the scheme. That is the point of it being one: a reference that leaks to a // machine uncomposed is refused by the runtime as malformed, in front of whoever sent it, rather // than pulled from a public registry that happens to have a repository by that name — which is // what an address-less `/@sha256:…` would be. // ArtifactStoreScheme marks a reference to something in the mesh's artifact store, kept without // the store's address. const ArtifactStoreScheme = ArtifactStoreProvision + "://" // Recorded is a reference as the mesh records it: the artifact store's address, if the builder wrote // one, taken off. // // For a reference the builder announced — one it pushed to the store — which is the only kind // this is called on. An image the builder named `/@sha256:…` is kept as its path; an // archive it named `http:///v2//blobs/` likewise. A reference with no address // in it — an image id from a genesis build that had nowhere to publish, a package version — is // what it was. func Recorded(reference string) string { if strings.HasPrefix(reference, ArtifactStoreScheme) { return reference } if rest, isURL := strings.CutPrefix(reference, "http://"); isURL { if _, path, ok := strings.Cut(rest, "/v2/"); ok && strings.Contains(path, "/blobs/") { return ArtifactStoreScheme + path } return reference } host, path, ok := strings.Cut(reference, "/") if !ok || !isRegistryHost(host) || !strings.Contains(path, "@sha256:") { return reference } return ArtifactStoreScheme + path } // isRegistryHost is the runtime's own rule for reading the first component of a reference as a // registry rather than as a namespace: it has a dot or a port in it, or it is localhost. func isRegistryHost(component string) bool { return component == "localhost" || strings.ContainsAny(component, ".:") } // InArtifactStore reports whether a reference is a kept one, and what it names there. func InArtifactStore(reference string) (path string, kept bool) { return strings.CutPrefix(reference, ArtifactStoreScheme) } // 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 { path, kept := InArtifactStore(reference) if !kept { return reference } if strings.Contains(path, "/blobs/") { return "http://" + address + "/v2/" + path } return address + "/" + path } // Rerouted is a reference the mesh recorded, whichever way it was recorded, as a machine fetches // it now: a kept one composed with the store's address, and one recorded before references were // kept without their address — the builder's own `/@sha256:…` — re-routed to where // the store is now. Only for references that are the mesh's own: everything a build record // holds is, by construction. func Rerouted(reference, address string) string { return Routed(Recorded(reference), address) } // artifactsInto composes the artifact store's address into a resource's `image` and `source`. // // **Composed here, at the last moment before a machine, and stored nowhere.** A kept reference is // routed through the store as this network reaches it now. A reference recorded with an address // before references were kept without one is re-routed the same way — but only when the mesh // built it (`with.Built` names every `/` it has), because a module may run an // image from a public registry under its own name and that one is exactly where it says. // // A kept reference with no store to route it through is refused: sent as it is, the runtime would // refuse the scheme on the machine, one push away from the reason. // // **What this does not reach: the images genesis pinned.** The installer builds the control plane // and the builder before the mesh exists, pushes them itself and pins their manifests to // `:/mesh-controller@…` and `:/mesh-builder@…` — single-segment // repositories with no build record, so `with.Built` does not name them and they are left as // written until each is rebuilt through the mesh, which records it by digest and path. Until then // a registry that moves strands exactly those two on a recreate, and the control plane's is the // one that cannot be repaired through the mesh. Rebuild both through `build` before moving the // store (novox/hq 04-ISSUES/102, finding F4). func artifactsInto(resource map[string]any, module string, with Rendering) error { for _, key := range []string{"image", "source"} { written, ok := resource[key].(string) if !ok { continue } if _, kept := InArtifactStore(written); kept { 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, ArtifactStoreProvision) } resource[key] = Routed(written, with.ArtifactStore) continue } if with.ArtifactStore == "" { continue } recorded := Recorded(written) if recorded == written { continue } path, _ := InArtifactStore(recorded) repository := path if at := strings.IndexAny(path, "@"); at >= 0 { repository = path[:at] } else if blobs := strings.Index(path, "/blobs/"); blobs >= 0 { repository = path[:blobs] } if with.Built[repository] { resource[key] = Routed(recorded, with.ArtifactStore) } } return nil }