The control plane's own manifest says where the node put the store and the broker

Beside each sealed connection genesis wrote, the port this machine put the
seat's holder at: `${seat:mesh-store:5432}` for the three stores,
`${seat:mesh-broker:…}` for the bus, the plain AMQP port and the management API.
Filled from the node's settings when the control plane composes its own
declaration; empty — the sealed value stands — when the mesh has nothing to add.

On its own, after the commit before it is built and running: a control plane
that does not know the placeholder passes it through as the value, and this
manifest is composed by whatever control plane is running when it is pushed.
The reader ignores an unfilled placeholder either way, and a test holds it to
ignoring exactly what this manifest says.

novox/hq 04-ISSUES/102
This commit is contained in:
2026-09-23 23:49:55 +02:00
parent e07b56ce43
commit cdd3638312
3 changed files with 71 additions and 1 deletions
+12
View File
@@ -101,6 +101,18 @@ func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("the control plane's own manifest does not parse:\n%v", err) t.Fatalf("the control plane's own manifest does not parse:\n%v", err)
} }
// The manifest itself names them now; withSeatPorts is a no-op on it, and this holds it so.
for _, r := range m.Resources {
if r["type"] != "container" {
continue
}
env, _ := r["env"].(map[string]any)
for key, want := range SeatPorts {
if env[key] != want {
t.Errorf("module.json says %s=%v, not %q", key, env[key], want)
}
}
}
m = withSeatPorts(m) m = withSeatPorts(m)
control, err := m.Resolve([]Built{{ control, err := m.Resolve([]Built{{
Name: "server", Kind: ArtifactImage, Name: "server", Kind: ArtifactImage,
+52
View File
@@ -0,0 +1,52 @@
package store
import (
"encoding/json"
"os"
"testing"
)
// **The manifest's placeholder, unfilled, reaches a store reader and changes nothing.**
//
// The control plane composes its own declaration, so the manifest naming `${seat:…}` can be
// composed by a control plane one build older than it — one that passes the literal through as
// the value. That is the state of the live control-node between this manifest landing and its
// next build being pushed, and a reader that refused the literal would leave it headless
// (novox/hq 04-ISSUES/102, finding F1). So the reader is held to ignoring exactly what
// module.json says, not a placeholder shaped like it.
func TestTheManifestsOwnPlaceholderUnfilledLeavesTheStoreWhereTheFileSays(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Resources []struct {
Type string `json:"type"`
Env map[string]string `json:"env"`
} `json:"resources"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
var written string
for _, r := range m.Resources {
if r.Type == "container" {
written = r.Env["MESH_STORE_INVENTORY_PORT"]
}
}
if written == "" {
t.Fatal("module.json no longer names MESH_STORE_INVENTORY_PORT")
}
alone(t)
t.Setenv(Variable(example), dsn)
t.Setenv(PortVariable(example), written)
opened, err := Open(t.Context(), example)
if err != nil {
t.Fatalf("the unfilled placeholder was refused, which is a headless control plane: %v", err)
}
defer opened.Close()
if got := opened.Pool().Config().ConnConfig.Port; got != 5432 {
t.Fatalf("the store is on %d; with the placeholder unfilled, the file's port stands", got)
}
}
+7 -1
View File
@@ -42,7 +42,13 @@
"MESH_STORE_LICENCES_FILE": "/run/secrets/licences", "MESH_STORE_LICENCES_FILE": "/run/secrets/licences",
"MESH_BROKER_AMQP_FILE": "/run/secrets/broker", "MESH_BROKER_AMQP_FILE": "/run/secrets/broker",
"MESH_BROKER_MANAGEMENT_FILE": "/run/secrets/broker-management", "MESH_BROKER_MANAGEMENT_FILE": "/run/secrets/broker-management",
"MESH_BROKER_ADDRESS_FILE": "/run/secrets/broker-address" "MESH_BROKER_ADDRESS_FILE": "/run/secrets/broker-address",
"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}",
"MESH_STORE_IDENTITY_PORT": "${seat:mesh-store:5432}",
"MESH_STORE_LICENCES_PORT": "${seat:mesh-store:5432}",
"MESH_BROKER_AMQP_PORT": "${seat:mesh-broker:5672}",
"MESH_BROKER_MANAGEMENT_PORT": "${seat:mesh-broker:15672}",
"MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}"
}, },
"volumes": [ "volumes": [
"mesh-broker-tls:/broker-tls:ro", "mesh-broker-tls:/broker-tls:ro",