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
217 lines
7.7 KiB
Go
217 lines
7.7 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The control plane's own addresses follow the node's ports (novox/hq 04-ISSUES/102).
|
|
|
|
func TestASeatPlaceholderAnswersWhereThisMachinePutTheHolder(t *testing.T) {
|
|
control := map[string]any{
|
|
"type": "container", "id": "server", "name": "mesh-controller",
|
|
"env": map[string]any{
|
|
"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}",
|
|
"MESH_BROKER_AMQP_PORT": "${seat:mesh-broker:5672}",
|
|
"MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}",
|
|
"MESH_STORE_INVENTORY_FILE": "/run/secrets/inventory",
|
|
},
|
|
}
|
|
with := Rendering{Seats: map[string]map[int]int{
|
|
"mesh-store": {5432: 6852}, "mesh-broker": {5672: 5679, 5671: 5671},
|
|
}}
|
|
if err := seatInto(control, "mesh-controller", with); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
env := control["env"].(map[string]any)
|
|
for key, want := range map[string]string{
|
|
"MESH_STORE_INVENTORY_PORT": "6852",
|
|
"MESH_BROKER_AMQP_PORT": "5679",
|
|
"MESH_BROKER_ADDRESS_PORT": "5671",
|
|
"MESH_STORE_INVENTORY_FILE": "/run/secrets/inventory",
|
|
} {
|
|
if env[key] != want {
|
|
t.Errorf("%s = %v, want %q", key, env[key], want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A seat nothing on this machine holds — or one whose holder the mesh has given no port — answers
|
|
// with nothing, so the port genesis wrote into the connection string stands. Not the software's
|
|
// own port: on a node given a port at genesis before the store's module exists, that would
|
|
// override the right number with the catalogue's.
|
|
func TestASeatTheMeshCannotPlaceAnswersWithNothing(t *testing.T) {
|
|
control := map[string]any{
|
|
"type": "container", "id": "server", "name": "mesh-controller",
|
|
"env": map[string]any{"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}"},
|
|
}
|
|
if err := seatInto(control, "mesh-controller", Rendering{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := control["env"].(map[string]any)["MESH_STORE_INVENTORY_PORT"]; got != "" {
|
|
t.Fatalf("with nothing known, the seat answered %q", got)
|
|
}
|
|
file := map[string]any{"type": "file", "content": "port=${seat:mesh-store:5432}\n"}
|
|
if err := seatInto(file, "x", Rendering{Seats: map[string]map[int]int{"mesh-store": {5433: 1}}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := file["content"]; got != "port=\n" {
|
|
t.Fatalf("a port the holder does not publish answered %q", got)
|
|
}
|
|
}
|
|
|
|
func TestASeatPlaceholderNamingNoPortIsRefused(t *testing.T) {
|
|
file := map[string]any{"type": "file", "content": "${seat:mesh-store:99999}"}
|
|
if err := seatInto(file, "x", Rendering{}); err == nil {
|
|
t.Fatal("99999 was accepted as a port")
|
|
}
|
|
}
|
|
|
|
func TestFillingASeatLeavesTheManifestAlone(t *testing.T) {
|
|
env := map[string]any{"MESH_STORE_INVENTORY_PORT": "${seat:mesh-store:5432}"}
|
|
manifest := map[string]any{"type": "container", "id": "server", "env": env}
|
|
for _, at := range []int{6852, 5432} {
|
|
copied := map[string]any{}
|
|
for k, v := range manifest {
|
|
copied[k] = v
|
|
}
|
|
with := Rendering{Seats: map[string]map[int]int{"mesh-store": {5432: at}}}
|
|
if err := seatInto(copied, "mesh-controller", with); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if env["MESH_STORE_INVENTORY_PORT"] != "${seat:mesh-store:5432}" {
|
|
t.Fatalf("the module's own manifest was edited: %v", env)
|
|
}
|
|
}
|
|
|
|
// **The control plane's own manifest, composed through the whole path.**
|
|
//
|
|
// The store was given 6852 and the broker's plain port 5679 (the control-node's migration, novox/hq
|
|
// 04-ISSUES/102). The control plane's own connections are sealed at genesis with the ports genesis
|
|
// wrote; what its container is told beside them is where this machine put the store and the
|
|
// broker now, read from the node's settings exactly as every consumer's binding is.
|
|
func TestTheControlPlanesOwnAddressesFollowTheNodesPorts(t *testing.T) {
|
|
raw, err := os.ReadFile("../../module.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m, err := ParseManifest(raw)
|
|
if err != nil {
|
|
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)
|
|
control, err := m.Resolve([]Built{{
|
|
Name: "server", Kind: ArtifactImage,
|
|
Reference: ArtifactStoreScheme + "mesh-controller/server@sha256:" + strings.Repeat("c", 64),
|
|
}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := Resolution{Node: "anchor", Modules: []Manifest{control}}
|
|
needed := map[string]map[string]string{"mesh-controller": {}}
|
|
for name := range m.OwnSecrets {
|
|
needed["mesh-controller"][name] = "sealed-" + name
|
|
}
|
|
out, err := r.Declaration(Rendering{
|
|
Needed: needed,
|
|
ArtifactStore: "anchor.internal:5100",
|
|
Seats: map[string]map[int]int{
|
|
"mesh-store": {5432: 6852},
|
|
"mesh-broker": {5671: 5671, 5672: 5679, 15672: 15673},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("the control plane does not compose: %v", err)
|
|
}
|
|
server := fileNamed(out, "mesh-controller.server")
|
|
if server == nil {
|
|
t.Fatalf("the control plane's container is not in the declaration: %v", out)
|
|
}
|
|
env, _ := server["env"].(map[string]any)
|
|
for key, want := range map[string]string{
|
|
"MESH_STORE_INVENTORY_PORT": "6852",
|
|
"MESH_STORE_IDENTITY_PORT": "6852",
|
|
"MESH_STORE_LICENCES_PORT": "6852",
|
|
"MESH_BROKER_AMQP_PORT": "5679",
|
|
"MESH_BROKER_MANAGEMENT_PORT": "15673",
|
|
"MESH_BROKER_ADDRESS_PORT": "5671",
|
|
} {
|
|
if env[key] != want {
|
|
t.Errorf("the control plane is told %s=%v; the node put it on %s", key, env[key], want)
|
|
}
|
|
}
|
|
if got := server["image"]; got != "anchor.internal:5100/mesh-controller/server@sha256:"+strings.Repeat("c", 64) {
|
|
t.Errorf("the control plane's own image is %v, not routed through the store", got)
|
|
}
|
|
|
|
// And on a mesh where the foundation is where genesis raised it, nothing is added.
|
|
out, err = r.Declaration(Rendering{Needed: needed, ArtifactStore: "anchor.internal:5100"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
env, _ = fileNamed(out, "mesh-controller.server")["env"].(map[string]any)
|
|
if env["MESH_STORE_INVENTORY_PORT"] != "" || env["MESH_BROKER_AMQP_PORT"] != "" {
|
|
t.Errorf("with no settings, the control plane is told %v", env)
|
|
}
|
|
}
|
|
|
|
// SeatPorts is what the control plane's manifest says beside each sealed connection: the port
|
|
// this machine put the seat's holder at (novox/hq 04-ISSUES/102).
|
|
var SeatPorts = map[string]string{
|
|
"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}",
|
|
}
|
|
|
|
// withSeatPorts is the control plane's manifest with SeatPorts in its container's environment.
|
|
//
|
|
// **The manifest lands one commit after the code that fills it**, deliberately: a control plane
|
|
// still running the previous build passes `${seat:…}` through unfilled, and the manifest may only
|
|
// name the placeholder once every control plane that could compose it knows it. So the test does
|
|
// not depend on module.json carrying these yet, and is a no-op once it does.
|
|
func withSeatPorts(m Manifest) Manifest {
|
|
out := m
|
|
out.Resources = nil
|
|
for _, r := range m.Resources {
|
|
if r["type"] != "container" {
|
|
out.Resources = append(out.Resources, r)
|
|
continue
|
|
}
|
|
copied := map[string]any{}
|
|
for k, v := range r {
|
|
copied[k] = v
|
|
}
|
|
env := map[string]any{}
|
|
if had, ok := r["env"].(map[string]any); ok {
|
|
for k, v := range had {
|
|
env[k] = v
|
|
}
|
|
}
|
|
for k, v := range SeatPorts {
|
|
if _, said := env[k]; !said {
|
|
env[k] = v
|
|
}
|
|
}
|
|
copied["env"] = env
|
|
out.Resources = append(out.Resources, copied)
|
|
}
|
|
return out
|
|
}
|