Brings A3's parts receiver and bus adapters, the shared caller lists and builder versions question, a transfer read back from bus state (0424e87) and catalogue.SeatReference (c10ef07).c974a11adds nothing: its version_numbered.go and BuildAnswer.OutOfStep are the join's own. publish stays out (ADR 0627,1a149d4): packages_list_test keeps the join's form. TestAHolderTakesAFileInParts is a subtest of TestAFileTravelsInTheParts, so unit tests stay at 476. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
117 lines
5.0 KiB
Go
117 lines
5.0 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
|
|
contractseats "git.novox.be/novox/mesh-contracts/go/seats"
|
|
)
|
|
|
|
// 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 `<host>/<path>@sha256:…` is kept as its path; an
|
|
// archive it named `http://<host>/v2/<path>/blobs/<digest>` 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 _, _, seat := InRegistrySeat(reference); seat || 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: `<module>/<artifact>@<digest>`
|
|
// for an image, `<module>/<artifact>/blobs/<digest>` for a bundle or an archive. **It reads both forms** (novox/hq ADR
|
|
// 0605 §9): the older `artifact-store://<path>`, and the form a registry seat answers, `<seat>/<module>/<artifact>@<digest>`
|
|
// through container-image-registry or oci-artifact-registry, whose blob it names the way the older form did.
|
|
func InArtifactStore(reference string) (path string, kept bool) {
|
|
if seat, rest, ok := InRegistrySeat(reference); ok {
|
|
if seat == OciArtifactRegistrySeat {
|
|
name, digest, _ := strings.Cut(rest, "@")
|
|
return name + "/blobs/" + digest, true
|
|
}
|
|
return rest, true
|
|
}
|
|
return strings.CutPrefix(reference, ArtifactStoreScheme)
|
|
}
|
|
|
|
// RegistrySeats are the seats a reference of the registry form names first, the two that front the artifact store
|
|
// (novox/hq ADR 0605 §1, §9).
|
|
var RegistrySeats = []string{ContainerImageRegistrySeat, OciArtifactRegistrySeat}
|
|
|
|
// The four registry seats (novox/hq ADR 0605 §1), named here so the rest of mesh-sdk takes them from the seat set
|
|
// through catalogue alone (ADR 0625 §3).
|
|
const (
|
|
NpmPackageRegistrySeat = contractseats.NpmPackageRegistry
|
|
GoPackageRegistrySeat = contractseats.GoPackageRegistry
|
|
ContainerImageRegistrySeat = contractseats.ContainerImageRegistry
|
|
OciArtifactRegistrySeat = contractseats.OciArtifactRegistry
|
|
)
|
|
|
|
// PublishSeats are the seats an artifact is published through (ADR 0605 §1).
|
|
var PublishSeats = []string{NpmPackageRegistrySeat, GoPackageRegistrySeat, ContainerImageRegistrySeat, OciArtifactRegistrySeat}
|
|
|
|
// The seats of the vault, the provisioner, the hand acts' record and the conditions' keeper, as the seat set names them,
|
|
// for the rest of mesh-sdk (ADR 0625 §3).
|
|
const (
|
|
VaultSeat = contractseats.MeshVault
|
|
ProvisionerSeat = contractseats.MeshProvisioner
|
|
)
|
|
|
|
// SeatReference is the reference a build names an image, bundle, archive or upstream copy by once it published it
|
|
// through a registry seat (novox/hq ADR 0605 §9): `<seat>/<module>/<artifact>@sha256:<digest>`, as InRegistrySeat reads
|
|
// it back. The store's address is left out, as in the older form: a reader routes it (Routed).
|
|
func SeatReference(seat, module, artifact, digest string) string {
|
|
return seat + "/" + module + "/" + artifact + "@" + digest
|
|
}
|
|
|
|
// 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.
|
|
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 {
|
|
return "", "", false
|
|
}
|
|
for _, s := range RegistrySeats {
|
|
if first == s {
|
|
return s, rest, true
|
|
}
|
|
}
|
|
return "", "", false
|
|
}
|
|
|
|
// 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
|
|
}
|