Genesis registers the control plane with the manifest its build produced (hq issue 072) #17

Merged
jschoubben merged 3 commits from feat/one-controller-manifest into main 2026-09-21 17:23:18 +00:00
8 changed files with 270 additions and 99 deletions
+1 -1
View File
@@ -221,7 +221,7 @@ func newFlagSet(opts *bootstrap.Options, jsonOut *bool) *flag.FlagSet {
set.StringVar(&opts.Out, "out", opts.Out, "where the produced bundle is written")
set.StringVar(&opts.State, "state", opts.State, "where this node records what it has applied")
set.StringVar(&opts.Catalogue, "catalog", opts.Catalogue,
"a checkout of the mesh's catalogue; without it this stops after the foundation")
"a checkout of the mesh's catalogue, for the registry's and the builder's manifests and what phase two installs; without it this stops after the foundation")
set.StringVar(&opts.Source.Repository, "source", opts.Source.Repository,
"the repository the control plane is built from, on a mesh that already exists")
set.StringVar(&opts.Source.Ref, "source-ref", opts.Source.Ref,
+6 -4
View File
@@ -137,9 +137,11 @@ type Options struct {
Node string
// Catalogue is a checkout of the mesh's catalogue repository, which is where the registry's and
// the control plane's manifests are read from. Empty stops the installer after the foundation:
// there is no pivot without manifests, and pretending otherwise would leave a machine that
// looks installed and cannot upgrade itself.
// the builder's manifests are read from, and everything phase two installs. Not the control
// plane's: that one comes out of the build at step 3, from the root of its own repository
// (novox/hq ADR 0069). Empty stops the installer after the foundation: there is no pivot
// without manifests, and pretending otherwise would leave a machine that looks installed and
// cannot upgrade itself.
Catalogue string
// Source is where the control plane is built from — a repository on a mesh that already
@@ -585,7 +587,7 @@ func Run(ctx context.Context, o Options, d Deps, say func(string)) (Result, erro
// ---- 9. control plane -----------------------------------------------------------------
say("control plane — installed as an ordinary module, pinned to that digest")
permanent, err := InstallControlPlane(ctx, o, d, temporary, rewritten.Declaration,
published.Reference, say)
built, published.Reference, say)
result.Permanent, result.PermanentAnswered = permanent.Container, permanent.Answered
result.StoresDelivered = permanent.Delivered
if err != nil {
+25 -1
View File
@@ -1,6 +1,7 @@
package bootstrap
import (
"bytes"
"context"
"encoding/json"
"errors"
@@ -57,12 +58,18 @@ type Built struct {
// Image is the artifact, named by the digest of its own configuration — the identity a machine
// can use with nothing serving it, and the same one the installer used for a carried image.
Image string
// Manifest is the module as the mesh should hold it: the manifest at the root of the repository
// that was built, its artifact resolved to Image. It is the control plane's ONE manifest
// (novox/hq ADR 0069) — the installer used to read a second copy out of the catalogue, and the
// two drifted apart the first time somebody edited one (novox/hq 04-ISSUES/072).
Manifest []byte
}
// builderOutput is the part of the builder's one-shot result this needs.
type builderOutput struct {
Module string `json:"module"`
Commit string `json:"commit"`
Manifest json.RawMessage `json:"manifest"`
Made []struct {
Name string `json:"name"`
Kind string `json:"kind"`
@@ -142,8 +149,25 @@ func BuildControlPlane(ctx context.Context, run Runner, builderTag string, sourc
result.Module, len(images))
}
// The manifest is what the mesh will hold the control plane as, so a result without one is
// a build the installer cannot finish — refused here, beside the builder that said it, rather
// than at step 9 with a message about a missing file.
manifest := bytes.TrimSpace(result.Manifest)
if len(manifest) == 0 || bytes.Equal(manifest, []byte("null")) {
return Built{}, fmt.Errorf(
"%s built, and the builder reported no manifest for it. The installer registers the "+
"control plane with the manifest the build produced — the one at the root of its "+
"repository, its artifact resolved — and has no other copy to use", result.Module)
}
if !bytes.Contains(manifest, []byte(`"`+images[0]+`"`)) {
return Built{}, fmt.Errorf(
"%s built %s, and the manifest the builder reported does not name that image, so "+
"the installer cannot tell which of its resources runs the control plane",
result.Module, images[0])
}
say(fmt.Sprintf(" built %s from %s", result.Module, shortRef(result.Commit)))
return Built{Module: result.Module, Commit: result.Commit, Image: images[0]}, nil
return Built{Module: result.Module, Commit: result.Commit, Image: images[0], Manifest: manifest}, nil
}
func shortRef(ref string) string {
+54
View File
@@ -1,6 +1,7 @@
package bootstrap
import (
"context"
"strings"
"testing"
)
@@ -39,3 +40,56 @@ func TestARepositoryAndACommitIsEnough(t *testing.T) {
t.Fatalf("a repository and a commit were refused: %v", err)
}
}
// **The build hands over the manifest, and the installer registers that one.** The control plane
// used to have two manifests — one at the root of its repository, which the mesh reads whenever
// it rebuilds the control plane from source, and a copy in the catalogue, which genesis read — and
// nothing kept them equal (novox/hq 04-ISSUES/072). The builder already reports the manifest it
// built, artifact resolved to the image; genesis takes it from there and reads no second copy.
func TestTheBuildHandsOverTheManifestTheMeshWillHold(t *testing.T) {
manifest := `{"module":"mesh-controller","version":"1","resources":[` +
`{"id":"server","type":"container","name":"mesh-controller","image":"` + builtImage + `"}]}`
runtime := &asked{answer: func(string, []string) (string, error) {
return `{"module":"mesh-controller","commit":"a1b2c3d4","manifest":` + manifest +
`,"made":[{"name":"server","kind":"image","reference":"` + builtImage + `"}]}` + "\n", nil
}}
built, err := BuildControlPlane(context.Background(), runtime.run, "mesh-builder:test",
Source{Repository: "https://example.invalid/mesh-controller.git", Ref: "a1b2c3d4"}, false, func(string) {})
if err != nil {
t.Fatal(err)
}
if built.Image != builtImage {
t.Errorf("the built image is %q", built.Image)
}
if string(built.Manifest) != manifest {
t.Errorf("the manifest handed over is not the one the builder reported:\n%s", built.Manifest)
}
}
// A result without a manifest is a build the installer cannot finish, and it is refused beside the
// builder that said it rather than at step 9 with a message about a missing file.
func TestABuildReportingNoManifestIsRefused(t *testing.T) {
runtime := &asked{answer: func(string, []string) (string, error) {
return `{"module":"mesh-controller","commit":"a1b2c3d4",` +
`"made":[{"name":"server","kind":"image","reference":"` + builtImage + `"}]}`, nil
}}
_, err := BuildControlPlane(context.Background(), runtime.run, "mesh-builder:test",
Source{Repository: "https://example.invalid/mesh-controller.git", Ref: "a1b2c3d4"}, false, func(string) {})
if err == nil || !strings.Contains(err.Error(), "no manifest") {
t.Fatalf("a build reporting no manifest was accepted, or refused for another reason: %v", err)
}
}
// And a manifest that does not name the image the build produced describes some other build.
func TestABuildWhoseManifestNamesAnotherImageIsRefused(t *testing.T) {
runtime := &asked{answer: func(string, []string) (string, error) {
return `{"module":"mesh-controller","commit":"a1b2c3d4","manifest":{"module":"mesh-controller",` +
`"resources":[{"id":"server","type":"container","image":"sha256:` + strings.Repeat("9", 64) + `"}]},` +
`"made":[{"name":"server","kind":"image","reference":"` + builtImage + `"}]}`, nil
}}
_, err := BuildControlPlane(context.Background(), runtime.run, "mesh-builder:test",
Source{Repository: "https://example.invalid/mesh-controller.git", Ref: "a1b2c3d4"}, false, func(string) {})
if err == nil || !strings.Contains(err.Error(), "does not name that image") {
t.Fatalf("a manifest naming another image was accepted, or refused for another reason: %v", err)
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ func InstallBuilder(ctx context.Context, o Options, d Deps, control controlPlane
"This is the manifest that makes the builder an ordinary module. Without it the mesh "+
"has the image and no way to run it, so nothing can be built here", err)
}
pinned, places, err := pinImage(manifest, published.Reference, BuilderModule)
pinned, places, err := pinPlaceholder(manifest, published.Reference, BuilderModule)
if err != nil {
return out, err
}
+49 -51
View File
@@ -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"+
"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", err)
"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)", shortImage(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 {
@@ -410,3 +398,13 @@ func sortedKeys(m map[string]string) []string {
sort.Strings(keys)
return keys
}
// shortImage is an image id as a person reads one: the first twelve hex digits, without the
// algorithm in front — `shortRef` would keep the prefix and show one digit of the digest.
func shortImage(id string) string {
digest := strings.TrimPrefix(id, "sha256:")
if len(digest) > 12 {
return digest[:12]
}
return digest
}
+62 -35
View File
@@ -11,13 +11,15 @@ import (
// that could go wrong quietly: pinning it to the wrong image, and delivering it store connections
// the mesh invented rather than the ones the foundation actually made.
// theControlPlaneModule is the catalogue's manifest, trimmed to what this installer reads.
// theControlPlaneModule is the manifest the build produces at step 3: the control plane's own,
// from the root of its repository, its artifact resolved to the image the machine built — named by
// the digest of its own configuration, with no registry in front (novox/hq ADR 0069).
//
// A fixture rather than the file itself, unlike the foundation example the rewrite tests use: the
// catalogue is a different repository on a different branch, and a test that read it would pass or
// fail according to what somebody else had checked out. What it must stay faithful to is the
// SHAPE — the placeholder digest, the own-secret per context, the mount from the machine's path to
// the container's, and the environment file that fills what is not a path.
// control plane is a different repository on a different branch, and a test that read it would
// pass or fail according to what somebody else had checked out. What it must stay faithful to is
// the SHAPE — the built image's bare id, the own-secret per context, the mount from the machine's
// path to the container's, and the environment file that fills what is not a path.
const theControlPlaneModule = `{
"module": "mesh-controller",
"version": "1",
@@ -37,7 +39,7 @@ const theControlPlaneModule = `{
"mode": "0600",
"content": "MESH_BROKER_AMQP=${secret:broker}\nMESH_BROKER_MANAGEMENT=${secret:broker-management}\nMESH_BROKER_ADDRESS=${machine:at}:5671\n"},
{"id": "server", "type": "container", "name": "mesh-controller",
"image": "mesh-controller@` + placeholderDigest + `",
"image": "` + builtImage + `",
"network": "host", "args": ["serve"],
"env-file": ["/var/lib/mesh/mesh-controller/broker.env"],
"env": {
@@ -58,57 +60,62 @@ const theControlPlaneModule = `{
const pushedReference = "127.0.0.1:5000/mesh-controller@sha256:" +
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
// **The whole reference moves, not only the digest.** The manifest's placeholder names a
// repository too, and replacing sixty-four zeros inside it would leave `mesh-controller@sha256:…`
// with no registry in front — which a runtime would go to the internet for, and this mesh's
// control plane exists in no public registry by design.
// builtImage is what step 3 built, as the machine that built it names it.
const builtImage = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
// theBuild is what step 3 hands step 9.
func theBuild(manifest string) Built {
return Built{Module: "mesh-controller", Commit: "a1b2c3d4", Image: builtImage, Manifest: []byte(manifest)}
}
// **The whole reference moves.** The build names its image by the bare digest of its configuration,
// which only the machine that built it can resolve; the mesh's record must name what the registry
// assigned, `<registry>/<repository>@sha256:…`, or every other machine the module is pushed to
// would go looking for an image nothing serves.
func TestTheControlPlaneIsPinnedToWhatThisMeshsRegistryAssigned(t *testing.T) {
pinned, places, err := pinImage([]byte(theControlPlaneModule), pushedReference, "mesh-controller")
pinned, places, err := pinImage([]byte(theControlPlaneModule), builtImage, pushedReference, "mesh-controller")
if err != nil {
t.Fatal(err)
}
if places != 1 {
t.Errorf("the placeholder was found in %d place(s)", places)
t.Errorf("the built image was found in %d place(s)", places)
}
if !strings.Contains(string(pinned), `"image": "`+pushedReference+`"`) {
t.Errorf("the manifest does not name the pushed image:\n%s", pinned)
}
if strings.Contains(string(pinned), `"mesh-controller@sha256:`) {
t.Errorf("the digest was replaced and the manifest's own repository name was left in "+
"front of it, so nothing says which registry serves it:\n%s", pinned)
if strings.Contains(string(pinned), builtImage) {
t.Errorf("the built image's bare id survived, which no other machine can resolve:\n%s", pinned)
}
}
// A manifest already naming a real digest was pinned by somebody else, to some other build.
// 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 TestAManifestAlreadyPinnedByHandIsRefused(t *testing.T) {
already := strings.Replace(theControlPlaneModule, placeholderDigest,
"sha256:"+strings.Repeat("9", 64), 1)
if _, _, err := pinImage([]byte(already), pushedReference, "mesh-controller"); err == nil {
t.Fatal("a manifest already pinned to some other image was accepted")
// A manifest naming some other image describes some other build. 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 TestAManifestNamingAnotherBuildIsRefused(t *testing.T) {
other := strings.Replace(theControlPlaneModule, builtImage, "sha256:"+strings.Repeat("9", 64), 1)
if _, _, err := pinImage([]byte(other), builtImage, pushedReference, "mesh-controller"); err == nil {
t.Fatal("a manifest naming some other image was accepted")
}
}
// Every placeholder moves. A manifest naming its image in a second resource — a runtime container
// beside the application's, which the catalogue's converted modules routinely carry — would
// otherwise be left half pinned, and fail inside an apply rather than here.
// Every place moves. A manifest naming its image in a second resource — a migrate step beside the
// server — would otherwise be left half pinned, and fail inside an apply rather than here.
func TestEveryPlaceTheManifestNamesTheImageIsPinned(t *testing.T) {
twice := strings.Replace(theControlPlaneModule,
`{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-controller", "mode": "0700"},`,
`{"id": "mesh-state", "type": "directory", "path": "/var/lib/mesh/mesh-controller", "mode": "0700"},
{"id": "migrate", "type": "container", "name": "mesh-controller-migrate", "run-once": true,
"image": "mesh-controller@`+placeholderDigest+`", "args": ["migrate"]},`, 1)
"image": "`+builtImage+`", "args": ["migrate"]},`, 1)
pinned, places, err := pinImage([]byte(twice), pushedReference, "mesh-controller")
pinned, places, err := pinImage([]byte(twice), builtImage, pushedReference, "mesh-controller")
if err != nil {
t.Fatal(err)
}
if places != 2 {
t.Errorf("the placeholder was found in %d place(s), and the manifest names it twice", places)
t.Errorf("the built image was found in %d place(s), and the manifest names it twice", places)
}
if strings.Contains(string(pinned), placeholderDigest) {
t.Error("a placeholder survived the pinning")
if strings.Contains(string(pinned), builtImage) {
t.Error("the built image's id survived the pinning")
}
}
@@ -230,7 +237,7 @@ func TestAManifestWantingNoStoresIsRefusedWithTheShapeItShouldHave(t *testing.T)
bare := `{"module":"mesh-controller","version":"1","resources":[
{"id":"container","type":"container","name":"mesh-controller",
"image":"mesh-controller@` + placeholderDigest + `"}]}`
"image":"` + builtImage + `"}]}`
_, err = deliverStores(context.Background(), Options{Node: "anchor"}, control,
[]byte(bare), rewritten.Declaration, func(string) {})
@@ -256,9 +263,11 @@ func TestThePermanentControlPlaneIsAskedTheSameQuestion(t *testing.T) {
t.Fatal(err)
}
out, err := InstallControlPlane(context.Background(),
installing(t, catalogueWith(t, ControlPlaneModule, theControlPlaneModule)),
Deps{Run: runtime.run}, control, rewritten.Declaration, pushedReference, func(string) {})
// No catalogue: the control plane's manifest is the one the build produced (novox/hq
// 04-ISSUES/072), and step 9 reads nothing from the catalogue any more.
out, err := InstallControlPlane(context.Background(), installing(t, t.TempDir()),
Deps{Run: runtime.run}, control, rewritten.Declaration, theBuild(theControlPlaneModule),
pushedReference, func(string) {})
if err != nil {
t.Fatal(err)
}
@@ -298,3 +307,21 @@ func TestABrokerSettingReadFromAFileIsDeliveredToo(t *testing.T) {
t.Fatal("a plain path variable was taken for a secret")
}
}
// Step 9 with nothing built is a caller's fault, and it stops rather than pretending to pivot.
func TestTheControlPlaneCannotBeInstalledWithoutABuild(t *testing.T) {
runtime := &asked{answer: aMeshThatAgrees(map[string]string{})}
control := controlPlane{container: "temp-mesh-controller", run: runtime.run, timeout: time.Second}
rewritten, err := Rewrite(theRealBundle(t), held)
if err != nil {
t.Fatal(err)
}
_, err = InstallControlPlane(context.Background(), installing(t, t.TempDir()),
Deps{Run: runtime.run}, control, rewritten.Declaration, Built{}, pushedReference, func(string) {})
if err == nil || !strings.Contains(err.Error(), "was not built") {
t.Fatalf("a missing build was not refused: %v", err)
}
if runtime.ran("module add") {
t.Error("something was registered without a manifest")
}
}
+66
View File
@@ -1,7 +1,9 @@
package bootstrap
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
@@ -143,3 +145,67 @@ func pushNode(ctx context.Context, o Options, control controlPlane, say func(str
say(" pushed " + o.Node)
return strings.TrimSpace(said), nil
}
// pinPlaceholder replaces the catalogue's placeholder digest with what the registry assigned — the
// builder's manifest, which the catalogue still holds (the control plane's comes out of its own
// build, see pinImage).
//
// **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.
//
// 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 pinPlaceholder(manifest []byte, reference, module string) ([]byte, int, error) {
places := bytes.Count(manifest, []byte(placeholderDigest))
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 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()
// 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.
var checked map[string]any
if err := json.Unmarshal(pinned, &checked); err != nil {
return nil, 0, fmt.Errorf(
"pinning the %s module's image broke its manifest: %w", module, err)
}
if bytes.Contains(pinned, []byte(placeholderDigest)) {
return nil, 0, fmt.Errorf(
"the %s module's manifest still carries a placeholder digest after pinning",
module)
}
return pinned, places, nil
}