package resolution import ( "fmt" "strings" mcat "git.novox.be/novox/mesh-sdk/go/catalogue" ) // 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 mcat.Rendering) error { for _, key := range []string{"image", "source"} { written, ok := resource[key].(string) if !ok { continue } if _, kept := mcat.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, mcat.ArtifactStoreProvision) } resource[key] = mcat.Routed(written, with.ArtifactStore) imageSeatInto(resource, key, written, with) continue } if with.ArtifactStore == "" { continue } recorded := mcat.Recorded(written) if recorded == written { continue } path, _ := mcat.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] = mcat.Routed(recorded, with.ArtifactStore) imageSeatInto(resource, key, written, with) } } return nil } // artifactSeat is the seat an image is routed through: the artifact store's (the registries' four seats follow as the // references name them). const artifactSeat = "mesh-artifact-store" // imageSeatInto writes beside a container's routed image the seat the reference came through and that seat's holder (design 51 // *distribution's first delivery*, issue 1966 ENG-10), so the node-engine finds by the declaration whom a failed pull waits // on, never by a module's name. Neither is part of what a container is: a declaration composed again for a new holder // replaces no container. Nothing is written where the holder is not on record, or for a resource that is no container. func imageSeatInto(resource map[string]any, key, written string, with mcat.Rendering) { if key != "image" || resource["type"] != "container" { return } // The seat a reference of the registry form names; the older forms came through the artifact store's. seat := artifactSeat if named, _, ok := mcat.InRegistrySeat(written); ok { seat = named } if with.SeatHolder[seat] == "" { return } resource["image-seat"] = seat resource["image-holder"] = with.SeatHolder[seat] }