Skip to content

Link discovered custom-resource instances to their CRD definitions - #2013

Open
nadaverell wants to merge 3 commits into
mainfrom
feature/relationship-crd-definition-navigation
Open

nadaverell wants to merge 3 commits into
mainfrom
feature/relationship-crd-definition-navigation

Conversation

@nadaverell

@nadaverell nadaverell commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

A custom-resource instance should let you inspect its actual definition, including irregular plurals and colliding Kinds. Add a Definition link to the existing metadata section, using confirmed definition identity from the current cluster rather than guessing plural.group or trusting the broader isCrd category flag.

The existing discovery endpoint now supplies optional definitionName only when the definition is observed in watched CRD inventory and the caller passes the existing CRD list permission. This reuses the cached CRD GVR and performs one inventory scan, without per-resource live GETs, new watches or permission grants. Aggregated APIs and native groups stay unlinked. A context switch during assembly yields a retryable response.

Centralize discovery-driven navigation initialization in useAPIResources. Clear maps on failed/unavailable discovery, reset on a cluster switch and cancel pending old requests; the discovery fetch honors AbortSignal. The hook also owns the existing known GitOps identity catalog, replacing the competing view effect; actual discovery identities take precedence. Static seeds are navigation declarations and cannot manufacture CRD definition links. The drawer callback remains the existing exact ResourceRef contract. Reverse CRD instance listings, graph/context/diagnose expansion and new integration collection are separate surfaces.

Validation: 42 shared focused cases, 22 focused host cases, full host suite (1866 passed), shared UI suite (4152 passed, one skipped), make tsc, complete make build and full root make test. HTTP regression tests cover readable versus denied CRD inventory, aggregated metrics/native resources, and zero hot object reads. Hook tests cover failed refresh, recovery and canceled old discovery. On the final binary, a cold Widget drawer opens its actual cluster-scoped CRD with NamesAccepted; discovery confirms that CRD and excludes real native APIService/DRA definitions. One final-head capture was inspected; the attachment shows the same metadata layout. Focused self/product review complete; independent review deferred per request.

crd-definition-navigation


Note

Medium Risk
Touches the discovery API contract and cluster-switch cache/navigation lifecycle, but definition links are gated on existing CRD list RBAC and cached inventory only.

Overview
Adds confirmed CRD definition navigation from custom resource instances: the metadata drawer can show a Definition link that opens the cluster-scoped CustomResourceDefinition, using server-supplied identity instead of inferring from isCrd or English plurals.

The /api/api-resources response gains optional definitionName, filled only when the caller can list CRDs and the definition appears in already-watched CRD inventory (one cache scan, no per-kind live reads). Aggregated or native “CRD-like” APIs stay unlinked. If discovery/cache identity changes while building the response, the handler returns 503 retry.

On the web app, useAPIResources owns initNavigationMap: discovery fetch honors AbortSignal, maps reset on failed refresh, and resetNavigationMap plus query cancel runs on cluster context switch. GitOps kind seeds move into the hook as navigation-only entries that cannot fabricate definition links. customResourceDefinitionRef and drawer onNavigate wire the new metadata link.

Reviewed by Cursor Bugbot for commit f88ca77. Bugbot is set up for automated code reviews on this repo. Configure here.

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

Copy link
Copy Markdown

PR Summary by Qodo

Link custom-resource instances to their CRD definitions

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add a Definition link to instance metadata when discovery confirms its group and kind are a CRD.
• Use the discovered resource plural and existing navigation callback to open the cluster-scoped
 definition.
• Avoid guessed links for built-ins or undiscovered types, and clear CRD mappings on discovery
 refresh.
Diagram

graph TD
  Discovery["API discovery"] --> Map["CRD name map"] --> Match{"CRD match?"} -->|Yes| Link["Definition link"] --> Drawer["CRD drawer"]
  Metadata["Instance metadata"] --> Match
  Match -->|No| Existing["Existing metadata"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fetch the CRD for each instance
  • ➕ Could verify the definition at click or render time.
  • ➖ Adds requests and loading behavior to shared drawers.
  • ➖ May require permissions beyond existing resource access.

Recommendation: Keep the discovery-backed lookup and existing resource callback. Discovery already provides the exact group, kind, and plural needed for navigation; a per-instance fetch adds cost without improving the intended link behavior.

Files changed (4) +136 / -2

Enhancement (3) +17 / -2
ResourceRendererDispatch.tsxPass navigation into shared metadata +1/-1

Pass navigation into shared metadata

• Passes the existing onNavigate callback to MetadataSection so definition links in common resource drawers can open the CRD.

packages/k8s-ui/src/components/shared/ResourceRendererDispatch.tsx

drawer-components.tsxShow confirmed CRD definitions in metadata +4/-1

Show confirmed CRD definitions in metadata

• Adds an optional Definition property to MetadataSection when the instance's API version and kind resolve to a discovered CRD. Renders it with the existing ResourceLink, falling back to plain text when no navigation callback is supplied.

packages/k8s-ui/src/components/ui/drawer-components.tsx

navigation.tsResolve CRD names from API discovery +12/-0

Resolve CRD names from API discovery

• Builds a group-and-kind CRD name map from discovered resources, using each resource's declared plural. Exposes a cluster-scoped CRD reference only for confirmed matches and replaces or clears the map during initialization and reset.

packages/k8s-ui/src/utils/navigation.ts

Tests (1) +119 / -0
crd-definition-navigation.test.tsxTest exact CRD references and metadata navigation +119/-0

Test exact CRD references and metadata navigation

• Covers irregular plurals, group collisions, built-in and undiscovered exclusions, and replacement of discovery data. A DOM test verifies that clicking the metadata link passes the cluster-scoped CRD reference to the navigation callback.

packages/k8s-ui/src/components/ui/crd-definition-navigation.test.tsx

@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 (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Aggregated resources link to nonexistent definitions ✓ Resolved
Description
initNavigationMap treats every resource with isCrd as having a CRD definition, although backend
discovery sets that flag for any API group outside its built-in group list. When an aggregated API
such as metrics.k8s.io is discovered, its instances receive a Definition link to a CRD that does
not exist.
Code

packages/k8s-ui/src/utils/navigation.ts[80]

+    if (r.isCrd) crdNames[`${r.group}/${r.kind.toLowerCase()}`] = `${r.name}.${r.group}`
Evidence
Backend discovery defines isCRD by exclusion from coreAPIGroups, which does not include
metrics.k8s.io; the API passes that value to the frontend, where the new map turns it into a CRD
reference.

pkg/k8score/discovery.go[57-75]
pkg/k8score/discovery.go[244-256]
internal/server/server.go[2071-2090]
packages/k8s-ui/src/utils/navigation.ts[77-80]
packages/k8s-ui/src/utils/navigation.ts[199-203]

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

## Issue description
Backend discovery marks non-built-in API groups as `isCrd`, including aggregated APIs, so the new link can target nonexistent definitions.
## Fix Focus Areas
- pkg/k8score/discovery.go[244-256]
- packages/k8s-ui/src/utils/navigation.ts[77-80]
## Recommended Fix
Provide an authoritative indication that a discovered resource has a corresponding CRD, and populate definition references only for those resources. Cover an aggregated API in the navigation tests.

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



Remediation recommended

2. Cluster switches retain old definition links ✓ Resolved
Description
discoveredCRDNames is replaced only after a successful discovery result, while the application’s
discovery effects do not clear it when a cluster changes or its refresh fails. During that
transition, an instance in the new cluster with the same group and kind can link to the previous
cluster’s CRD name, including its different declared plural.
Code

packages/k8s-ui/src/utils/navigation.ts[65]

+let discoveredCRDNames: Record<string, string> | null = null
Evidence
The new CRD map is module-global; initialization is gated on a truthy query result, and its only
clearing function is not called by either application discovery effect. The shared query key and
reconnect invalidation allow the previous result to remain in use until a replacement arrives.

packages/k8s-ui/src/utils/navigation.ts[62-65]
packages/k8s-ui/src/utils/navigation.ts[91-103]
web/src/App.tsx[455-457]
web/src/components/resources/ResourcesView.tsx[110-114]
web/src/api/apiResources.ts[31-37]
web/src/context/ConnectionContext.tsx[149-158]

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

## Issue description
The module-global CRD identity map remains available while discovery for another cluster is pending or has failed.
## Fix Focus Areas
- packages/k8s-ui/src/utils/navigation.ts[65-65]
- web/src/App.tsx[455-457]
- web/src/components/resources/ResourcesView.tsx[110-114]
## Recommended Fix
Tie the CRD identity map to the active cluster. Clear it on a cluster change and when the replacement discovery result is unavailable, without reinitializing it from cached data for the previous cluster.

ⓘ 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 route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/k8s-ui/src/utils/navigation.ts Outdated
Comment thread packages/k8s-ui/src/utils/navigation.ts

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/k8s-ui/src/utils/navigation.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit af34411. Configure here.

Comment thread web/src/api/apiResources.ts
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