Skip to content

Share cached observed dependency target materialization - #1986

Merged
nadaverell merged 1 commit into
mainfrom
feature/relationship-observed-dependencies
Oct 13, 2026
Merged

nadaverell merged 1 commit into
mainfrom
feature/relationship-observed-dependencies

Conversation

@nadaverell

@nadaverell nadaverell commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

A refactor with no behavior change: certificate → issuer links are identical before and after. It moves cached observed-dependency materialization into one shared helper, so the identity, scope and deduplication rules have one implementation. The KEDA (#1987) and Flux (#1989) dependency PRs are built on it; it exists so those two don't carry separate copies of the same logic.

Controller-specific parsers supply exact source/target references and retain their own defaults and valid-kind rules. The common helper groups target reads by group/kind, lists each kind once, reuses existing node identities and adds only observed referenced targets. Missing sources or targets and repeated passes cannot fabricate or duplicate relationships. Exact cluster authorization metadata stays attached to materialized nodes.

Validation: the complete Go, type-check and frontend matrix passes at the final head. Fresh live REST/MCP checks preserve both issuer dependency directions and the Issuer's metadata owner. Unit cases cover lookup fanout, missing/reused targets, scope/group collisions, absent sources and repeated-pass deduplication. Visual testing is skipped because this backend refactor changes no rendered surface.

No new endpoint, renderer or mandatory provider interface is added. Cached reads are distinct from startup discovery and informer traffic.

@nadaverell
nadaverell requested a review from hisco as a code owner October 7, 2026 01:26
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Share cached materialization of observed dependency targets

âœĻ Enhancement 🧊 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

â€Ē Extract shared topology handling for controller-declared dependencies backed by observed CRD
 targets.
â€Ē Preserve certificate issuer defaults, node identities, namespace scope, and cluster-scoped
 authorization metadata.
â€Ē Avoid unnecessary lookups and duplicate edges; test cache failures, reuse, and repeated passes.
Diagram

graph TD
  Cert["Certificate issuerRef"] --> Parse["Issuer reference parser"] --> Helper["Observed dependency helper"] --> Eligible{"Eligible source?"}
  Eligible -->|yes| Cache["Dynamic cache"] --> Join["Identity join"] --> Graph["Topology graph"]
  Eligible -->|no| Skip["No lookup"]
  Join -->|observed targets| Graph
  subgraph Legend
    direction LR
    _step["Processing step"] ~~~ _decision{"Decision"}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Integrate with generic CRD graph construction
  • ➕ Could reuse CRD target discovery already performed by the generic graph pass.
  • ➖ Couples explicit dependencies to generic CRD settings and ownership handling.
  • ➖ Makes certificate issuer behavior harder to preserve independently.

Recommendation: Keep the focused shared helper: it makes declared dependencies reusable without changing the generic CRD pass or public contracts. The generic-pass alternative offers possible lookup reuse but adds coupling and behavior risk.

Files changed (4) +160 / -63

Enhancement (1) +107 / -0
observed_dependencies.goAdd shared cached observed-dependency graph construction +107/-0

Add shared cached observed-dependency graph construction

â€Ē Groups eligible references by exact group and kind, lists referenced targets once per kind, and joins only observed objects by canonical identity. Reuses existing nodes, preserves cluster-scoped authorization metadata, deduplicates Uses edges, and reports list failures per kind.

pkg/topology/observed_dependencies.go

Refactor (1) +7 / -61
certificate_issuers.goDelegate certificate issuer dependencies to the shared helper +7/-61

Delegate certificate issuer dependencies to the shared helper

â€Ē Retains issuerRef defaults, valid kinds, and namespaced versus cluster-scoped target selection. Converts valid declarations to exact resource references and delegates observed-target resolution and graph materialization.

pkg/topology/certificate_issuers.go

Tests (2) +46 / -2
certificate_issuers_test.goAdjust unavailable-issuer regression expectations +2/-2

Adjust unavailable-issuer regression expectations

â€Ē Gives the certificate source its namespace identity and expects one list-failure warning for the referenced Issuer kind rather than warnings for both issuer kinds.

pkg/topology/certificate_issuers_test.go

observed_dependencies_test.goTest shared dependency identity and lookup behavior +44/-0

Test shared dependency identity and lookup behavior

â€Ē Covers existing-node reuse, duplicate declarations and repeated passes, namespace and absent-target filtering, and no per-reference Get calls. Verifies that a missing source does not trigger target listing.

pkg/topology/observed_dependencies_test.go

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

qodo-free-for-open-source-projects Bot commented Oct 7, 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. Core-group links can hit wrong resources 📘 Rule violation ≡ Correctness
Description
addObservedDependencyEdges calls GetGVRWithGroup for every target, but an empty group is
resolved through the unqualified kind lookup rather than an exact core-group lookup. When a kind
exists in multiple API groups, the returned non-core GVR indexes observed objects under a different
group key, so valid core-group dependencies are not materialized.
Code

pkg/topology/observed_dependencies.go[56]

+		gvr, ok := provider.GetGVRWithGroup(kind.Kind, kind.Group)
Evidence
The new shared resolver requests a GVR by kind and group, but the discovery helper falls back to an
unqualified kind lookup for the empty core group. That lookup can select a non-core colliding
resource, while the resolver indexes objects with the returned GVR group and searches using the
requested core-group key, preventing the dependency from being found.

Rule 3036574: Disambiguate Kubernetes resource kinds that share the same Kind name by group-aware GVR and ID prefixes
pkg/topology/observed_dependencies.go[56-81]
pkg/k8score/discovery.go[475-499]
pkg/resourceid/model.go[35-37]

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

## Issue description
Core-group dependency targets pass an empty group to `GetGVRWithGroup`, whose fallback can select a colliding resource from another API group. The resulting GVR group does not match the dependency reference group, so valid core-group links can be skipped.
## Fix Focus Areas
- pkg/topology/observed_dependencies.go[56-78]
- pkg/k8score/discovery.go[491-531]
## Recommended Fix
Ensure the GVR lookup performs an exact group-aware match even when the requested group is empty, rather than falling back to the unqualified kind lookup. Keep observed-object indexing and dependency-key matching based on the same exact group identity, and add a collision test covering a core-group kind and a non-core resource with the same Kind.

ⓘ 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 turn these tips off under Display > Comment parts

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

nadaverell added a commit that referenced this pull request Oct 10, 2026
â€Ķling (#2048)

Topology's `uses` edges no longer mean only "autoscales". They also draw
mounted claims and declared dependencies, such as Certificate → Issuer,
Kustomization → its source, and Gateway → GatewayClass. Two places in
the UI still read every `uses` edge as autoscaling: the graph legend,
and the timeline's lane nesting.

## What changed

**Timeline nesting.** The timeline made the target of every `uses` edge
its source's parent lane. That's right for an autoscaler or a mounted
claim, which sit under the workload they serve. For a dependency it
files every dependent under one shared lane: every Certificate under its
ClusterIssuer, and Gateways from different namespaces under one
GatewayClass. Only HorizontalPodAutoscalers, VerticalPodAutoscalers,
KEDA ScaledObjects and ScaledJobs, and PersistentVolumeClaims, matched
on exact API group, now nest under their target.

**Graph legend.**
- Pink edges were labelled "scales"; they now read "uses".
- Gray `manages` edges were labelled "owns", though they also draw
GitOps inventory; they now read "manages".
- Policy and disruption-budget (`protects`) edges had no color of their
own and rendered in that same gray. They're now violet, with their own
legend entry.

## Testing

- A timeline test with an HPA → Deployment edge and a Certificate →
Issuer edge. The autoscaler nests, the dependency doesn't. It fails with
the old rule.
- Topology component tests, `make tsc`, and the shared UI and web suites
pass.

## Notes

The open dependency PRs (#1979, #1986, #1989, #1993) add `uses` edges
from dependents to their dependencies. This PR makes the legend and
timeline correct for them, so it should land before or with them.
@nadaverell
nadaverell force-pushed the feature/relationship-certificate-issuers branch from 5a684b7 to 3958943 Compare October 13, 2026 03:26
@nadaverell
nadaverell force-pushed the feature/relationship-observed-dependencies branch from 0975de5 to 5b8ad1e Compare October 13, 2026 03:26
@nadaverell

Copy link
Copy Markdown
Contributor Author

/review

Comment thread pkg/topology/observed_dependencies.go
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 5b8ad1e

Base automatically changed from feature/relationship-certificate-issuers to main October 13, 2026 09:14
Share cached exact-identity target enrollment so controller dependency joins reuse observed nodes without per-reference API reads.
@nadaverell
nadaverell force-pushed the feature/relationship-observed-dependencies branch from 5b8ad1e to f1e10bd Compare October 13, 2026 10:21
@nadaverell
nadaverell merged commit 85e87c2 into main Oct 13, 2026
9 checks passed
@nadaverell
nadaverell deleted the feature/relationship-observed-dependencies branch October 13, 2026 11:17
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