Files
mesh-host/internal/bootstrap/build_test.go
T
jochen d9ea387680 Raise a process-form controller at genesis as the container it replaces (hq issue 223)
The controller's manifest now declares a Go bundle the host runs as a
process (novox/hq issue 213). Genesis cannot run that: the bundle is
fetched from the artifact store and compiled in a toolchain, and the mesh
makes both long after the controller. The builder, asked to build the
manifest at genesis, refuses for lack of the Go toolchain. So genesis
raises the controller as before, as a container, and the first push hands
it over to the process through `replaces` (issue 223, option b).

- Step 3 clones the controller at the commit with the carried builder's
  git and reads its manifest. In the image form (an older controller) it
  builds through the builder as before. In the process form it builds the
  repository's own Dockerfile and hands step 9 a manifest of its own
  shape: the process becomes a container with the id the process
  `replaces`, the image genesis built, host network, and every host path
  the process's env names mounted at that same path read-only. Secrets
  belong to the image's user (65534) until the process's account takes
  them over. `prepares` is dropped: the temporary controller from the
  same commit already migrated the stores, and a pinned image is
  nothing the controller can derive a step from.
- Steps 4 to 9 are unchanged: they take the manifest as they did.
- apply.ForTests lets the bootstrap's test apply a process.

The first composed declaration from the process manifest names
`mesh-controller.server`, which is what the host recorded for the genesis
container, so the first apply hands over and leaves one controller.
2026-10-04 01:47:33 +02:00

96 lines
4.8 KiB
Go

package bootstrap
import (
"context"
"strings"
"testing"
)
// An installer that carries a builder and was told nothing refuses, and says what is missing.
//
// **Exercised rather than assumed.** This is the refusal that stands between a person and a
// machine left holding a store, a broker and no control plane — the failure the whole preflight
// exists to prevent — and a refusal nothing tests is a refusal nobody has read.
func TestAnInstallerWithNothingToBuildRefuses(t *testing.T) {
err := Source{}.Check()
if err == nil {
t.Fatal("a source naming no repository was accepted; nothing would have been built")
}
for _, want := range []string{"--source", "--source-ref"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("the refusal does not name %s, so it does not say how to fix it: %v", want, err)
}
}
}
// A repository without a commit refuses too, because a branch is somebody else's moving target.
func TestABranchIsNotACommit(t *testing.T) {
err := Source{Repository: "https://example.invalid/mesh-controller.git"}.Check()
if err == nil {
t.Fatal("a source with no ref was accepted; genesis would have built whatever a branch pointed at")
}
if !strings.Contains(err.Error(), "--source-ref") {
t.Errorf("the refusal does not name the flag that fixes it: %v", err)
}
}
// And a repository with a commit is enough.
func TestARepositoryAndACommitIsEnough(t *testing.T) {
if err := (Source{Repository: "https://example.invalid/mesh-controller.git", Ref: "a1b2c3d4"}).Check(); err != nil {
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: aRepository(t, imageFormManifest, 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: aRepository(t, imageFormManifest, 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: aRepository(t, imageFormManifest, 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)
}
}