NamesServed read a route's public `name` and plan.go then filtered by suffix — telling the mesh's names from public ones by their spelling, when the mesh composed both itself. It now publishes the `internal-name` it composed under the serving node (ADR 0151); the suffix filter is gone.
116 lines
5.2 KiB
Go
116 lines
5.2 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// The mesh of forge issue 227 (novox/hq issue 178): a dashboard on the home server contributes its
|
|
// label to the route its proxy serves AND to the identity provider on the control node, which must
|
|
// know the dashboard's public name to compose a redirect. Both contributions carry the composed
|
|
// name; only the proxy serves it.
|
|
func twoNodesOneName(t *testing.T) (map[string]Resolution, map[string]SettingsBy) {
|
|
t.Helper()
|
|
catalogue := shelf(
|
|
Manifest{Module: "route-adapter", Version: "1", Provides: Offers("route"),
|
|
Serves: map[string]map[string]any{"route": {}}, Receives: map[string]string{"route": "/etc/adapter/mesh.json"}},
|
|
Manifest{Module: "route-proxy", Version: "1", Provides: Offers("route"),
|
|
Serves: map[string]map[string]any{"route": {}}, Receives: map[string]string{"route": "/etc/proxy/mesh.json"}},
|
|
Manifest{Module: "keycloak", Version: "1", Provides: FromAnywhere("oidc-client"),
|
|
Serves: map[string]map[string]any{"oidc-client": {"token-path": "/token"}},
|
|
Receives: map[string]string{"oidc-client": "/etc/keycloak/clients.json"},
|
|
Listens: []Listening{{Port: 8080, From: FromMesh, Why: "the login page"}},
|
|
// Published through the proxy itself: the identity provider is routed, not a router.
|
|
Contributes: map[string]map[string]any{"route": {"label": "login", "endpoint": "web", "port": 8080}}},
|
|
Manifest{Module: "grafana", Version: "1",
|
|
Listens: []Listening{{Port: 3000, From: FromMesh, Why: "dashboards"}},
|
|
Contributes: map[string]map[string]any{
|
|
"route": {"label": "grafana", "endpoint": "web", "port": 3000},
|
|
"oidc-client": {"label": "grafana", "endpoint": "web", "port": 3000, "callback": "/login"},
|
|
}},
|
|
)
|
|
home := withDomain("home.example")
|
|
home.Name, home.At = "home-server", "home-server.internal"
|
|
control := withDomain("control.example")
|
|
control.Name, control.At = "anchor", "anchor.internal"
|
|
|
|
onHome, err := Resolve(catalogue, []string{"grafana", "route-adapter"}, home, World{
|
|
Offered: map[string][]Provider{"oidc-client": {{Node: "anchor", At: "anchor.internal", Module: "keycloak"}}},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
onControl, err := Resolve(catalogue, []string{"keycloak", "route-proxy"}, control, World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return map[string]Resolution{"home-server": onHome, "anchor": onControl},
|
|
map[string]SettingsBy{"home-server": {}, "anchor": {}}
|
|
}
|
|
|
|
func TestANameResolvesToTheNodeWhoseProxyServesIt(t *testing.T) {
|
|
plans, settings := twoNodesOneName(t)
|
|
// Many times, because the fault was map order: one plan said one node, the next the other.
|
|
for i := 0; i < 25; i++ {
|
|
served, err := NamesServed(plans, settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if served["grafana.home-server.internal"] != "home-server" {
|
|
t.Fatalf("run %d: the dashboard's name is served by %q, and its proxy is on the home server: %v",
|
|
i, served["grafana.home-server.internal"], served)
|
|
}
|
|
if served["login.anchor.internal"] != "anchor" {
|
|
t.Fatalf("run %d: the identity provider's own name is served by its proxy on the control node: %v", i, served)
|
|
}
|
|
if _, leaked := served["grafana.anchor.internal"]; leaked {
|
|
t.Fatalf("a name composed for the identity provider's benefit is not one it serves: %v", served)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A module that is routed several times names each route (ADR 0094's sibling for contributes);
|
|
// every one of them is a name the mesh must resolve, and none reached the names region before.
|
|
func TestEveryRouteOfAModuleWithSeveralIsANameServed(t *testing.T) {
|
|
catalogue := shelf(
|
|
Manifest{Module: "route-proxy", Version: "1", Provides: Offers("route"),
|
|
Serves: map[string]map[string]any{"route": {}}, Receives: map[string]string{"route": "/etc/proxy/mesh.json"}},
|
|
Manifest{Module: "photos", Version: "1",
|
|
Listens: []Listening{{Port: 8102, From: FromMesh, Why: "web"}, {Port: 9102, From: FromMesh, Why: "api"}},
|
|
ContributesMany: map[string]map[string]map[string]any{"route": {
|
|
"site": {"label": "photos", "endpoint": "web", "port": 8102},
|
|
"api": {"label": "photos-api", "endpoint": "api", "port": 9102},
|
|
}}},
|
|
)
|
|
node := withDomain("control.example")
|
|
node.Name, node.At = "anchor", "anchor.internal"
|
|
plan, err := Resolve(catalogue, []string{"photos", "route-proxy"}, node, World{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
served, err := NamesServed(map[string]Resolution{"anchor": plan}, map[string]SettingsBy{"anchor": {}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, name := range []string{"photos.anchor.internal", "photos-api.anchor.internal"} {
|
|
if served[name] != "anchor" {
|
|
t.Fatalf("%s is not served by its proxy: %v", name, served)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A route's public name is the operator's and answered by public DNS; the mesh publishes only the
|
|
// internal name it composed for the same route (novox/hq ADR 0191) — told apart by where each was
|
|
// composed, never by how it is spelled.
|
|
func TestAPublicNameIsNeverAMeshName(t *testing.T) {
|
|
plans, settings := twoNodesOneName(t)
|
|
served, err := NamesServed(plans, settings)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, public := range []string{"grafana.home.example", "login.control.example"} {
|
|
if node, published := served[public]; published {
|
|
t.Fatalf("the public name %s is published at %s; public DNS answers it: %v", public, node, served)
|
|
}
|
|
}
|
|
}
|