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.example"] != "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.example"], served) } if served["login.control.example"] != "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.control.example"]; 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.control.example", "photos-api.control.example"} { if served[name] != "anchor" { t.Fatalf("%s is not served by its proxy: %v", name, served) } } }