Skip to content

feat(topology): visualize canary/blue-green traffic in the resource graph - #1626

Open
jfillman wants to merge 11 commits into
skyhook-io:mainfrom
jfillman:feature/rollout-topology-traffic
Open

jfillman wants to merge 11 commits into
skyhook-io:mainfrom
jfillman:feature/rollout-topology-traffic

Conversation

@jfillman

@jfillman jfillman commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The Rollout deployment-plugin work (renderer-level: step timeline, AnalysisRun history, etc.) told the "what's this rollout doing" story in the resource drawer. This tells the same story graphically in the main cluster Topology view — which ReplicaSets/Pods are canary vs. stable (or active vs. preview for blue-green), which Services route to which, and how much traffic each side is getting.

  • Fixed a real bug along the way: the existing Service→Rollout edge matcher compared a Service's selector against the Rollout's static spec.template.metadata.labels — but canary/stable Service selectors are keyed on the live rollouts-pod-template-hash, which isn't in that static label set, so these edges silently never rendered. Replaced with direct name-matching against spec.strategy.canary.canaryService/.stableService (and the blue-green equivalents), which the Rollout already declares.
  • trafficRole computed server-side at Pod/ReplicaSet/Service node build time (canary/stable/active/preview), read off the same rollouts-pod-template-hash label those nodes already carry — no new K8s API calls.
  • Canary weight labels (Canary · 20% / Stable · 80%) on the Service→workload edges: reads status.canary.weights when a service-mesh trafficRouting plugin is configured, falling back to deriving the split from the current step's setWeight for "basic canary" rollouts (the common case — status.canary.weights is only ever populated with a mesh plugin, confirmed against real Argo Rollouts behavior).
  • Rollout-owned ReplicaSets bypass the global IncludeReplicaSets collapse when live (spec.replicas > 0) — surfaces exactly the revisions actually carrying traffic without touching the broader Deployment-history noise-reduction setting.
  • Traffic-role badges on Pod/ReplicaSet/Service nodes, and the canary/stable (or active/preview) edges animate in Network Flow view, reusing the graph's existing traffic-animation plumbing.
  • Per-resource Topology tab now polls instead of fetching once, so it stays live during a rollout instead of going stale.

Test plan

  • go test ./pkg/topology/... (new coverage: Service→Rollout edge + weight label for both strategies, trafficRole on Pod/RS/Service, zero-replica old ReplicaSets stay hidden, step-weight fallback)
  • make tsc
  • npx vitest run (k8s-ui)
  • Live-verified against canary + blue-green Rollout fixtures on a local cluster

🤖 Generated with Claude Code


Note

Medium Risk
Touches topology graph construction and edge rendering for production workloads; changes are well-tested but incorrect traffic-role logic could mislabel blue-green vs canary revisions.

Overview
Adds end-to-end Argo Rollouts traffic visualization in the resources topology: server-side trafficRole (canary/stable/active/preview) on Services, ReplicaSets, and Pods, weighted edge labels like Canary · 20%, and matching badges plus animated dashed edges on the graph (including outside Network Flow view).

Topology builder fixes and behavior: Service→Rollout links now match named canary/stable/active/preview Services instead of label selectors (which missed live rollouts-pod-template-hash). Live Rollout-owned ReplicaSets stay visible when global ReplicaSet collapse is off; ownership edges carry the same traffic labels down to pods. Large mixed-owner PodGroups get correct multi-owner edges and per-pod ownerIds so expansion does not wire canary pods to stable ReplicaSets.

Rollout health copy: Go and TypeScript argoStepDetail now share rich canary step labels (pause, experiment, setWeight, etc.) and optional canary traffic %, kept in sync via golden vectors.

Workload drawer: The per-resource Topology tab polls every 5s while expanded so weights and roles update during a rollout.

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

…urce graph

Rollout nodes now read canaryService/stableService/activeService/previewService
and canary weights from spec/status, and the Service->Rollout matcher fixes a
real bug where canary/stable Services never connected at all (their selector
targets the live rollouts-pod-template-hash, which never appears in the
Rollout's static template labels — name matching is used instead, falling
back to the old selector match for a Rollout's ordinary/primary Service).
Pods and ReplicaSets owned by a Rollout are tagged with a trafficRole
(canary/stable/active/preview) by comparing their pod-template-hash against
the Rollout's live status pointers. Rollout-owned ReplicaSets that still have
replicas bypass the IncludeReplicaSets collapse so the canary/stable split is
visible without the noisy-history toggle.
…c edges

Pod/ReplicaSet/Service nodes carrying a server-set trafficRole (canary,
stable, active, preview) get a small accent badge in the existing header
pill row — accent1 for the being-tested side (canary/preview), accent2 for
the serving side (stable/active).

Canary/stable/active/preview Service->Rollout edges now animate in the main
resources-view topology, not just the separate Network Flow view — that view
builds its own Ingress/Gateway/Service/Pod graph and never includes Rollout
edges at all, so gating on isTrafficView would mean these edges never
animate. Detected via the edge's fixed label vocabulary set server-side
(Canary/Stable with a weight suffix, or bare Active/Preview).
…ership

Service->Rollout exposes edges already animated for a canary/stable/active/
preview role; ownership edges (Rollout->ReplicaSet, ReplicaSet->Pod) stopped
short of that even though the target node already carries the same
trafficRole. Detected via a nodeId->trafficRole lookup built from the node
list (ownership edges carry no label to key off, unlike the exposes edges),
so the "active" path now reads continuously from the Service all the way
down to the pods actually serving that role - live-verified against the
blue-green scenario on kind-radar-rollouts-demo, user-reported gap.
…y role

Service->Rollout edges already carried a "Canary · 20%" style label; the
animated arrows one hop further down (Rollout->ReplicaSet, ReplicaSet->Pod)
carried the same visual treatment but no text, so a blinking edge told the
user nothing about which revision was carrying which share of traffic -
exactly the gap called out live-testing canary: "the blinking arrows aren't
that useful if they all blink throughout all the steps."

rolloutTrafficEdgeLabel is the one place this label string is built now
(previously inlined only at the Service-matching site); reused at both new
edge sites via the trafficRole each already computes for its own node.
The rollout-in-progress banner (WorkloadRolloutNotice, shown on the Topology
tab and others while a Rollout/Deployment/StatefulSet/DaemonSet is actively
rolling out) said only "Step 3" - no hint what that step actually does, and
no traffic-weight number, even though the same information is already
formatted for the drawer's overview page via canaryStepLabel. Now reuses
that exact function so the two surfaces describe a step identically, and
appends the live canary weight when the controller has reported one.

pkg/health/workload_rollout.go carries a parallel Go port (canaryStepLabel)
for cross-language parity with the TS version - the two are checked against
the same golden fixture (workload_rollout_vectors.json, updated here for the
2 vectors whose expected detail text changed), not duplicated by accident.
… plugin

Confirmed live: status.canary.weights is NEVER populated for a "basic
canary" Rollout (no Istio/SMI/ALB/NGINX trafficRouting configured) - the
controller drives the split by scaling the canary ReplicaSet's replica
count to approximate the last setWeight step reached, and simply never
writes that status field. Every Rollout in the demo fixture is basic
canary, so every traffic edge showed a bare "Canary"/"Stable" role with no
percentage for the entire time it was progressing - user-reported live
while testing.

canaryStepWeight falls back to walking backward from currentStepIndex to
the most recent setWeight step when status.canary.weights is absent.
Returns nothing once fully promoted (currentStepIndex past the end of
steps) - there's no active split left to report, and the settled "Stable"
label correctly shows no percentage there (100% implied).
… once

useTopology's query had no refetchInterval here and none is set by default -
it fetched once when the drawer opened and then never refreshed. Every
symptom reported live-testing canary traced back to this single gap:
weight labels stuck at their initial value, a Pod's role never flipping
canary->stable, the old ReplicaSet never disappearing once scaled down, an
AnalysisRun staying "Running" long after it finished on the cluster - none
of it was a rendering bug, the tab just never asked the backend for new
data. Matches the existing useTrace "drawer feeling live" polling idiom
(5s interval).
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Visualize Argo Rollouts traffic in topology

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Visualizes canary and blue-green roles, weights, and live ReplicaSets across topology.
• Fixes rollout Service edges by matching declared traffic-routing Service names.
• Adds live polling, animated traffic paths, detailed rollout steps, and regression coverage.
Diagram

graph TD
  cluster["Cluster Resources"] --> builder["Topology Builder"] --> payload["Role-Aware Graph"] --> graph["Topology Graph"] --> badges["Role Badges"]
  graph --> animations["Animated Edges"]
  polling["Workload Polling"] --> builder
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Typed traffic edge metadata
  • ➕ Avoids detecting rollout traffic by parsing display-label vocabulary
  • ➕ Separates presentation text from animation and traffic semantics
  • ➕ Supports future rollout roles without frontend string matching
  • ➖ Requires expanding and coordinating the topology API contract
  • ➖ Adds migration and compatibility work beyond the current feature scope
2. Client-side rollout classification
  • ➕ Keeps rollout-specific presentation logic near the graph UI
  • ➕ Could reduce backend topology-specific fields
  • ➖ Duplicates Kubernetes ownership and status interpretation in TypeScript
  • ➖ Requires richer raw resource data or additional API calls
  • ➖ Risks inconsistent classifications across topology consumers

Recommendation: Server-side role and weight derivation is the best approach because the topology builder already has the required resources and ownership relationships, avoiding new Kubernetes calls and duplicated client logic. The current implementation is appropriate for this PR, although explicit typed edge metadata would be a worthwhile follow-up to replace frontend recognition of traffic edges through display labels.

Files changed (10) +1045 / -68

Enhancement (6) +621 / -65
K8sResourceNode.tsxRender rollout traffic-role badges on resource nodes +15/-0

Render rollout traffic-role badges on resource nodes

• Reads the server-provided traffic role from node data and renders a compact badge. Canary and preview use the testing-side accent, while stable and active use the serving-side accent.

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

TopologyGraph.tsxAnimate complete rollout traffic paths +49/-7

Animate complete rollout traffic paths

• Recognizes rollout traffic Service edges and role-bearing ownership edges, then animates them outside Network Flow mode. Extends edge-style caching and edge construction with rollout traffic state from topology nodes.

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

workload-rollout.tsAdd step labels and traffic weights to rollout details +15/-2

Add step labels and traffic weights to rollout details

• Enhances Argo Rollout progress details with the current canary step description and reported canary traffic percentage. Reuses the renderer's step-label formatter to preserve frontend consistency.

packages/k8s-ui/src/utils/workload-rollout.ts

workload_rollout.goEnrich server-side Argo Rollout step details +172/-8

Enrich server-side Argo Rollout step details

• Adds descriptions for canary step types, analysis and experiment templates, route changes, scaling, and plugins. Includes reported canary traffic weight while preserving replica-only details when step data is unavailable.

pkg/health/workload_rollout.go

builder.goBuild role-aware Argo Rollout topology +202/-48

Build role-aware Argo Rollout topology

• Collects rollout Service names, live revision selectors, and traffic weights while building topology nodes. Fixes split-Service matching, exposes live rollout ReplicaSets despite global collapsing, assigns traffic roles, and labels each traffic-path edge.

pkg/topology/builder.go

rollout_traffic.goCentralize rollout traffic classification and labeling +168/-0

Centralize rollout traffic classification and labeling

• Introduces helpers for classifying canary, stable, active, and preview revisions and generating consistent edge labels. Derives basic-canary weights from the latest reached setWeight step when mesh-reported weights are absent.

pkg/topology/rollout_traffic.go

Bug fix (1) +7 / -1
WorkloadView.tsxPoll expanded resource topology +7/-1

Poll expanded resource topology

• Refreshes the expanded per-resource topology every five seconds so rollout weights, roles, edges, and analysis state remain current.

web/src/components/workload/WorkloadView.tsx

Tests (3) +417 / -2
workload_rollout_vectors.jsonUpdate rollout health detail expectations +2/-2

Update rollout health detail expectations

• Updates golden health vectors to include descriptive pause-step labels in rollout progress details.

pkg/health/testdata/workload_rollout_vectors.json

builder_test.goAdapt owner-edge test to rollout traffic context +1/-0

Adapt owner-edge test to rollout traffic context

• Updates the existing pod owner-edge test invocation for the new rollout traffic information parameter.

pkg/topology/builder_test.go

rollout_traffic_test.goCover rollout traffic topology behavior +414/-0

Cover rollout traffic topology behavior

• Adds canary and blue-green coverage for Service edges, role assignment, weight labels, Pod and ReplicaSet paths, and live ReplicaSet visibility. Verifies basic-canary weight fallback and omission after promotion.

pkg/topology/rollout_traffic_test.go

@qodo-code-review

qodo-code-review Bot commented Sep 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Pod groups mix traffic roles ✓ Resolved 🐞 Bug ≡ Correctness
Description
Pod grouping uses shared application labels, but the large-group path assigns trafficRole and
ownership from only the first pod. Rollouts with more than five stable/canary pods can therefore
show one arbitrary badge and connect the entire group to only one ReplicaSet, misrepresenting the
traffic topology.
Code

pkg/topology/builder.go[R3061-3064]

				firstPod := group.Pods[0]
-				edges = append(edges, b.createPodOwnerEdges(firstPod, podGroupID, opts, replicaSetIDs, replicaSetToDeployment, replicaSetToRollout, jobIDs, jobToCronJob, jobToScaledJob, workflowIDs, workflowToCronWorkflow)...)
+				if role := podRolloutTrafficRole(firstPod, replicaSetToRollout, rolloutTrafficByID); role != "" {
+					podGroupNode.Data["trafficRole"] = role
+				}
Relevance

●●● Strong

Large groups collapse heterogeneous rollout pods while assigning role and ownership from only the
first pod.

PR-#720

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Groups are keyed by app.kubernetes.io/name or app, with no revision hash or traffic role in the
key. Once a group exceeds the default threshold of five, the changed path derives both its new role
and its ownership edge exclusively from group.Pods[0], even though traffic roles are resolved per
pod through its owning ReplicaSet.

pkg/topology/pod_grouping.go[105-135]
pkg/topology/builder.go[3029-3066]
pkg/topology/rollout_traffic.go[149-167]

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

## Issue description
Large pod groups can contain stable and canary pods from different ReplicaSets, but topology classification and ownership use only the first pod. Split Rollout-owned pods by revision/traffic role, or represent every role and owner in the grouped node.

## Issue Context
`GroupPods` groups primarily by application label and the resources view collapses groups larger than five pods. A grouped Rollout must not discard the ownership and traffic roles of all pods except the first.

## Fix Focus Areas
- pkg/topology/builder.go[3029-3066]
- pkg/topology/pod_grouping.go[105-135]
- pkg/topology/rollout_traffic.go[149-167]

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



Remediation recommended

2. Refresh bypasses animation cutoff ✓ Resolved 🐞 Bug ➹ Performance
Description
The visual-refresh path passes the current edge count as buildEdges's nodeCount, so a graph with
at least 200 nodes but fewer than 200 edges re-enables the newly added rollout animations after a
status update. This defeats the graph's explicit large-graph performance safeguard during polling or
probe refreshes.
Code

packages/k8s-ui/src/components/topology/TopologyGraph.tsx[865]

+    setEdges(prev => (prev.length === 0 ? prev : buildEdges(workingEdges, collapsedGroups, groupMapRef.current ?? new Map(), groupingMode, isTrafficView, undefined, prev.length, groupLevels, false, workingNodes)))
Relevance

●●● Strong

Refresh passes edge count instead of node count, deterministically bypassing the 200-node animation
safeguard.

PR-#1146

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
buildEdges compares its nodeCount argument with the 200-node animation threshold. The initial
build passes nodesWithHandlers.length, but the changed refresh call passes prev.length, which is
the number of edges; the PR's rollout edges can therefore become animated outside traffic view after
this rebuild.

packages/k8s-ui/src/components/topology/TopologyGraph.tsx[121-145]
packages/k8s-ui/src/components/topology/TopologyGraph.tsx[211-220]
packages/k8s-ui/src/components/topology/TopologyGraph.tsx[806-817]
packages/k8s-ui/src/components/topology/TopologyGraph.tsx[847-869]

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 visual-only edge rebuild supplies edge count where `buildEdges` expects node count, allowing animations on graphs above the configured node threshold.

## Issue Context
The normal layout path correctly supplies `nodesWithHandlers.length`. The refresh path should use the current working/rendered node count as well, especially now that rollout edges animate outside traffic view.

## Fix Focus Areas
- packages/k8s-ui/src/components/topology/TopologyGraph.tsx[128-145]
- packages/k8s-ui/src/components/topology/TopologyGraph.tsx[806-817]
- packages/k8s-ui/src/components/topology/TopologyGraph.tsx[847-869]

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


3. Comments document prior design ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
Added comments describe how the original implementation read only status.canary.weights and how
edges previously appeared. This embeds change history in code comments instead of documenting only
the current behavior and rationale.
Code

pkg/topology/rollout_traffic_test.go[R318-320]

+// progressing, because the original design only ever read
+// status.canary.weights and had no fallback for the (far more common)
+// case where that field is simply never written.
Relevance

●●● Strong

Comments explicitly describe prior implementation behavior, matching the repository rule prohibiting
change-history documentation.

PR-#724

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 3036538 prohibits explicit diff or change history in code comments. The test
comment says the original design only ever read status.canary.weights, while the implementation
comment also recounts that traffic edges previously showed bare role labels.

Rule 3036538: Disallow references to tickets or PR history in code comments
pkg/topology/rollout_traffic_test.go[313-320]
pkg/topology/rollout_traffic.go[90-100]

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 comments describe the previous implementation and its historical UI behavior, which violates the prohibition on change-history references in code comments.

## Issue Context
Retain any durable Argo Rollouts constraint or invariant, but rewrite the comments to explain only why the fallback is required by current controller behavior.

## Fix Focus Areas
- pkg/topology/rollout_traffic_test.go[313-320]
- pkg/topology/rollout_traffic.go[90-100]

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


4. Experiment names disappear ✓ Resolved 🐞 Bug ≡ Correctness
Description
Go's new canaryStepLabel reads experiment templates through templateNames, which only recognizes
templateName and clusterTemplateName, while Argo experiment templates use name.
Server-generated rollout details consequently render Experiment for … instead of listing template
names and disagree with the TypeScript implementation.
Code

pkg/health/workload_rollout.go[R311-314]

+		if len(names) > 0 {
+			return fmt.Sprintf("Experiment: %s%s", strings.Join(names, ", "), duration)
+		}
+		return fmt.Sprintf("Experiment%s", duration)
Relevance

●●● Strong

Go and TypeScript formatters diverge because Argo experiment templates use name, a concrete
server-rendering correctness bug.

PR-#1411

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The branch's TypeScript formatter maps experiment templates using t.name, whereas the new Go
helper only extracts templateName and clusterTemplateName. Argo's experiment documentation
likewise shows templates with fields such as name: baseline and name: canary.

pkg/health/workload_rollout.go[305-315]
pkg/health/workload_rollout.go[390-411]
packages/k8s-ui/src/components/resources/renderers/RolloutRenderer.tsx[240-252]
🌐 Argo's documented experiment-step templates identify each template with a name field.

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 Go rollout step formatter uses analysis-template field names for experiment templates, dropping valid experiment template names from health details.

## Issue Context
Analysis entries use `templateName` or `clusterTemplateName`, but experiment template entries use `name`. The TypeScript formatter already handles experiment templates with `t.name`.

## Fix Focus Areas
- pkg/health/workload_rollout.go[305-315]
- pkg/health/workload_rollout.go[390-411]
- packages/k8s-ui/src/components/resources/renderers/RolloutRenderer.tsx[240-252]
- pkg/health/testdata/workload_rollout_vectors.json[1-428]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 41 rules
✅ Web pages:
  +11 more
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can route each action level your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread pkg/topology/rollout_traffic_test.go Outdated
Comment thread pkg/topology/builder.go Outdated
Comment thread pkg/health/workload_rollout.go
Comment thread packages/k8s-ui/src/components/topology/TopologyGraph.tsx Outdated
Comment thread pkg/topology/rollout_traffic.go
Comment thread pkg/health/workload_rollout.go
jfillman and others added 3 commits September 4, 2026 00:27
Two real bugs, both confirmed live and cross-checked against the real
Argo Rollouts status schema:

- rolloutTrafficRole checked stableRS/currentPodHash before
  activeSelector/previewSelector. The first pair are generic,
  strategy-agnostic status fields the Rollout controller maintains for
  EVERY Rollout (canary or blueGreen), so a blueGreen Rollout's
  ReplicaSets/Pods had real values there too — every blueGreen
  revision in the graph got badged canary/stable instead of
  active/preview. Reordered to check activeSelector/previewSelector
  first; they live under status.blueGreen, which is only ever
  populated for a blueGreen-strategy Rollout, so a canary Rollout's
  hash can never coincidentally match either.

- The large-group PodGroup path (>5 pods sharing an app label,
  regardless of which ReplicaSet owns them — the normal shape once a
  Rollout mid-transition splits stable/canary across more than 5 pods)
  used only group.Pods[0] for both the trafficRole badge and the owner
  edge, misrepresenting the whole group with one arbitrary pod's role
  and connecting it to only one of the ReplicaSets actually present.
  Now scans every pod: sets trafficRole only when every pod in the
  group agrees (a wrong badge is worse than no badge), and draws an
  owner edge to every distinct ReplicaSet the group's pods actually
  belong to.

Also rewrote a test comment that described the fallback's prior
(unfixed) behavior instead of just why it exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
canaryStepLabel's Go port reused templateNames (built for
templateName/clusterTemplateName AnalysisTemplate refs) for
experiment.templates too — but those are RolloutExperimentTemplate pod
template variants ("baseline"/"canary", etc.), which carry their
identifier under `name`, not templateName/clusterTemplateName. The TS
version this is meant to mirror already reads `name` there; Go emitted
"Experiment for 5m" instead of "Experiment: baseline, canary for 5m",
silently dropping the template names.

Added experimentTemplateNames for the correct shape and a new golden
fixture vector — no existing vector exercised an experiment step, on
either side, which is how this parity gap slipped through the shared
cross-language test in the first place. Verified via real test runs on
both languages against the same fixture, not hand-computed: both now
produce the identical string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…or the animation cutoff

buildEdges gates its large-graph performance safeguard (disabling
edge animations above a node-count threshold) on a nodeCount
parameter. The visual-only-sync refresh path passed prev.length — the
PREVIOUS EDGES array's length, not nodes — while the real layout path
a few lines up correctly passes the node array's length. A tree-shaped
graph commonly has fewer edges than nodes, so a graph at or above the
threshold could read as under it on every status-driven refresh,
re-enabling animations (including the newly added rollout traffic
ones) and defeating the safeguard on exactly the large graphs it
exists to protect. workingNodes — the real node list — is already in
scope at this call site; used its length instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@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 758cd4e. Configure here.

Comment thread pkg/topology/builder.go
…owner onto every pod

A large PodGroup spanning more than one owner (a Rollout's canary +
stable ReplicaSets both over the 5-pod individual-display threshold)
already draws one collapsed edge per distinct owner correctly, but
expanding the group on the frontend connected every one of those
owner edges to every individual pod — a canary pod would render as
owned by the stable ReplicaSet too, and vice versa.

Each pod now carries an ownerKey (from its own OwnerReferences) and,
once the backend resolves each owner to its real edge source(s), an
ownerIds list — so expansion reconnects a pod only to its actual
owner(s), falling back to every source when a pod has none (a plain
single-owner group, the common case, is unaffected).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@nadaverell nadaverell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this, the drawer Topology tab with the canary/stable split is a real improvement, and catching the Service selector vs rollouts-pod-template-hash mismatch was a good find.

Found 4 things that I think should be fixed before we merge this (and also you need to rebase - some conflicts with main, sorry about that...):

  1. Main Topology view orphans Rollout pods. The main view hides ReplicaSet by default (web/src/App.tsx DEFAULT_VISIBLE_KINDS), and the SSE topology is built with IncludeReplicaSets=false. Main emits a Rollout→Pod shortcut edge in that mode; this branch replaces it with RS→Pod only, so with default filters every Rollout-owned pod floats with no edges and the Rollout has no children. Easy to see on make rollouts-demo. Please keep the shortcut: the existing pattern is the CronJob→Pod edge in createPodOwnerEdges, emitted with SkipIfKindVisible: "ReplicaSet" alongside the RS→Pod edge, and carrying the same role label.
  2. Settled Rollouts should be quiet. When stableRS == currentPodHash (or activeSelector == previewSelector for blue-green) nothing is in flight, but every RS and pod still gets a Stable/Active badge and every edge from the Services down to the pods animates permanently, and the RS layer stays expanded where Deployments collapse. Treat that state as settled: keep the edges (the canary/stable Services should still connect to the Rollout by name), but no roles, labels or animation, and the same RS collapse Deployments get. Roles and animation come back only while a transition is in progress. This also stops a blue-green's retained old RS from reading "Stable" during post-promotion.
  3. Weights on basic canaries should be the actual split, not setWeight. With no trafficRouting the controller only approximates the target through replica counts (3 replicas at setWeight 20 is really 25%), and an aborted rollout still walks back to the last setWeight and shows "Stable · 50%" while stable is serving 100% (canary-degraded on the demo cluster). Deriving canary/stable from the live ReplicaSets' replicas (what Argo's CLI calls ActualWeight) fixes both; keep status.canary.weights when it's present.
  4. Shortcut edges in the drawer. Only the main view's kind filter honors skipIfKindVisible; the drawer's neighborhood graph doesn't, so after (1) the Topology tab would draw both Rollout→Pod and Rollout→RS→Pod during a transition (CronJob→Job→Pod has the same quirk today). Please have the graph drop a shortcut edge whenever a node of its skipIfKindVisible kind is present, so both views get the same picture.

I think some tests will need fixing to match the new behavior.

Smaller, but if you wanna do while you're at it:

  • I think we can drop the percentage from the RS->Pod edges (the pod badge already carries it and three stacked "Stable 75%" labels overlap).
  • the health detail's % canary traffic suffix reads only status.canary.weights so it never shows for the basic canaries the topology just handled, either fall back the same way or drop it
  • move canaryStepLabel into utils/workload-rollout.ts to avoid the utils->renderer->utils import cycle
  • preferably trim comments a bit - avoid history like "without this fallback … showed", no "confirmed on the demo cluster") - maybe useful for review, but not really for the next reader after this PR is merged

The last few items I can do as a followup later, if you wanna just take the main items.

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.

2 participants