Skip to content

Topology routing: per-Pod Service membership, default backends, private Knative, and every Internet entrypoint - #2045

Merged
nadaverell merged 12 commits into
mainfrom
fix/topology-routing-membership
Oct 12, 2026
Merged

nadaverell merged 12 commits into
mainfrom
fix/topology-routing-membership

Conversation

@nadaverell

@nadaverell nadaverell commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

The traffic view drew routes that don't exist and dropped some that do:

  • A Service selecting only stable Pods was drawn to canary Pods too.
  • An Ingress with only a default backend had no edge, and its drawer warned "Traffic will not be routed".
  • A private Knative Service looked reachable from the Internet.
  • Every Traefik IngressRoute, Knative Service, Istio Gateway and Contour HTTPProxy was disconnected from the Internet node. The traffic view always groups by namespace for layout, even under "No Grouping", and regrouping rebuilt Internet edges only to Ingresses and Gateways.

These come from an audit of every place Radar builds topology edges; this PR fixes the routing and membership findings.

What changed

Pod membership is per Pod, not per group. Pods group by app label, so one group can hold stable and canary Pods that different Services select. The traffic view took the union of the group's Services and drew every one to every Pod.

  • Membership is now recorded per Pod (PodGroup.PodServiceIDs).
  • Individual Pod edges and summary-mode health counts use each Pod's own Services.
  • When a Service selects only part of a group, its edge to the group is labelled, e.g. "5 of 7 pods". Expanding the group reconnects each Pod only to its own Services, through the same ownerIds the resources view uses for mixed-owner groups.

Selectors require their keys. A selector value of "" matches only Pods that carry the label with an empty value, as in Kubernetes. Radar read a missing key as "", so a Service selecting role: "" was drawn to Pods without the label. This applies to every selector match in topology.

Every Internet entrypoint stays connected. Regrouping the Internet node now moves each original Internet edge, whatever the entrypoint kind, and leaves an ungrouped entrypoint on the original node. The logic moved into a pure, tested helper, internet-groups.ts.

Ingress default backends. Several readers ignored spec.defaultBackend. One extraction (configrefs.IngressBackendServices: the default backend plus every rule path, each Service once) now feeds:

  • Topology, in both views. The same edge ID used to be emitted once per path.
  • AI resource context.
  • The missing-Service check, which no longer reports one Service once per path and now checks the default backend.

The Ingress drawer shows a Default Backend row, for a Service or a resource backend, and no longer warns for an Ingress that routes everything to its default backend.

Private Knative Services. A Knative Service is private when it has the label networking.knative.dev/visibility=cluster-local, or when its published URL is its cluster-local address (status.url host equals status.address.url host). Knative builds a private Route's URL from the same cluster-local template as its address, on any cluster domain, so this also covers a label set on the Route. A DomainMapping that targets the Service or its Route publishes it again. Private Services get no Internet edge (private services, DomainMapping).

The node says why:

  • A cluster-local Service carries a Cluster-local badge. Radar withholds it if DomainMappings can't be read, because the Service may be mapped.
  • A mapped Service shows its custom domain as its subtitle. The tooltip lists every mapped domain and the internal address.

Traefik ServersTransport configures a route; it doesn't replace its backend. A backend with serversTransport used to become route → transport → Service, so the Service lost the route from its Routes, focused topology stopped at the transport, and two routes sharing a transport looked joined. Now:

  • The route always routes to its backend.
  • The transport is configuration of each route that uses it, drawn even when the backend Service is missing.
  • The transport resolves where Traefik resolves it: an HTTP backend's namespace, a TCP route's own namespace. A TraefikService backend ignores serversTransport, so it gets no transport edge.

Result on a live cluster

A kind cluster with 7 shop Pods (5 stable, 2 canary) behind three Services, 3 cart Pods behind stable and canary Services, Ingresses with a default backend, three repeated paths and missing Services, Traefik routes sharing a transport, and public, private and domain-mapped Knative Services. Same cluster, main vs this PR:

Traffic view, before Traffic view, after
Before: Internet reaches only Ingresses; IngressRoutes and Knative Services float unconnected After: every public entrypoint connected, private Knative Services off the Internet, partial-membership labels on the shop group
Ingress drawer, before Ingress drawer, after
Before: default-backend-only Ingress warns Traffic will not be routed After: Default Backend shop:80 and its Service under Related Resources
  • The "5 of 7" and "2 of 7" labels match the Services' EndpointSlices exactly, as do the per-Pod ownerIds.
  • A missing-Service audit on one Ingress went from gone three times to ghost-default and gone once each.

On a real Knative 1.23 install (EKS), the traffic view looks like this:

Knative on a real cluster: hello-private with a Cluster-local badge and no Internet edge; search-mapped and api-mapped showing their custom domains

Testing

  • Builder-level Go tests for each fix:
    • stable/canary Services against small groups, large groups and summary mode;
    • transports shared by two routes, with a missing backend, across namespaces, and on a TraefikService backend;
    • a default-backend-only Ingress and a triplicated path, in both views;
    • public, labelled, URL-private, custom-cluster-domain, unpublished and domain-mapped Knative Services, including the node's badge and domains, and a failed DomainMapping list;
    • selectors with an empty value, against Pods with and without the label.
  • An audit check test, an Ingress drawer render test, and unit tests for the regrouping helper.
  • Each new test fails with its fix reverted.
  • Full suites pass: pkg and internal Go tests, make tsc, the shared UI and web unit suites.

Notes

  • Not fixed here: every Istio Gateway and Gateway API Gateway still gets an Internet edge, including Knative's cluster-local gateway. Telling an external gateway from an internal one means reading its Service type and listeners, which is a design question. It's tracked in RAD-695, with these known gaps that predate this PR:
    • provider-qualified transport references (ns-name@kubernetescrd) and transports inside TraefikService weighted or mirroring lists aren't resolved;
    • app grouping doesn't move Internet edges for entrypoints the layout assigns to an app by inference.
  • An IngressRoute's backend still files under Pods in the drawer; Related resources: put every relationship under a true label #2049 moves it to Routes To.
  • Version skew: a newer UI against an older Radar keeps that Radar's membership decisions. An older UI against this Radar ignores the partial-membership label.

Pods group by app label, so one group can hold stable and canary Pods that
different Services select. The traffic view unioned the group's Services
and drew every one to every Pod, and summary mode gave each Service the
health of every Pod in the group. Membership is now tracked per Pod: Pod
edges, summary counts and group expansion use each Pod's own Services, and
a Service selecting only part of a group labels its edge ("4 of 7 pods").
A backend with serversTransport replaced the route → Service edge with a
route → transport → Service chain of configuration edges. The Service lost
the route from its Routes, focused topology stopped at the transport, two
routes sharing a transport looked joined through it, and the traffic view
(which keeps route → Service) disagreed with the resources view. The route
now always routes to its backend, and the transport is configuration of
each route that uses it.
Topology read only rule paths: an Ingress whose only backend is
spec.defaultBackend had no edge in either view (though its AI context
listed the Service), and an Ingress with several paths to one Service
emitted the same edge ID once per path. Topology and resource context now
share one backend extraction that includes the default backend and lists
each Service once.
The traffic view connected the Internet to every Knative Service. A
Service labelled networking.knative.dev/visibility=cluster-local (or made
private through its Route, which publishes a <name>.<ns>.svc URL) is only
reachable inside the cluster; it now has no Internet edge.
Grouping replaced the Internet node with one per group, but rebuilt edges
only to Ingresses and Gateways: Istio Gateways, Knative Services, Traefik
routes and Contour HTTPProxies lost their Internet connection. Regrouping
now moves each original Internet edge, whatever its entrypoint kind, and
leaves an ungrouped entrypoint on the original node.
The missing-Service check read only rule paths, so an Ingress whose
default backend names a missing Service passed (and one Service missing
from several paths was reported once per path). The Ingress drawer warned
"Traffic will not be routed" for an Ingress with only a default backend
and never showed it. Both now use the default backend: the check through
the shared backend extraction, the drawer as a Default Backend row.
The transport → route configuration edge was resolved after the backend
Service lookup and skipped with it.
@nadaverell
nadaverell requested a review from hisco as a code owner October 10, 2026 02:27
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Correct topology routes, Pod membership, and Internet entrypoints

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Connect Pods and health summaries only to Services that select them, including within mixed
 groups.
• Restore Ingress defaults and Traefik backends while excluding private Knative Services from
 Internet routes.
• Preserve every Internet entrypoint during regrouping and add cross-stack regression coverage.
Diagram

graph TD
  I["Grouped Internet"] -->|"public routes"| E["Entry points"] -->|"routes to"| S["Backend Service"] --> G["Pod group"] -->|"selected members"| P["Selected Pods"]
  T["ServersTransport"] -. "configures routes" .-> E
  X["Ingress backends"] -. "selects target" .-> S
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Group Pods by Service-selector membership
  • ➕ Each group would have uniform Service membership, simplifying collapsed routing edges.
  • ➖ Splits a single app into additional groups and changes the existing grouping model.
  • ➖ Still requires correct per-Pod attribution for health summaries and expanded views.

Recommendation: Keep the PR's per-Pod membership approach: it corrects routing without changing app-based grouping. The shared Ingress extractor also keeps topology, audit, and AI context aligned, while regrouping existing Internet edges avoids another entrypoint-kind allowlist.

Files changed (15) +601 / -220

Bug fix (7) +237 / -205
IngressRenderer.tsxDisplay Ingress default backends +16/-3

Display Ingress default backends

• Adds a navigable Default Backend property and treats that backend as a valid route. Hides the empty Rules section when there are no rules.

packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx

TopologyGraph.tsxUse shared Internet regrouping helper +3/-71

Use shared Internet regrouping helper

• Replaces inline, Ingress-and-Gateway-only Internet splitting with the edge-preserving helper for grouped traffic views.

packages/k8s-ui/src/components/topology/TopologyGraph.tsx

internet-groups.tsRegroup existing Internet edges without filtering entrypoint kinds +55/-0

Regroup existing Internet edges without filtering entrypoint kinds

• Moves each groupable Internet edge to a per-group Internet node regardless of target kind. Retains the original node and edges for targets without a group.

packages/k8s-ui/src/components/topology/internet-groups.ts

checks.goAudit distinct Ingress backends, including the default +9/-19

Audit distinct Ingress backends, including the default

• Uses shared backend extraction to report a missing Service once per name, including a default backend. Default-backend-only Ingresses now qualify for the check.

pkg/audit/checks.go

ingress.goCentralize distinct Ingress Service references +29/-0

Centralize distinct Ingress Service references

• Adds an extractor for Service names from the default backend followed by rule paths. It preserves first-seen order, deduplicates names, and ignores non-Service backends.

pkg/configrefs/ingress.go

builder.goCorrect backend, visibility, and Pod membership edges +116/-108

Correct backend, visibility, and Pod membership edges

• Routes both topology views to distinct Ingress backends, including defaults, and keeps Traefik route-to-backend edges separate from transport configuration. Excludes cluster-local Knative Services from Internet edges and uses per-Pod Service membership for traffic edges, summaries, partial-group labels, and expansion metadata.

pkg/topology/builder.go

pod_grouping.goRecord Service membership for each grouped Pod +9/-4

Record Service membership for each grouped Pod

• Adds PodServiceIDs alongside the group's Service union, keeping selector matches aligned with each Pod for downstream routing and health calculations.

pkg/topology/pod_grouping.go

Refactor (1) +3 / -15
build.goShare Ingress backend extraction with AI context +3/-15

Share Ingress backend extraction with AI context

• Builds Ingress backend Service references from the shared extractor instead of maintaining separate traversal logic.

pkg/resourcecontext/build.go

Tests (7) +361 / -0
IngressRenderer.defaultBackend.test.tsxCover default-backend-only Ingress rendering +22/-0

Cover default-backend-only Ingress rendering

• Verifies that a default backend appears in the drawer without a no-routing warning. Confirms the warning remains for an Ingress with no backend or rules.

packages/k8s-ui/src/components/resources/renderers/IngressRenderer.defaultBackend.test.tsx

internet-groups.test.tsTest Internet connections after regrouping +35/-0

Test Internet connections after regrouping

• Checks that different entrypoint kinds retain Internet edges in their namespace groups. Also verifies that an ungrouped entrypoint stays connected to the original Internet node.

packages/k8s-ui/src/components/topology/internet-groups.test.ts

checks_test.goTest missing default-backend audit finding +21/-0

Test missing default-backend audit finding

• Adds a check that an Ingress referencing a nonexistent default Service produces one missing-Service finding.

pkg/audit/checks_test.go

ingress_backends_test.goTest Ingress routing in both topology views +53/-0

Test Ingress routing in both topology views

• Verifies that a default-backend-only Ingress has a Service edge and that three paths to one Service emit that edge once in each view.

pkg/topology/ingress_backends_test.go

knative_visibility_test.goTest public and cluster-local Knative reachability +50/-0

Test public and cluster-local Knative reachability

• Confirms Internet reaches a public Knative Service but not Services made private by a visibility label or a cluster-local published URL.

pkg/topology/knative_visibility_test.go

traefik_transport_test.goTest Traefik transport and backend relationships +67/-0

Test Traefik transport and backend relationships

• Verifies that shared transport configuration points to each route without replacing route-to-Service edges. Covers a missing backend and the Service's reported route relationship.

pkg/topology/traefik_transport_test.go

traffic_membership_test.goTest stable and canary Pod membership +113/-0

Test stable and canary Pod membership

• Checks selector-correct edges for individual Pods, partial-membership labels and expansion metadata for a collapsed group, and Service-specific Pod counts in summary mode.

pkg/topology/traffic_membership_test.go

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Default backends get the wrong edge type 📘 Rule violation ≡ Correctness
Description
buildResourcesTopology and buildTrafficTopology assign EdgeRoutesTo rather than EdgeExposes
to Ingress-to-Service edges produced by IngressBackendServices. The helper newly includes
spec.defaultBackend, so a default-backend-only Ingress now creates this incorrectly typed edge in
both views.
Code

pkg/topology/builder.go[3634]

+					Type:   EdgeRoutesTo,
Evidence
Rule 3036572 assigns Ingress exposure relationships to EdgeExposes. The new backend extraction
includes default backends, while both topology builders emit those edges as EdgeRoutesTo.

Rule 3036572: Use semantically correct topology edge types for Kubernetes relationships
pkg/configrefs/ingress.go[19-25]
pkg/topology/builder.go[3628-3635]
pkg/topology/builder.go[6373-6383]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New default-backend Ingress edges use a routing type where the checklist requires an exposure type.
## Fix Focus Areas
- pkg/topology/builder.go[3628-3635]
- pkg/topology/builder.go[6373-6383]
- pkg/topology/relationships.go[350-375]
- pkg/topology/relationships.go[425-445]
## Recommended Fix
Use `EdgeExposes` consistently for Ingress-to-Service edges in both views. Update relationship projection as needed so Ingress and Service relationship lists retain their current behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Resource default backends vanish from the drawer ✓ Resolved
Description
IngressRenderer suppresses the no-rules warning for any spec.defaultBackend, but renders the
Default Backend row only when that backend has a Service name. When an Ingress uses
spec.defaultBackend.resource, the drawer shows neither its destination nor a routing warning.
Code

packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx[R55-56]

+          {defaultService?.name && (
+            <Property label="Default Backend" value={
Evidence
The helper explicitly distinguishes resource backends from Service backends. The renderer treats
either as sufficient to disable the warning, but its only default-backend display requires a Service
name.

pkg/configrefs/ingress.go[5-16]
packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx[19-21]
packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx[55-64]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A valid resource-backed Ingress default backend is absent from the drawer because the new row only handles Service backends.
## Fix Focus Areas
- packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx[19-21]
- packages/k8s-ui/src/components/resources/renderers/IngressRenderer.tsx[55-64]
## Recommended Fix
Render a Default Backend row for `spec.defaultBackend.resource` that identifies its API group, kind, and name, while retaining the existing Service link for Service backends. Add a render test for a resource-only default backend.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Public Knative apps on an "svc." domain lose their Internet edge ✓ Resolved
Description
knativeServiceIsClusterLocal treats a Service as private whenever its status URL host equals
..svc or starts with ..svc., without checking that what follows is the cluster domain. With
Knative's default {{.Name}}.{{.Namespace}}.{{.Domain}} template and a public domain such as
svc.example.com, a public Service at web.team.svc.example.com is classed as cluster-local, so
the traffic view drops its Internet edge.
Code

pkg/topology/builder.go[R9121-9123]

+	local := ksvc.GetName() + "." + ksvc.GetNamespace() + ".svc"
+	host := parsed.Hostname()
+	return host == local || strings.HasPrefix(host, local+".")
Evidence
The host check accepts any suffix after .svc.. A public domain that begins with svc. gives the
same host prefix as the cluster-local ..svc.cluster.local. The result decides whether the Service
is added to publicKnativeServiceIDs, which is the list that gets Internet edges.

pkg/topology/builder.go[9113-9124]
pkg/topology/builder.go[6666-6670]
pkg/topology/builder.go[7296-7300]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`knativeServiceIsClusterLocal` marks a Knative Service as private when its URL host starts with `<name>.<namespace>.svc.`. That also matches public hosts under a domain like `svc.example.com`, so those Services lose their Internet edge.
## Fix Focus Areas
- pkg/topology/builder.go[9113-9124]
## Recommended Fix
Only treat the host as cluster-local when it is exactly `<name>.<namespace>.svc`, or `<name>.<namespace>.svc.cluster.local` (or the configured cluster domain if Radar knows it). Do not accept any suffix after `.svc.`. Keep the visibility-label check as it is. Add a test case with a public URL such as `https://web.team.svc.example.com`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread pkg/topology/builder.go
Comment thread pkg/topology/builder.go Outdated
…al URL

An Ingress whose default backend is a resource (not a Service) lost its
routing warning without gaining a row; it now shows the resource. A
Knative URL counts as cluster-local only at <name>.<ns>.svc.cluster.local,
so a public domain that starts with "svc." keeps its Internet edge; the
visibility label covers other cluster domains.
…s do

A Knative Service is private when its published URL is its cluster-local
address, on any cluster domain, and a DomainMapping publishes even a private
one. An HTTP route's ServersTransport resolves in its backend's namespace, a
TCP route's in the route's, and a TraefikService backend has none.
@nadaverell

Copy link
Copy Markdown
Contributor Author

Qodo review of 9e677a7:

  1. Default backends get the wrong edge type: skipping. On main, every Ingress → Service edge, for every rule path in both views, is already routes-to. Default-backend edges reuse that type so an Ingress has one kind of edge to its Services. The relationship projection, the drawer's Services list and focused topology all read Ingress → Service as routes-to. Typing only the default backend as exposes would split one Ingress's backends across two groups. Retyping all Ingress edges would be a separate change to the edge model; it's tracked with the edge-type definitions in our backlog.
  2. Resource default backends vanish from the drawer: already resolved; the drawer shows a Default Backend row for both Service and resource backends.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Qodo review of 9e677a7:

  1. Default backends get the wrong edge type: skipping. On main, every Ingress → Service edge, for every rule path in both views, is already routes-to. Default-backend edges reuse that type so an Ingress has one kind of edge to its Services. The relationship projection, the drawer's Services list and focused topology all read Ingress → Service as routes-to. Typing only the default backend as exposes would split one Ingress's backends across two groups. Retyping all Ingress edges would be a separate change to the edge model; it's tracked with the edge-type definitions in our backlog.
  2. Resource default backends vanish from the drawer: already resolved; the drawer shows a Default Backend row for both Service and resource backends.

For finding 1, the concern is specifically the rule’s required semantic type: Ingress-to-Service exposure relationships should use EdgeExposes, even though the existing rule-path edges currently use EdgeRoutesTo. Reusing the existing type preserves consistency but also preserves the reported semantic violation; changing all Ingress backend edges would indeed be a broader edge-model change.

The finding remains open because the recommended fix targets both default and rule-path Ingress edges, plus the relationship projection—not only the newly added default-backend edges. You can update the rule’s scope or disable it here: https://app.qodo.ai/rules/3036572?state=active

The resource-backend finding is already resolved, as noted.

A selector such as role: "" selects only Pods that carry the role label
with an empty value. Radar compared labels[k] with the value, so a missing
key read as "" and matched, and the Service was drawn to Pods it doesn't
select. Require the key in both selector helpers, as Kubernetes does.
A cluster-local Knative Service is now drawn without an Internet edge, but
nothing on the node said why, so it looked disconnected by mistake. A
private Service published through a DomainMapping showed its cluster-local
URL next to an Internet edge. Mark cluster-local Services with a badge, and
show a mapped Service's custom domain, with every domain and the internal
URL in its tooltip. When DomainMappings can't be read, the badge is
withheld, since the Service may be mapped.
@cursor

cursor Bot commented Oct 12, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@nadaverell
nadaverell merged commit 6713f57 into main Oct 12, 2026
9 checks passed
@nadaverell
nadaverell deleted the fix/topology-routing-membership branch October 12, 2026 07:27
nadaverell added a commit that referenced this pull request Oct 12, 2026
Radar's Related Resources section, and the MCP resource context given to
AI tools, put many relationships under labels that were false for them:
- An IngressRoute listed its backend Service under **Pods**.
- A VirtualService listed its Istio Gateway under **Services**.
- A Service listed the HTTPRoutes in front of it under **Gateways**.
- A Traefik Middleware showed the route that uses it as its
**Configuration**, the reverse of the truth.
- A ServiceMonitor appeared under **Used By** on everything it scrapes.

These come from an audit of every place Radar builds topology edges.
This PR makes each group's label true. It builds on #2045 (merged),
which owns the Traefik routing edges this PR also touches.

## What changed

**Configuration edges point from configuration to consumer.** A
`configures` edge means "source configures target", and the projection
reads it that way. These producers drew it from consumer to
configuration, so their labels came out swapped:
- Karpenter NodePool → NodeClass.
- Traefik routes → Middleware, TLSOption, TLSStore, ServersTransport and
Certificate.
- Middleware chains.
- ServersTransport, TLSOption and TLSStore → Secrets.
- Contour HTTPProxy → TLS Secret and Certificate.

They're now reversed, so a route lists its middleware under
Configuration and the middleware lists the route under Used By.

**A Knative front door exposes its Knative Service.** Knative's Route
creates a core Service named after the Knative Service: the alias for
the ingress that callers address. Radar drew it as the Knative Service's
backend, matched by name alone. It's now drawn as the Service that
exposes the Knative Service, and only when the Route of that name owns
it. The Knative Service lists it under Services, and it lists the
Knative Service under Routes To.

**New groups for relationships that had none.** Each matches on exact
API group. All are additive fields on `Relationships`, with drawer
labels:

| Field | Label | What it holds |
|---|---|---|
| `backends` | Routes To | what a Service, router, Istio Gateway,
Knative Route, Broker or event source routes to (Pods stays for real
Pods) |
| `routedFrom` | Routed From | upstream routers that aren't core
Services or recognized routes (Istio Gateway, Broker, event source) |
| `monitors` / `monitorTargets` | Monitored By / Monitors | Prometheus
Operator ServiceMonitor and PodMonitor, both directions |
| `protects` | Selects | what a disruption budget or network policy
selects (previously not shown at all) |
| `healthChecks` | Health Checks | a CAPI Cluster's MachineHealthChecks
|
| `stagedPolicies` | Staged Policies | Calico staged policies, kept
apart from enforcing Network Policies |

Workload history attaches staged policies alongside NetworkPolicies, so
a staged policy's changes stay on the workload's timeline. A Knative
Service's chip reads `ksvc/` rather than `svc/`, so it isn't mistaken
for the core Services listed beside it.

**Existing groups tightened:**
- **Services:** only core Services.
- **Gateways:** only Gateway API Gateways. HTTP, GRPC, TCP, TLS and UDP
routes go to **Routes**.
- **Routers:** no longer list themselves or their sibling routes as
their own upstream.

**MCP resource context.** `exposes` merged both directions, so an
Ingress "exposed" its Service and the Service "exposed" the Ingress.
Directional `exposedBy` and `backends` fields are added, along with
`monitors`, `monitorTargets`, `protects`, `healthChecks` and
`stagedPolicies`. `exposes` stays as their union for compatibility. Each
field applies the caller's read permissions, and `docs/mcp.md` describes
them. Applications and the workload view's serving path keep counting
the entries that moved.

## Result on live clusters

I ran this PR and `main` against real Traefik 3.7, Contour 1.33, Knative
1.23 and Istio installs, and compared Related Resources for the same
objects:

| Resource | `main` | This PR |
|---|---|---|
| IngressRoute `edge-web` | **Pods:** Service `whoami` · **Used By:**
its Middleware | **Routes To:** Service `whoami` · **Configuration:**
ServersTransport, Middleware |
| Middleware `strip-api` | **Configuration:** the route using it |
**Used By:** the route |
| HTTPProxy `shop` | **Pods:** Service, child HTTPProxy · **Used By:**
its TLS Secret | **Routes To:** Service, child HTTPProxy ·
**Configuration:** its TLS Secret |
| Istio VirtualService `productpage` | **Pods:** Service · **Services:**
Istio Gateway | **Routes To:** Service · **Routed From:** Istio Gateway
|
| Knative Route `hello-public` | **Pods:** Revision | **Routes To:**
Revision |

Applications output was identical on both builds for every namespace
checked.

| IngressRoute drawer, `main` | IngressRoute drawer, this PR |
|---|---|
| ![main: backend Service under Pods, the route's Middleware under Used
By](https://github.com/user-attachments/assets/583dc9e2-c27c-464e-959b-fe1bdd292ebf)
| ![This PR: ServersTransport and Middleware under Configuration, the
Service under Routes
To](https://github.com/user-attachments/assets/016e88f1-b394-4811-b649-9bc5354ab6f1)
|

## Testing

- Builder-level projection tests for each item, plus drawer and
workload-view render tests and resource-context tests. Each fails with
its fix reverted.
- `pkg` and `internal` Go tests, `make tsc`, and the shared UI and web
suites pass.

## Notes

- **Version skew:** a newer UI against an older Radar renders the old
fields as before. An older UI against this Radar loses only the entries
that moved into the new groups.
- **Scope:** the Traefik TLS-Secret and traffic-view configuration
producers don't exist in the builder, so they weren't invented here.
- **Follow-up:** a semantic relation field on edges, which would replace
classification by Kind and group, is tracked in RAD-695.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant