ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk carries no composition rule. The catalogue functions only the node-engine's composition reaches (Resolve, Resolution's Compose, Declaration, DataItems, LeftOut, Rules and the rest, the settings, placeholder and template refusals, the rendering helpers) are copied with their tests into internal/compose/resolution; a method of a mesh-sdk shape becomes a function taking it first. The composition calls its own copies, and the captured catalogue its tests read lives here now, shared by mesh-compose's tests. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
163 lines
6.0 KiB
Go
163 lines
6.0 KiB
Go
package resolution
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
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_ADDRESS_PORT": "${seat:mesh-broker:5671}",
|
|
"MESH_STORE_INVENTORY_FILE": "/run/secrets/inventory",
|
|
},
|
|
}
|
|
with := mcat.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_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", mcat.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", mcat.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", mcat.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 := mcat.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)
|
|
}
|
|
}
|
|
|
|
func runtimeTrust() map[string]any {
|
|
return map[string]any{
|
|
"type": "file", "id": "daemon", "path": "/etc/docker/daemon.json", "into": "json",
|
|
"content": `{"live-restore": true, "insecure-registries": ["${seat:mesh-artifact-store:reach}"]}` + "\n",
|
|
}
|
|
}
|
|
|
|
func TestASeatsReachIsWhereThisMachineReachesItsHolder(t *testing.T) {
|
|
file := runtimeTrust()
|
|
with := mcat.Rendering{SeatReach: map[string]string{"mesh-artifact-store": "anchor.internal:5100"}}
|
|
if err := seatInto(file, "docker", with); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := `{"live-restore": true, "insecure-registries": ["anchor.internal:5100"]}` + "\n"
|
|
if file["content"] != want {
|
|
t.Fatalf("content = %q, want %q", file["content"], want)
|
|
}
|
|
|
|
process := map[string]any{"type": "process", "name": "p",
|
|
"env": map[string]any{"REGISTRY": "${seat:mesh-artifact-store:reach}"}}
|
|
if err := seatInto(process, "x", with); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := process["env"].(map[string]any)["REGISTRY"]; got != "anchor.internal:5100" {
|
|
t.Fatalf("an environment value was filled with %q", got)
|
|
}
|
|
}
|
|
|
|
// With no holder reachable, the runtime is not told to trust "": the member is dropped, and the
|
|
// list with it when nothing else is in it.
|
|
func TestAnUnansweredReachAddsNothingToAList(t *testing.T) {
|
|
file := runtimeTrust()
|
|
if err := seatInto(file, "docker", mcat.Rendering{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := file["content"]; got != `{"live-restore":true}`+"\n" {
|
|
t.Fatalf("with no store reachable, the runtime's keys are %q", got)
|
|
}
|
|
|
|
kept := map[string]any{"type": "file", "into": "json",
|
|
"content": `{"insecure-registries": ["${seat:mesh-artifact-store:reach}", "192.0.2.7:5000"]}`}
|
|
if err := seatInto(kept, "docker", mcat.Rendering{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := kept["content"]; got != `{"insecure-registries":["192.0.2.7:5000"]}`+"\n" {
|
|
t.Fatalf("a list's other members were not kept: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestAReachTheMeshDoesNotAnswerIsRefused(t *testing.T) {
|
|
file := map[string]any{"type": "file", "content": "${seat:mesh-store:reach}"}
|
|
err := seatInto(file, "x", mcat.Rendering{SeatReach: map[string]string{"mesh-store": "anchor.internal:5432"}})
|
|
if err == nil || !strings.Contains(err.Error(), "mesh-artifact-store") {
|
|
t.Fatalf("a reach the mesh does not answer was not refused by name: %v", err)
|
|
}
|
|
}
|
|
|
|
// Through the whole composition, as the container runtime's module declares it.
|
|
func TestTheRuntimesTrustIsComposedFromTheSeatsReach(t *testing.T) {
|
|
r := mcat.Resolution{Node: "anchor", Modules: []mcat.Manifest{{
|
|
Module: "docker", Resources: []map[string]any{runtimeTrust()},
|
|
}}}
|
|
out, err := Declaration(r, mcat.Rendering{
|
|
SeatReach: map[string]string{"mesh-artifact-store": "anchor.internal:5100"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
file := fileNamed(out, "docker.daemon")
|
|
if file == nil {
|
|
t.Fatalf("no file in %v", out)
|
|
}
|
|
if got := file["content"]; !strings.Contains(fmt.Sprint(got), `["anchor.internal:5100"]`) {
|
|
t.Fatalf("the runtime's file says %q", got)
|
|
}
|
|
}
|