mesh/merge-gate head e0bc919d4945 merged into trunk f88e3452f8b8 — pass: builds build-agent → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head e0bc919d4945 merged into trunk f88e3452f8b8 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery covered: a later merge that contains it was delivered: novox/mesh-catalog@2f5e97030a5c (merged as 5cd608ba into main, walk plan-17918799551…
mesh/delivery-group group fix/121-genesis-package-registry delivered: every member is delivered
build-agent's npm-package-registry becomes optional, so genesis's step 13 is no longer refused with 'nothing provides npm-package-registry'. Until gitea is assigned, the agent on genesis's node reads the genesis-package-registry own-secret the installer seals at the packages step; it is issued outside the mesh, so no other machine is given one. The binding and its credential are composed only while bound (ADR 0330 §6), and no file names them otherwise.
118 lines
4.1 KiB
Go
118 lines
4.1 KiB
Go
// The build agent's manifest (novox/hq ADR 0336, issue 121). The build agent is assigned at genesis's
|
|
// step 13, before anything provides the package registry, so that requirement is optional
|
|
// (ADR 0330); until it binds, the agent on genesis's node reads the genesis document the installer
|
|
// seals to it at the packages step. The program is mesh-controller's cmd/mesh-builder; only the
|
|
// packaging lives here, and this is its test.
|
|
package buildagent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type manifest struct {
|
|
Requires []json.RawMessage `json:"requires"`
|
|
Binds map[string]string `json:"binds"`
|
|
Secrets map[string]string `json:"secrets"`
|
|
OwnSecrets map[string]json.RawMessage `json:"own-secrets"`
|
|
Resources []map[string]any `json:"resources"`
|
|
}
|
|
|
|
func read(t *testing.T) manifest {
|
|
t.Helper()
|
|
raw, err := os.ReadFile("module.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m manifest
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
// requirement is one entry of `requires`: its name, and whether it is optional.
|
|
func requirements(t *testing.T, m manifest) map[string]bool {
|
|
t.Helper()
|
|
out := map[string]bool{}
|
|
for _, raw := range m.Requires {
|
|
var name string
|
|
if json.Unmarshal(raw, &name) == nil {
|
|
out[name] = false
|
|
continue
|
|
}
|
|
var long struct {
|
|
Name string `json:"name"`
|
|
Optional bool `json:"optional"`
|
|
}
|
|
if err := json.Unmarshal(raw, &long); err != nil {
|
|
t.Fatalf("requires: %s: %v", raw, err)
|
|
}
|
|
out[long.Name] = long.Optional
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestThePackageRegistryIsOptionalAndTheArtifactStoreStaysHard(t *testing.T) {
|
|
r := requirements(t, read(t))
|
|
optional, has := r["npm-package-registry"]
|
|
if !has || !optional {
|
|
t.Errorf("npm-package-registry is not an optional requirement (ADR 0336 §1): %v", r)
|
|
}
|
|
if optional, has := r["artifact-store"]; !has || optional {
|
|
t.Errorf("artifact-store is not a hard requirement: %v", r)
|
|
}
|
|
}
|
|
|
|
// The binding and the credential of the registry are delivered only while it is bound: ADR 0330 §6
|
|
// composes neither for an absent requirement, so no file of the module may name them through a
|
|
// placeholder unless it is itself only-when-bound.
|
|
func TestTheRegistrysBindsAndSecretsAreOnlyWhenBound(t *testing.T) {
|
|
m := read(t)
|
|
if m.Binds["npm-package-registry"] == "" || m.Secrets["npm-package-registry"] == "" {
|
|
t.Fatalf("the agent is not told where the registry is once it binds: binds %v, secrets %v", m.Binds, m.Secrets)
|
|
}
|
|
for _, r := range m.Resources {
|
|
content, _ := r["content"].(string)
|
|
if strings.Contains(content, "${bound:npm-package-registry:") || strings.Contains(content, "${secret:npm-package-registry}") {
|
|
if r["only-when-bound"] != "npm-package-registry" {
|
|
t.Errorf("%v names the registry's binding and is delivered while it is absent", r["id"])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The genesis document is an own-secret issued outside the mesh — by the installer at genesis's packages
|
|
// step (ADR 0336 §3) — so the mesh never makes one of its own on any other machine, and the agent is
|
|
// told where it lands.
|
|
func TestTheGenesisDocumentIsDeclaredAndIssuedOutside(t *testing.T) {
|
|
m := read(t)
|
|
raw, has := m.OwnSecrets["genesis-package-registry"]
|
|
if !has {
|
|
t.Fatalf("the build agent declares no genesis-package-registry own-secret: %v", m.OwnSecrets)
|
|
}
|
|
var own struct {
|
|
Path string `json:"path"`
|
|
IssuedBy string `json:"issued-by"`
|
|
}
|
|
if err := json.Unmarshal(raw, &own); err != nil || own.IssuedBy != "outside" {
|
|
t.Fatalf("the genesis document is not issued outside the mesh: %s (%v)", raw, err)
|
|
}
|
|
if !strings.HasPrefix(own.Path, "${dir:mesh-state}/") {
|
|
t.Fatalf("the genesis document is not where the container reads /run/mesh: %s", own.Path)
|
|
}
|
|
inside := "/run/mesh/" + strings.TrimPrefix(own.Path, "${dir:mesh-state}/")
|
|
for _, r := range m.Resources {
|
|
if r["id"] == "agent-env" {
|
|
content, _ := r["content"].(string)
|
|
if !strings.Contains(content, "MESH_GENESIS_PACKAGE_REGISTRY="+inside+"\n") {
|
|
t.Errorf("the agent is not told where the genesis document is (%s):\n%s", inside, content)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
t.Fatal("the build agent has no environment file")
|
|
}
|