Files
jschoubben e07b56ce43 An address is read from the node's settings where it is used, never recorded with a port
Three readers did not follow a moved foundation port (novox/hq 04-ISSUES/102),
and each took the control-node down in its own way: the control plane's own
store and broker connections, sealed at genesis with the port inside; and every
build the mesh ever recorded, kept as `<registry>:<port>/<module>/<artifact>@…`.

The control plane cannot open its own sealed connections to move a port, and it
cannot bind the store as a consumer would — a binding mints a credential. So its
settings get a third twin, `NAME_PORT`, read on top of the sealed value by the
store, the broker, the management API and the bus connection, and filled into
its container by a placeholder that names a seat, `${seat:mesh-store:5432}`,
from the node's given or mesh-assigned ports — never the manifest's number, and
empty when the mesh has nothing to add, so what genesis wrote stands. A value
that is still a placeholder is nothing said, aloud: the manifest naming it lands
in the next commit, once every control plane that composes it knows it.

A build is now recorded by digest and path — `artifact-store://<module>/<artifact>@…`
— and the store's address is composed in where a reference is used: the
declaration, the trust file, the bases a build is handed, a replay to the
catalogue. Over the network as `<node>.internal:<port>`; on the store's own node
before any network exists — every genesis push before its "network" step — by
loopback. A reference recorded before this, with an address, is re-routed the
same way when the mesh built it. The trust file and every provider's address
come from one derivation: the node's given port, over the mesh's assignment,
over the manifest's number.

novox/hq 04-ISSUES/102
2026-09-23 23:49:31 +02:00

79 lines
3.3 KiB
Go

package envfile
import (
"os"
"path/filepath"
"strings"
"testing"
)
// The control plane's own connections follow the port the node moved (novox/hq 04-ISSUES/102).
func TestAPortTwinMovesTheValuesPort(t *testing.T) {
cases := map[string]string{
"postgres://mesh:s3cret@127.0.0.1:5432/inventory?sslmode=disable": "postgres://mesh:s3cret@127.0.0.1:6852/inventory?sslmode=disable",
"postgres://mesh:s3cret@127.0.0.1/inventory": "postgres://mesh:s3cret@127.0.0.1:6852/inventory",
"amqp://control:p%40ss@127.0.0.1:5672/": "amqp://control:p%40ss@127.0.0.1:6852/",
"amqp://control:p@ss:with@127.0.0.1:5672/": "amqp://control:p@ss:with@127.0.0.1:6852/",
"http://guest:guest@127.0.0.1:15672": "http://guest:guest@127.0.0.1:6852",
"postgres://mesh:a/b?c#d@e@127.0.0.1:5432/inventory": "postgres://mesh:a/b?c#d@e@127.0.0.1:6852/inventory",
"http://[::1]:15672/api": "http://[::1]:6852/api",
"broker.example:5671": "broker.example:6852",
"broker.example": "broker.example:6852",
"host=127.0.0.1 port=5432 dbname=inventory": "host=127.0.0.1 port=6852 dbname=inventory",
"host=127.0.0.1 dbname=inventory": "host=127.0.0.1 dbname=inventory port=6852",
}
for value, want := range cases {
got, err := WithPort(value, "6852")
if err != nil || got != want {
t.Errorf("WithPort(%q) = %q, %v; want %q", value, got, err, want)
}
}
}
func TestPlacedLeavesTheValueAloneWhenNothingSaysAPort(t *testing.T) {
path := filepath.Join(t.TempDir(), "value")
if err := os.WriteFile(path, []byte("postgres://m:p@127.0.0.1:5432/inventory\n"), 0o600); err != nil {
t.Fatal(err)
}
t.Setenv("MESH_P_FILE", path)
t.Setenv("MESH_P_PORT", "")
got, err := Placed("MESH_P")
if err != nil || got != "postgres://m:p@127.0.0.1:5432/inventory" {
t.Fatalf("got %q, %v", got, err)
}
t.Setenv("MESH_P_PORT", " 6852 ")
got, err = Placed("MESH_P")
if err != nil || got != "postgres://m:p@127.0.0.1:6852/inventory" {
t.Fatalf("got %q, %v", got, err)
}
}
func TestAPortTwinThatIsNotAPortIsRefusedWithoutQuotingTheValue(t *testing.T) {
t.Setenv("MESH_Q", "postgres://m:hunter2@127.0.0.1:5432/inventory")
t.Setenv("MESH_Q_PORT", "many")
_, err := Placed("MESH_Q")
if err == nil {
t.Fatal("a port that is not a number was accepted")
}
if strings.Contains(err.Error(), "hunter2") {
t.Fatalf("the value was quoted back: %v", err)
}
t.Setenv("MESH_Q", "")
t.Setenv("MESH_Q_PORT", "6852")
if _, err := Placed("MESH_Q"); err == nil {
t.Fatal("a port with no value to put it on was accepted")
}
}
// A placeholder nothing filled is nothing said, not a fault: the control plane composing the
// declaration may be one build behind the manifest, and refusing would leave it headless.
func TestAnUnfilledPlaceholderIsNothingSaid(t *testing.T) {
t.Setenv("MESH_R", "postgres://m:p@127.0.0.1:5432/inventory")
t.Setenv("MESH_R_PORT", "${seat:mesh-store:5432}")
got, err := Placed("MESH_R")
if err != nil || got != "postgres://m:p@127.0.0.1:5432/inventory" {
t.Fatalf("an unfilled placeholder was not ignored: %q, %v", got, err)
}
}