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.
87 lines
3.5 KiB
Go
87 lines
3.5 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// BuilderModule is the module that lets a mesh produce anything at all.
|
|
const BuilderModule = "builder"
|
|
|
|
// BuilderRepository is what its image is called in this mesh's own registry.
|
|
const BuilderRepository = "mesh-builder"
|
|
|
|
// Builder is what installing it produced.
|
|
type Builder struct {
|
|
Published Published
|
|
Installed Installed
|
|
// Account is true when a broker account was issued for it here.
|
|
Account bool
|
|
}
|
|
|
|
// InstallBuilder gives a fresh mesh the thing that makes everything else.
|
|
//
|
|
// **Without this a mesh can run and cannot produce** (novox/hq ADR 0073). Genesis ends with a
|
|
// control plane, a store, a queue and a registry — and almost every module in the catalogue is
|
|
// waiting to be built, because a manifest names artifacts and nothing has made them. The builder is
|
|
// one of the few things that cannot be built by the thing it is, so it is carried; and it is
|
|
// already here, because it is what built the control plane.
|
|
//
|
|
// So this is the same two acts the control plane went through, in the same order and for the same
|
|
// reason: publish the image so the mesh names it by a digest its own registry assigned rather than
|
|
// by a local identity nothing else can fetch, then install it as an ordinary module pinned to that.
|
|
//
|
|
// And then the part only it needs: a broker account. A builder takes work from a queue and
|
|
// announces what it made, and it holds its own credential for that like any module — asking for a
|
|
// generic one produced an account that could do neither, which is what made this worth its own step
|
|
// rather than a line in another.
|
|
func InstallBuilder(ctx context.Context, o Options, d Deps, control controlPlane, imageID string,
|
|
say func(string)) (Builder, error) {
|
|
|
|
var out Builder
|
|
|
|
published, err := publishAs(ctx, o, d, imageID, BuilderRepository, say)
|
|
out.Published = published
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
manifest, err := readManifest(o.Catalogue, BuilderModule)
|
|
if err != nil {
|
|
return out, fmt.Errorf("%w\n"+
|
|
"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 := pinPlaceholder(manifest, published.Reference, BuilderModule)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
say(fmt.Sprintf(" pinned to %s, named in %d place(s)", published.Reference, places))
|
|
|
|
installed, err := registerAndAssign(ctx, o, control, BuilderModule, pinned, say)
|
|
out.Installed = installed
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
// Before the push, so the account is in the declaration the machine receives rather than in
|
|
// the one after it. A builder that arrives without its credential starts, finds nothing it may
|
|
// read, and waits — which looks exactly like a builder with no work.
|
|
account := o.Node + "-" + BuilderModule
|
|
if _, err := control.tell(ctx, "builder", "issue", account, "--node", o.Node); err != nil {
|
|
// Said and carried on. An account that already exists is the ordinary case on a re-run,
|
|
// and the push below is what makes either state true on the machine.
|
|
if !strings.Contains(err.Error(), "already") {
|
|
return out, fmt.Errorf("the builder has no broker account, so it can take no work: %w", err)
|
|
}
|
|
say(" broker account " + account + " — already issued")
|
|
} else {
|
|
out.Account = true
|
|
say(" broker account " + account + ", scoped to what it consumes and emits")
|
|
}
|
|
|
|
out.Installed.Pushed, err = pushNode(ctx, o, control, say)
|
|
return out, err
|
|
}
|