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
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user