Genesis registers the control plane with the manifest its build produced
The control plane's manifest existed twice: at the root of its repository, read whenever the mesh rebuilds it from source, and as a copy in the catalogue, read by genesis. Nothing kept them equal, and the first rebuild replaced the mesh's record with the repository's shape while every later push was refused (novox/hq 04-ISSUES/072). The builder's one-shot result already carries the manifest it built, artifact resolved to the image; step 3 keeps it and step 9 registers it, re-pinning the built image's bare id to the reference the registry assigned. The catalogue is still read for the registry's and the builder's manifests and for phase two.
This commit is contained in:
@@ -53,26 +53,33 @@ type Permanent struct {
|
||||
// exactly the path for a value the mesh must carry and could not have invented — and they are read
|
||||
// out of the bundle this installer produced rather than reconstructed, because the bundle is what
|
||||
// created them and a second opinion about what a DSN should say is a second chance to be wrong.
|
||||
//
|
||||
// **The manifest is the one the build produced** — the manifest at the root of the control plane's
|
||||
// own repository, its artifact resolved to the image built at step 3 (novox/hq ADR 0069). The
|
||||
// installer used to read a second copy out of the catalogue and pin its placeholder; the copies
|
||||
// drifted, and the first rebuild from source replaced the mesh's record with the repository's shape
|
||||
// while every later push was refused on its behalf (novox/hq 04-ISSUES/072). There is one manifest
|
||||
// now, and the only thing this step changes in it is the image's name: the id the machine built it
|
||||
// under becomes the reference the registry assigned at step 8.
|
||||
func InstallControlPlane(ctx context.Context, o Options, d Deps, control controlPlane,
|
||||
foundation *declaration.Declaration, image string, say func(string)) (Permanent, error) {
|
||||
foundation *declaration.Declaration, built Built, image string, say func(string)) (Permanent, error) {
|
||||
|
||||
out := Permanent{Image: image}
|
||||
|
||||
manifest, err := readManifest(o.Catalogue, ControlPlaneModule)
|
||||
if err != nil {
|
||||
if len(built.Manifest) == 0 {
|
||||
return out, fmt.Errorf(
|
||||
"%w\n"+
|
||||
"This is the manifest that makes the control plane an ordinary module. Without it "+
|
||||
"the machine keeps the temporary control plane the foundation raised, which works "+
|
||||
"and cannot be upgraded — so the install stops here rather than pretending to "+
|
||||
"have pivoted", err)
|
||||
"the control plane was not built, so there is no manifest to register it with. " +
|
||||
"This is the manifest that makes the control plane an ordinary module. Without it " +
|
||||
"the machine keeps the temporary control plane the foundation raised, which works " +
|
||||
"and cannot be upgraded — so the install stops here rather than pretending to " +
|
||||
"have pivoted")
|
||||
}
|
||||
|
||||
pinned, places, err := pinImage(manifest, image, ControlPlaneModule)
|
||||
pinned, places, err := pinImage(built.Manifest, built.Image, image, ControlPlaneModule)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
say(fmt.Sprintf(" pinned %s, in %d place(s)", image, places))
|
||||
say(fmt.Sprintf(" pinned %s → %s, in %d place(s)", shortRef(built.Image), image, places))
|
||||
|
||||
container, _, err := containerIn(pinned, controlPlaneResourceIn(pinned))
|
||||
if err != nil {
|
||||
@@ -118,52 +125,34 @@ func InstallControlPlane(ctx context.Context, o Options, d Deps, control control
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// pinImage replaces the catalogue's placeholder digest with what the registry assigned.
|
||||
// pinImage replaces the image the build named with the reference the registry assigned.
|
||||
//
|
||||
// **Textual, and every place it appears.** A manifest may name its image in more than one resource
|
||||
// — the catalogue's converted modules routinely carry a runtime container beside the application's
|
||||
// — and the same reasoning as the bundle rewrite applies: replacing one and not the others leaves
|
||||
// something pointing at an image nothing serves, and it fails half way through an apply rather
|
||||
// than here.
|
||||
// — a migrate step beside the server, a runtime beside the application — and the same reasoning
|
||||
// as the bundle rewrite applies: replacing one and not the others leaves something pointing at an
|
||||
// image nothing serves, and it fails half way through an apply rather than here.
|
||||
//
|
||||
// It refuses a manifest with no placeholder in it. That is not pedantry: a manifest already
|
||||
// carrying a real digest is one somebody pinned by hand, and quietly registering it would install a
|
||||
// control plane that is not the image this machine just published — which is the one thing this
|
||||
// step exists to guarantee.
|
||||
func pinImage(manifest []byte, reference, module string) ([]byte, int, error) {
|
||||
places := bytes.Count(manifest, []byte(placeholderDigest))
|
||||
// The built image is named by the digest of its own configuration, `sha256:…` with no registry in
|
||||
// front, which only the machine that built it can resolve. The whole JSON string moves to the
|
||||
// registry's `<registry>/<repository>@sha256:…`, so every machine the module is later pushed to
|
||||
// pulls it from the mesh's own store.
|
||||
//
|
||||
// It refuses a manifest that does not name the built image. That is not pedantry: a manifest
|
||||
// naming some other image is one that describes some other build, and quietly registering it would
|
||||
// install a control plane that is not the image this machine just published — which is the one
|
||||
// thing this step exists to guarantee.
|
||||
func pinImage(manifest []byte, built, reference, module string) ([]byte, int, error) {
|
||||
from := []byte(`"` + built + `"`)
|
||||
places := bytes.Count(manifest, from)
|
||||
if places == 0 {
|
||||
return nil, 0, fmt.Errorf(
|
||||
"the %s module's manifest carries no placeholder digest (%s), so there is nothing to "+
|
||||
"pin to the image this machine just published.\n"+
|
||||
"A manifest already naming a digest was pinned by somebody else, to some other "+
|
||||
"build. Registering it would install a module that is not the one this "+
|
||||
"installer carried and pushed", module, placeholderDigest)
|
||||
"the %s module's manifest does not name the image this machine built (%s), so there "+
|
||||
"is nothing to pin to the image it just published.\n"+
|
||||
"A manifest naming some other image describes some other build. Registering it "+
|
||||
"would install a module that is not the one this installer built and pushed",
|
||||
module, built)
|
||||
}
|
||||
// The reference the registry gave back is `<registry>/<repository>@sha256:…`, and what the
|
||||
// manifest holds is `<something>@sha256:0…0`. Replacing only the digest would leave the
|
||||
// manifest's own repository name in front of it — which may be `mesh-controller` with no
|
||||
// registry, and a runtime would then pull it from the internet. The whole reference moves.
|
||||
var out bytes.Buffer
|
||||
rest := manifest
|
||||
for {
|
||||
at := bytes.Index(rest, []byte(placeholderDigest))
|
||||
if at < 0 {
|
||||
out.Write(rest)
|
||||
break
|
||||
}
|
||||
// Back up over the repository this digest belongs to, which runs to the opening quote.
|
||||
start := bytes.LastIndexByte(rest[:at], '"')
|
||||
if start < 0 {
|
||||
return nil, 0, fmt.Errorf(
|
||||
"the %s module's manifest has a placeholder digest that is not inside a JSON "+
|
||||
"string, so the installer cannot tell what image it belongs to", module)
|
||||
}
|
||||
out.Write(rest[:start+1])
|
||||
out.WriteString(reference)
|
||||
rest = rest[at+len(placeholderDigest):]
|
||||
}
|
||||
pinned := out.Bytes()
|
||||
pinned := bytes.ReplaceAll(manifest, from, []byte(`"`+reference+`"`))
|
||||
|
||||
// Read back. A substitution on text can catch more than it was aimed at, and the manifest is
|
||||
// about to be handed to the mesh as the description of what it runs.
|
||||
@@ -172,17 +161,16 @@ func pinImage(manifest []byte, reference, module string) ([]byte, int, error) {
|
||||
return nil, 0, fmt.Errorf(
|
||||
"pinning the %s module's image broke its manifest: %w", module, err)
|
||||
}
|
||||
if bytes.Contains(pinned, []byte(placeholderDigest)) {
|
||||
if bytes.Contains(pinned, from) {
|
||||
return nil, 0, fmt.Errorf(
|
||||
"the %s module's manifest still carries a placeholder digest after pinning",
|
||||
module)
|
||||
"the %s module's manifest still names the built image after pinning", module)
|
||||
}
|
||||
return pinned, places, nil
|
||||
}
|
||||
|
||||
// controlPlaneResourceIn is the id of the resource that runs the control plane.
|
||||
//
|
||||
// The manifest is written by the catalogue and the installer does not get to name its resources.
|
||||
// The manifest is the control plane's own and the installer does not get to name its resources.
|
||||
// What it can do is find the one container whose image is the one just pinned — and when a manifest
|
||||
// declares exactly one container, that is the answer without any searching at all.
|
||||
func controlPlaneResourceIn(manifest []byte) string {
|
||||
|
||||
Reference in New Issue
Block a user