Merge pull request 'Leave a file the controller's manifest writes itself out of genesis's secrets (issue 597)' (#100) from fix/597-a-file-the-module-writes-is-not-a-secret into main

This commit was merged in pull request #100.
This commit is contained in:
2026-10-13 00:40:33 +00:00
2 changed files with 98 additions and 2 deletions
+16 -2
View File
@@ -311,6 +311,17 @@ func secretsByVariableIn(manifest []byte) (map[string]string, error) {
for name, path := range m.OwnSecrets { for name, path := range m.OwnSecrets {
secretAt[path] = name secretAt[path] = name
} }
// **A file the module itself declares is its own, not a secret** (novox/hq issue 597). The
// controller reads its merge window's settings from a file its manifest writes from its settings
// (MESH_MERGE_WINDOW_FILE, ADR 0276): the mesh writes it with the module's declaration, and there
// is nothing for genesis to carry in. Only a `…_FILE` naming neither an own-secret nor a file of
// the module is refused.
declared := map[string]bool{}
for _, r := range m.Resources {
if r.Type == "file" && r.Path != "" {
declared[r.Path] = true
}
}
wanted := map[string]string{} wanted := map[string]string{}
for _, r := range m.Resources { for _, r := range m.Resources {
@@ -333,10 +344,13 @@ func secretsByVariableIn(manifest []byte) (map[string]string, error) {
on = from on = from
} }
secret, named := secretAt[on] secret, named := secretAt[on]
if !named && declared[on] {
continue
}
if !named { if !named {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"the %s module's container reads %s from %s, and no own-secret of that "+ "the %s module's container reads %s from %s, and neither an own-secret nor "+
"module writes that file.\n"+ "a file of that module writes that file.\n"+
"So the mesh would seal nothing there and the control plane would find "+ "So the mesh would seal nothing there and the control plane would find "+
"an empty file where a connection string has to be. The manifest has to "+ "an empty file where a connection string has to be. The manifest has to "+
"name the two ends the same, directly or through a mount", "name the two ends the same, directly or through a mount",
+82
View File
@@ -180,3 +180,85 @@ func TestAProcessFormNamingNoOneReplacementIsRefused(t *testing.T) {
} }
} }
} }
// theStepNineSecretsOf is what step 9 delivers for a process-form controller manifest: its genesis
// container made, pinned, and its secrets read from that container's environment.
func theStepNineSecretsOf(t *testing.T, manifest []byte) (map[string]string, error) {
t.Helper()
form, err := processFormOf(manifest)
if err != nil || !form.Found {
t.Fatalf("the manifest has no process form: %v", err)
}
genesis, err := genesisForm(manifest, form, builtImage)
if err != nil {
t.Fatal(err)
}
pinned, _, err := pinImage(genesis, builtImage, "registry.internal:5000/mesh-controller@sha256:"+strings.Repeat("e", 64), ControlPlaneModule)
if err != nil {
t.Fatal(err)
}
return secretsByVariableIn(pinned)
}
// **A file the controller's manifest writes itself is not a secret** (novox/hq issue 597). The merge
// window's settings reach the controller in a file its manifest declares from its settings
// (MESH_MERGE_WINDOW_FILE, ADR 0276); genesis refused it as a secret nobody writes, and stopped at
// step 11 of 24.
func TestAFileTheControllersManifestWritesIsNotASecret(t *testing.T) {
manifest := strings.Replace(processFormManifest,
`{"id": "controller", "type": "process",`,
`{"id": "merge-window", "type": "file", "path": "${dir:mesh-state}/merge-window.json", "mode": "0600",
"owner": "mesh-controller", "content": "{\"merge-window\": \"${setting:merge-window}\"}\n"},
{"id": "controller", "type": "process",`, 1)
manifest = strings.Replace(manifest, `"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus"}`,
`"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus",
"MESH_MERGE_WINDOW_FILE": "${dir:mesh-state}/merge-window.json"}`, 1)
if !strings.Contains(manifest, "MESH_MERGE_WINDOW_FILE") || !strings.Contains(manifest, `"id": "merge-window"`) {
t.Fatal("the fixture was not changed")
}
wanted, err := theStepNineSecretsOf(t, []byte(manifest))
if err != nil {
t.Fatalf("step 9 refuses a file the manifest writes itself: %v", err)
}
if _, asked := wanted["MESH_MERGE_WINDOW"]; asked {
t.Errorf("the merge window's settings are delivered as a secret: %v", wanted)
}
if wanted["MESH_STORE_INVENTORY"] != "inventory" || wanted["MESH_BUS_NATS"] != "bus" {
t.Errorf("step 9 delivers %v; the store's and the bus's secrets are still wanted", wanted)
}
}
// And a `…_FILE` that neither an own-secret nor a file of the module writes is still refused.
func TestAFileNothingOfTheControllerWritesIsStillRefused(t *testing.T) {
manifest := strings.Replace(processFormManifest, `"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus"}`,
`"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus",
"MESH_MERGE_WINDOW_FILE": "${dir:mesh-state}/merge-window.json"}`, 1)
if _, err := theStepNineSecretsOf(t, []byte(manifest)); err == nil || !strings.Contains(err.Error(), "MESH_MERGE_WINDOW_FILE") {
t.Fatalf("a file nothing writes was accepted, or refused without its name: %v", err)
}
}
// The controller's own manifest, as the mesh runs it, passes step 9 (issue 597): in a merge check the
// node-engine's check has the controller cloned beside it (MESH_CHECK_BESIDE), where it must be; else a
// checkout beside this one; else it skips aloud.
func TestTheControllersOwnManifestPassesStepNine(t *testing.T) {
var manifest []byte
if root := os.Getenv("MESH_CHECK_BESIDE"); root != "" {
raw, err := os.ReadFile(filepath.Join(root, "mesh-controller", "module.json"))
if err != nil {
t.Fatalf("the controller is not beside this check in MESH_CHECK_BESIDE=%s: %v", root, err)
}
manifest = raw
} else if raw, err := os.ReadFile(filepath.Join("..", "..", "..", "mesh-controller", "module.json")); err == nil {
manifest = raw
} else {
t.Skip("NOT JUDGED: no controller beside this checkout and no MESH_CHECK_BESIDE; the merge check judges it")
}
wanted, err := theStepNineSecretsOf(t, manifest)
if err != nil {
t.Fatalf("genesis's step 9 refuses the controller the mesh runs: %v", err)
}
if wanted["MESH_STORE_INVENTORY"] != "inventory" {
t.Errorf("step 9 delivers %v", wanted)
}
}