package catalogue import ( "strings" "testing" ) // a web module with one routed endpoint, the shape almost every routed module in the catalogue has. func aRoutedWeb() Manifest { return Manifest{ Module: "web", Listens: []Listening{{Port: 3000, From: FromMesh}}, Contributes: map[string]map[string]any{ "route": {"label": "app", "port": 3000}, }, } } func reachSet(reach string) SettingsBy { return SettingsBy{"web": {{From: "node anchor", Values: map[string]any{ReachSetting: map[string]any{"3000": reach}}}}} } // namesFor renders the contribution a routed module makes and returns the two names it carries. func namesFor(t *testing.T, m Manifest, settings SettingsBy) (public, internal string) { t.Helper() r := Resolution{Node: "anchor", Modules: []Manifest{m}, PublicDomain: "example.test", At: "anchor.internal"} given, err := r.contributions(settings, nil, nil) if err != nil { t.Fatalf("contributions: %v", err) } for _, c := range given["route"] { p, _ := c.Values["name"].(string) i, _ := c.Values["internal-name"].(string) return p, i } t.Fatal("the module contributed no route") return "", "" } // **Nothing said composes both names, exactly as before.** This is the assertion that keeps every // mesh already running identical until an assignment speaks, and it is the one that would break first // if reach were read where it should not be. func TestAnEndpointWithNoReachKeepsBothNames(t *testing.T) { public, internal := namesFor(t, aRoutedWeb(), nil) if public != "app.example.test" || internal != "app.anchor.internal" { t.Fatalf("names are %q and %q, want both composed as before", public, internal) } } // An internal endpoint has an internal name and no public one — so the proxy serves it inside, and // the public authority is never asked for a name nobody wanted. This is what "must not be public" // could not say before. func TestAnInternalEndpointHasNoPublicName(t *testing.T) { public, internal := namesFor(t, aRoutedWeb(), reachSet(ReachInternal)) if public != "" { t.Fatalf("an internal endpoint composed the public name %q", public) } if internal != "app.anchor.internal" { t.Fatalf("internal name is %q, want app.anchor.internal", internal) } } // And the mirror: a public endpoint gets the public name and not the internal one, so the mesh's own // authority is not asked to certify a name the service is not reached by. func TestAPublicEndpointHasNoInternalName(t *testing.T) { public, internal := namesFor(t, aRoutedWeb(), reachSet(ReachPublic)) if internal != "" { t.Fatalf("a public endpoint composed the internal name %q", internal) } if public != "app.example.test" { t.Fatalf("public name is %q, want app.example.test", public) } } func TestBothComposesBothNames(t *testing.T) { public, internal := namesFor(t, aRoutedWeb(), reachSet(ReachBoth)) if public == "" || internal == "" { t.Fatalf("both should compose both names, got %q and %q", public, internal) } } // **The filter reads the same value — for an endpoint the proxy does not serve.** // // A routed endpoint's port is how the proxy reaches it and nothing else (ADR 0045): a public service // listens from the mesh, only the proxy reaches it, and it is exposed by name. So on a routed // endpoint the reach asks for a name and the port keeps what the manifest said. func TestAnUnroutedEndpointsPortFollowsItsReach(t *testing.T) { // The same module with its route taken away: now the port is the only way in, so reach governs it. bare := aRoutedWeb() bare.Contributes = nil for _, c := range []struct{ reach, want string }{ {ReachInternal, FromMesh}, {ReachPublic, FromEverywhere}, {ReachBoth, FromEverywhere}, {ReachMachine, FromMachine}, } { r := Resolution{Node: "anchor", Modules: []Manifest{bare}} rules, err := r.Rules(Rendering{Settings: reachSet(c.reach)}) if err != nil { t.Fatalf("%s: rules: %v", c.reach, err) } found := false for _, rule := range rules { if rule.Port == 3000 { found = true if rule.From != c.want { t.Fatalf("reach %q made the filter say %q, want %q", c.reach, rule.From, c.want) } } } if !found { t.Fatalf("reach %q produced no rule for the port", c.reach) } } } // **A public name does not open the machine's port**, which is the case that found this. // // A module whose routed name must be public and whose machine-side port must not be had no way to say // so while one value drove both. Under one value it could not be expressed; the port would reopen. func TestAPublicNameLeavesARoutedPortAsTheManifestSaid(t *testing.T) { r := Resolution{Node: "anchor", Modules: []Manifest{aRoutedWeb()}, PublicDomain: "example.test", At: "anchor.internal"} rules, err := r.Rules(Rendering{Settings: reachSet(ReachPublic)}) if err != nil { t.Fatal(err) } for _, rule := range rules { if rule.Port == 3000 && rule.From != FromMesh { t.Fatalf("a public reach opened a routed port to %q; the proxy is how it is reached", rule.From) } } // And the name it asked for is there, so the reach was not simply ignored. public, internal := namesFor(t, aRoutedWeb(), reachSet(ReachPublic)) if public != "app.example.test" || internal != "" { t.Fatalf("names are %q and %q, want the public one only", public, internal) } } // A reach for a port the module does not listen on reaches nothing, and is refused where it is // written rather than accepted and ignored. func TestAReachForAPortTheModuleDoesNotListenOnIsRefused(t *testing.T) { _, err := Reaches(aRoutedWeb(), []Layer{{From: "node anchor", Values: map[string]any{ReachSetting: map[string]any{"9999": ReachInternal}}}}) if err == nil || !strings.Contains(err.Error(), "reaches nothing") { t.Fatalf("a reach naming an undeclared port was accepted: %v", err) } } // A value that is not a reach is refused, and the refusal names the four so a reader is one edit from // right. "mesh" is the tempting wrong answer, because that is the filter's word for nearly the same // thing. func TestAValueThatIsNotAReachIsRefused(t *testing.T) { for _, wrong := range []string{"mesh", "anywhere", "private", "true"} { _, err := Reaches(aRoutedWeb(), []Layer{{From: "node anchor", Values: map[string]any{ReachSetting: map[string]any{"3000": wrong}}}}) if err == nil || !strings.Contains(err.Error(), "a reach is") { t.Fatalf("%q was accepted as a reach: %v", wrong, err) } } } // **A port that says both reach and expose is refused.** They say the same thing in different words, // and accepting both would have the filter follow one while the names followed the other — the // disagreement ADR 0138 exists to remove, reintroduced by the migration away from the older word. func TestReachAndExposeForOnePortAreRefused(t *testing.T) { _, err := Reaches(aRoutedWeb(), []Layer{{From: "node anchor", Values: map[string]any{ ReachSetting: map[string]any{"3000": ReachInternal}, ExposeSetting: map[string]any{"3000": FromEverywhere}, }}}) if err == nil || !strings.Contains(err.Error(), "same thing in different") { t.Fatalf("a port set both ways was accepted: %v", err) } } // A path-level refusal carries no port: it is a rule about a name, not an endpoint, and it inherits // whatever that name turned out to be. Narrowing the endpoint must not silently drop it. func TestARuleWithNoPortIsLeftAlone(t *testing.T) { m := aRoutedWeb() m.ContributesMany = map[string]map[string]map[string]any{ "route": {"refused": {"label": "app", "path": "/internal", "deny": true}}, } r := Resolution{Node: "anchor", Modules: []Manifest{m}, PublicDomain: "example.test", At: "anchor.internal"} given, err := r.contributions(reachSet(ReachInternal), nil, nil) if err != nil { t.Fatal(err) } var sawDeny bool for _, c := range given["route"] { if deny, _ := c.Values["deny"].(bool); deny { sawDeny = true // It keeps both, because it named no endpoint to be narrowed by. if c.Values["name"] == nil || c.Values["internal-name"] == nil { t.Fatalf("the path rule lost a name it shadows: %v", c.Values) } } } if !sawDeny { t.Fatal("the path rule was dropped") } }