From a74f9db508687f6fbf3c8074a2a8e195824a500a Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 19:06:37 +0100 Subject: [PATCH] Test route-proxy without composing: its manifest's own facts and a plan entry's port, since the composition left mesh-sdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The composer's proof that the manifest composes, and that a port is the placement's, is the node-engine's now (ADR 0593 ยง7). Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- .../cmd/route-provider/manifest_test.go | 32 +------- .../cmd/route-proxy/routes_test.go | 78 +++++-------------- 2 files changed, 24 insertions(+), 86 deletions(-) diff --git a/modules/route-proxy/cmd/route-provider/manifest_test.go b/modules/route-proxy/cmd/route-provider/manifest_test.go index 40fdef3..8d54a5b 100644 --- a/modules/route-proxy/cmd/route-provider/manifest_test.go +++ b/modules/route-proxy/cmd/route-provider/manifest_test.go @@ -54,40 +54,16 @@ func TestRouteProxyKeepsItsPublicAccountDirectory(t *testing.T) { if slices.Contains(m.Requires, "acme-ca") { t.Fatal("route-proxy still asks for acme-ca; the public issuer is its own fact (ADR 0226)") } - // As a build would leave it: each artifact a container names resolved to an image. Which image does not matter to - // the file checked here. An archive resource carries no image: it unpacks a bundle. - for _, res := range m.Resources { - if res["type"] == "archive" { - continue - } - if artifact, ok := res["artifact"].(string); ok { - delete(res, "artifact") - res["image"] = "registry.invalid/route-proxy/" + artifact + - "@sha256:1111111111111111111111111111111111111111111111111111111111111111" - } - } - r := catalogue.Resolution{ - Node: "anchor", - Modules: []catalogue.Manifest{m}, - Needs: []catalogue.Needed{{ - Name: "internal-acme-ca", From: "home", At: "home.internal", For: "route-proxy", - Serves: map[string]any{"port": float64(9000), "path": "/acme/acme/directory", "roots": "/roots.pem"}, - }}, - } - // Its own broker credential, sealed as the mesh would; what it is does not matter here. - out, err := r.Declaration(catalogue.Rendering{Needed: map[string]map[string]string{"route-proxy": {"broker": "sealed"}}}) - if err != nil { - t.Fatalf("route-proxy could not be composed for a machine: %v", err) - } - env := resourceNamed(out, "route-proxy.acme-env") + // The tree read inlines the template the manifest names, a literal with nothing to fill, so this is what it writes. + env := resourceNamed(m.Resources, "acme-env") if env == nil { - t.Fatalf("nothing writes the public issuer's environment: %v", out) + t.Fatalf("nothing writes the public issuer's environment: %v", m.Resources) } if got, _ := env["content"].(string); got != renderedBeforeTheFold { t.Fatalf("acme.env changed, which moves the proxy's account and reorders every certificate:\ngot %q\nwant %q", got, renderedBeforeTheFold) } - if resourceNamed(out, "route-proxy.bound-acme-ca") != nil { + if resourceNamed(m.Resources, "bound-acme-ca") != nil { t.Error("a binding to acme-ca is still written") } var trust string diff --git a/modules/route-proxy/cmd/route-proxy/routes_test.go b/modules/route-proxy/cmd/route-proxy/routes_test.go index 0602ed3..58618fc 100644 --- a/modules/route-proxy/cmd/route-proxy/routes_test.go +++ b/modules/route-proxy/cmd/route-proxy/routes_test.go @@ -154,70 +154,32 @@ func TestARouteWithNoInternalNameGetsNoAlias(t *testing.T) { } } -// A route is dialled where the machine publishes its module's endpoint, as the module's declaration publishes it (issue -// 774, research 072 row 34; 2026-10-31: gitea's 3000, published at 20000, and grafana's 3000 on another machine, -// published at 20012, answered 502): over loopback beside the proxy, which is the only thing that works without a -// private network, and at its machine's address otherwise. Each plan is composed by the SDK as the seat -// mesh-provisioner composes it, its ports as compose's portsFor gives them (the placement's for a port the mesh may -// move, the manifest's own otherwise), and read as this proxy reads it: the live shapes the old proxy served alike. -func TestAComposedPlanIsDialledWhereTheMachinePublishesIt(t *testing.T) { - proxy := catalogue.Manifest{Module: "route-proxy", Version: "1", - Provides: []catalogue.Offer{{Name: "route", Scope: catalogue.ScopeMesh, AnsweredBy: catalogue.AnsweredByHelper}}} - world := catalogue.World{Offered: map[string][]catalogue.Provider{ - "route": {{Node: "anchor", At: "anchor.internal", Module: "route-proxy"}}}} +// A route is dialled at the machine port its plan entry carries (issue 774, research 072 row 34; 2026-10-31: gitea's +// 3000, published at 20000, and grafana's 3000 on another machine, published at 20012, answered 502): over loopback +// when the plan is the one of this proxy's own node, which is the only thing that works without a private network, and +// at the address the entry gives for its machine otherwise. The plans here are written as the seat mesh-provisioner +// carries them; that the port is the placement's and not the software's own is the composition's, proved by the +// node-engine's composer, not here. +func TestAPlanEntryIsDialledAtTheMachinePortItCarries(t *testing.T) { for _, c := range []struct { - name, node, module string - port int // the endpoint's port, as listens says it - published []any // the container's ports; nil for the host's network - placed map[int]int // what mesh-placement holds for the module - target string + name, node, module, at string + port int + target string }{ - {"beside the proxy, moved by the placement (gitea)", "anchor", "gitea", 3000, []any{"3000"}, map[int]int{3000: 20000}, - "http://127.0.0.1:20000"}, - {"on another machine, moved by the placement (grafana)", "home", "grafana", 3000, []any{"3000"}, - map[int]int{3000: 20012}, "http://home.internal:20012"}, - {"a bare port the placement holds none for (influxdb)", "home", "influxdb", 8086, []any{"8086"}, nil, - "http://home.internal:8086"}, - {"a fixed mapping (mailu 7080:80)", "anchor", "mailu", 7080, []any{"25", "7080:80"}, nil, "http://127.0.0.1:7080"}, - {"a fixed mapping the placement names another port of (photos 9102:9000)", "anchor", "photos", 9102, - []any{"9102:9000"}, map[int]int{9000: 20011}, "http://127.0.0.1:9102"}, - {"the host's network (home-assistant)", "anchor", "home-assistant", 8123, nil, nil, "http://127.0.0.1:8123"}, + {"beside the proxy (gitea)", "anchor", "gitea", "", 20000, "http://127.0.0.1:20000"}, + {"beside the proxy, though the entry says an address (mailu)", "anchor", "mailu", "anchor.internal", 7080, "http://127.0.0.1:7080"}, + {"on another machine (grafana)", "home", "grafana", "home.internal", 20012, "http://home.internal:20012"}, + {"on another machine, a bare port (influxdb)", "home", "influxdb", "home.internal", 8086, "http://home.internal:8086"}, } { - container := map[string]any{"type": "container", "name": c.module, "image": "x"} - if c.published != nil { - container["ports"] = c.published - } else { - container["network"] = "host" - } - module := catalogue.Manifest{Module: c.module, Version: "1", Requires: []string{"route"}, - Listens: []catalogue.Listening{{Name: "web", Port: c.port, Protocol: "tcp", From: "mesh"}}, - Contributes: map[string]map[string]any{"route": {"label": c.module, "endpoint": "web"}}, - Resources: []map[string]any{container}} - node := catalogue.Node{Name: c.node, At: c.node + ".internal", PublicDomain: "example.org", - Capabilities: map[string]bool{"container-runtime": true}} - r, err := catalogue.Resolve(map[string]catalogue.Manifest{module.Module: module, proxy.Module: proxy}, - []string{module.Module}, node, world) + values, _ := json.Marshal(map[string]any{"name": c.module + ".example.org", "port": c.port}) + contributed, err := catalogue.EncodeContributed(catalogue.ContributedForm{At: c.at, + Each: []catalogue.ContributedOne{{Values: values}}}) if err != nil { t.Fatalf("%s: %v", c.name, err) } - r.Account = "op" - for i, n := range r.Needs { - r.Needs[i].AnsweredByHelper = n.Name == "route" // as the plan sets it (compose's answeredByHelper) - } - ports := map[int]int{} - for _, l := range module.Listens { // compose's portsFor - where, mayAssign := module.MachineSide(l.Port) - if at, known := c.placed[l.Port]; mayAssign && known { - where = at - } - ports[l.Port] = where - } - composed, err := r.Compose(catalogue.Rendering{Hands: true, Provisioner: "mesh-provisioner-client", - ArtifactStore: "store:5000", Planned: true, Ports: map[string]map[int]int{c.module: ports}}) - if err != nil { - t.Fatalf("%s: %v", c.name, err) - } - value, err := provisions.EncodePlan(provisions.Plan{Node: c.node, Module: c.module, Plan: composed.Plans[c.module]}) + value, err := provisions.EncodePlan(provisions.Plan{Node: c.node, Module: c.module, Plan: catalogue.Plan{ + Provisions: []catalogue.PlanProvision{{Provision: contract.Provision, Provider: "anchor", + ProviderModule: contract.Module, Contributed: contributed}}}}) if err != nil { t.Fatalf("%s: %v", c.name, err) }