Skip to content

kro: embed resource graphs on detail pages (0.2.0-alpha) - #1182

Open
danbruno101 wants to merge 3 commits into
headlamp-k8s:mainfrom
danbruno101:kro-embedded-graphs
Open

kro: embed resource graphs on detail pages (0.2.0-alpha)#1182
danbruno101 wants to merge 3 commits into
headlamp-k8s:mainfrom
danbruno101:kro-embedded-graphs

Conversation

@danbruno101

Copy link
Copy Markdown
Contributor

What this adds

Embedded resource graphs on the kro plugin's detail pages, and a 0.2.0-alpha release for them.

  • RGD detail gains a Template Graph: a synthetic RGD root, one node per spec.resources entry, and edges from the dependency graph kro publishes in status.resources[].dependencies (never re-derived from CEL). External externalRef resources and includeWhen conditionals are marked.
  • Instance detail gains a Resource Graph of the objects kro actually created, with live health. It is fed by the sub-resource watch set the page already owns, so the graph and the existing Sub-resources table always render from the same data.

Renderer selection at runtime

kubernetes-sigs/headlamp#6992 exposes Headlamp's Map renderer to plugins as window.pluginLib.ResourceMap.GraphView. That export is newer than Headlamp 0.44.0, and a build-time import of lib/ResourceMap would fail to typecheck against the released @kinvolk/headlamp-plugin and break the plugin at load time on released hosts. So the component is looked up on window.pluginLib at render time:

  • host has the export → sections render Headlamp's own GraphView, so nodes get KubeIcons and open the standard node details panel on click;
  • older host → a small @xyflow/react renderer (FallbackGraphView). That library comes from headlamp-plugin's own dependency tree, so this adds no runtime dependency.

Detection fails closed: anything other than a renderable component falls back.

Scoping an embedded map

defaultSources is appended to (not substituted for) the globally registered map sources, so scoping comes from defaultNodeSelection — the native GraphView renders only the selected node's connected component. Two consequences shaped the code:

  • the template graph is rooted at a synthetic node (template-root, deliberately not the RGD's uid, which the global source already uses with edges to every instance) so the DAG is always a single connected component;
  • instance ownership edges reuse the global map source's kro-owns-<uid> ids, so first-wins dedupe collapses duplicates instead of drawing them twice.

Known behaviors inherited from the Map renderer

Documented in the plugin README and filed upstream as kubernetes-sigs/headlamp#7242 (requesting an opt-in embedded mode): the embedded view writes ?node=/?group= to the page URL, resets the global namespace filter on mount, and shows the full Map chrome.

Testing

  • npm run lint, npm run tsc, npm run test (53 tests, 8 files), npm run build all pass.
  • New unit tests cover the graph builders (single-connected-component guarantee, defensive edge filtering, Inactive-RGD safety, edge ids, layout layering and cycle guard), the runtime detection (absent / non-renderable / function / lazy-object shapes), and the GraphSource adapter.
  • Verified live against a kind cluster running kro 0.9.2 with the kro-genaiops-demo RGDs, on both paths: the fallback renderer on Headlamp 0.44.x desktop, and the native GraphView on Headlamp built from main. Checked node-click details, live convergence after deleting the Deployment's pods, and that the global Map view and View-in-Map deep links still work.

Screenshots

Embedded template graph (RGD detail) Embedded instance graph
Template graph Instance graph
Node details opened from a graph node
Node details

🤖 Generated with Claude Code

https://claude.ai/code/session_016zqV1YPx3JSFGVXqoCp76s

danbruno101 and others added 2 commits August 14, 2026 15:25
The RGD detail page gains a template DAG (a synthetic RGD root, one
node per spec.resources entry, edges from kro's published dependency
analysis) and the instance detail page gains a live resource graph of
the objects kro created, both fed by the sub-resource watch set the
page already owns so the graph and the Sub-resources table can never
disagree.

Rendering picks the best available renderer at runtime.
kubernetes-sigs/headlamp#6992 exposes Headlamp's own Map renderer to
plugins as window.pluginLib.ResourceMap.GraphView; when the host has
it, the sections render that, so nodes get Headlamp's KubeIcons and
open its standard details panel on click. The export is newer than
Headlamp 0.44.0 and importing it at build time would break the plugin
on released hosts, so it is looked up on window.pluginLib at render
time and falls back to a small @xyflow/react renderer (that library
comes from headlamp-plugin's own dependency tree, so no runtime
dependency is added).

Because the host merges all registered map sources into an embedded
view, scoping comes from defaultNodeSelection: the native GraphView
renders only the selected node's connected component. The template
graph is therefore rooted at a synthetic node so it is always a single
component, and instance ownership edges reuse the global map source's
kro-owns-<uid> ids so duplicates dedupe away.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zqV1YPx3JSFGVXqoCp76s
Signed-off-by: Daniel Bruno <daniel.c.bruno@gmail.com>
Bump the plugin to 0.2.0-alpha, add the Artifact Hub packaging for it,
document the embedded graphs and the upstream behaviors they inherit
from the Map renderer (query params, global namespace filter, Map
chrome; tracked in kubernetes-sigs/headlamp#7242), and refresh the
screenshots to show the embedded template graph, the embedded instance
graph, and the node details opened from a graph node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zqV1YPx3JSFGVXqoCp76s
Signed-off-by: Daniel Bruno <daniel.c.bruno@gmail.com>
Run the formatter over the new graph builder tests (CI runs
format --check), and link the upstream Headlamp issue references as
full URLs: the owner/repo#number shorthand does not resolve in
rendered markdown files, so it pointed at whichever repository the
README was being viewed from.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zqV1YPx3JSFGVXqoCp76s
Signed-off-by: Daniel Bruno <daniel.c.bruno@gmail.com>
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