Skip to content

feat(telemetry): Add operator health dashboard and metrics - #314

Merged
jthakkar04 merged 5 commits into
mainfrom
jthakkar/operator-health-telemetry
Aug 5, 2026
Merged

feat(telemetry): Add operator health dashboard and metrics#314
jthakkar04 merged 5 commits into
mainfrom
jthakkar/operator-health-telemetry

Conversation

@jthakkar04

@jthakkar04 jthakkar04 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Answers "is the operator itself healthy?" through dashboard.

  • Gets two CR-health gauges from the reconciler — wandb_weightsandbiases_ready and wandb_infra_state — on the operator's existing metrics endpoint (no new scrape).
  • Add a W&B Operator Health Grafana dashboard covering reconcile, workqueue, Kubernetes API-client, webhook and process health, plus CR readiness, dependency states, version drift, and liveness/restarts/CPU-throttling.
  • Queries are scoped to the operator's job (namespace-portable), current-state tiles use instant queries, and each section has note.
Screenshot 2026-08-05 at 1 59 27 PM

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a Grafana dashboard for monitoring W&B Operator health, readiness, dependencies, reconciliation, webhooks, resource usage, and runtime performance.
    • Added visibility into W&B instance readiness and infrastructure dependency states through Prometheus metrics.
    • Dashboard is automatically deployed and refreshes every 30 seconds.
  • Tests

    • Added coverage for readiness changes and accurate replacement of outdated infrastructure status metrics.

@jthakkar04
jthakkar04 requested a review from a team as a code owner August 5, 2026 18:58
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added readiness and infrastructure-state Prometheus metrics to reconciliation. Added tests for metric values and stale-state replacement. Added a VictoriaMetrics-backed Grafana dashboard and provisioned it through a GrafanaDashboard resource.

Changes

Operator health telemetry

Layer / File(s) Summary
Readiness and infrastructure metric contracts
internal/metrics/metrics.go, internal/metrics/metrics_test.go
Added and registered readiness and infrastructure-state gauges. Setters record active values and remove stale infrastructure states. Tests verify both behaviors.
Reconciliation metric recording
internal/controller/reconciler/readiness.go, internal/controller/reconciler/reconcile_v2.go
Reconciliation now records W&B readiness and MySQL, Redis, object store, ClickHouse, and Kafka dependency states.
Grafana dashboard provisioning
deploy/telemetry/dashboards/wandb-operator-health.json, deploy/telemetry/templates/telemetry-ui.yaml
Added the operator health dashboard and provisioned it with VictoriaMetrics for Grafana instances labeled dashboards: grafana.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Reconciler
  participant Prometheus
  participant VictoriaMetrics
  participant Grafana
  Reconciler->>Prometheus: Record readiness and dependency gauges
  Prometheus->>VictoriaMetrics: Expose telemetry for storage
  Grafana->>VictoriaMetrics: Query operator health metrics
  VictoriaMetrics-->>Grafana: Return dashboard series
Loading

Possibly related PRs

  • wandb/operator#311: Both changes update reconciliation handling involving readiness and infrastructure state.
  • wandb/operator#312: Both changes integrate Grafana and VictoriaMetrics telemetry resources.

Suggested reviewers: casey-coreweave

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the added operator health dashboard and telemetry metrics.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jthakkar/operator-health-telemetry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🧹 Nitpick comments (1)
internal/metrics/metrics_test.go (1)

35-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reduce the duplicate collector helper.

gather duplicates gatherApplicationInfo at lines 11-25. Only the collector differs. Make the older helper delegate.

♻️ Proposed refactor
 func gatherApplicationInfo(t *testing.T) []*dto.Metric {
 	t.Helper()
-	out := make(chan prometheus.Metric, 64)
-	go func() {
-		ApplicationInfo.Collect(out)
-		close(out)
-	}()
-	var metrics []*dto.Metric
-	for m := range out {
-		dtoMetric := &dto.Metric{}
-		assert.NoError(t, m.Write(dtoMetric))
-		metrics = append(metrics, dtoMetric)
-	}
-	return metrics
+	return gather(t, ApplicationInfo)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/metrics/metrics_test.go` around lines 35 - 49, Update the existing
gatherApplicationInfo helper to delegate to the generic gather function, passing
its application-info collector instead of duplicating collection and metric
conversion logic. Preserve gather’s current behavior and remove the redundant
implementation from gatherApplicationInfo.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deploy/telemetry/dashboards/wandb-operator-health.json`:
- Around line 112-119: Scope all five operator-gauge PromQL targets to the
operator job by adding the existing job=~"(.*/)?wandb-operator" selector:
wandb_weightsandbiases_ready, both wandb_application_info targets, and both
wandb_infra_state targets. Preserve each query’s existing filters and
expressions while ensuring only the W&B operator scrape series are included.
- Line 1615: Update the CPU throttling Prometheus expression in the dashboard to
derive the operator pod selector from the chart values, using the rendered
telemetry namespace and operator fullname/prefix rather than hardcoded
wandb-operator identifiers. Ensure the selector remains scoped to the configured
namespace and matches non-default release names, preserving the existing zero
fallback.

In `@internal/controller/reconciler/reconcile_v2.go`:
- Around line 1590-1607: Add deletion handling for readiness and infrastructure
metric series: in internal/controller/reconciler/reconcile_v2.go, call the new
metrics deletion helper in the finalizer branch before
controllerutil.RemoveFinalizer, and clear each component’s existing series at
the start of every status-map loop before recording current entries. In
internal/metrics/metrics.go, add DeleteWeightsAndBiasesMetrics(namespace, name
string) to delete the readiness pair and infrastructure metrics, plus a
component-scoped helper that deletes matching series by namespace, name, and
component.
- Line 1606: Guard the SetInfraState call for the Kafka component in the
reconciliation flow so it runs only when wandb.Spec.Kafka.ManagedKafka is
configured. Leave Kafka status inference unchanged, and avoid recording metrics
with an empty instance name or state for resources without managed Kafka.

---

Nitpick comments:
In `@internal/metrics/metrics_test.go`:
- Around line 35-49: Update the existing gatherApplicationInfo helper to
delegate to the generic gather function, passing its application-info collector
instead of duplicating collection and metric conversion logic. Preserve gather’s
current behavior and remove the redundant implementation from
gatherApplicationInfo.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 05547bea-8f68-491a-bb56-564c72a75359

📥 Commits

Reviewing files that changed from the base of the PR and between db6e8d9 and 0dafbe5.

📒 Files selected for processing (6)
  • deploy/telemetry/dashboards/wandb-operator-health.json
  • deploy/telemetry/templates/telemetry-ui.yaml
  • internal/controller/reconciler/readiness.go
  • internal/controller/reconciler/reconcile_v2.go
  • internal/metrics/metrics.go
  • internal/metrics/metrics_test.go

Comment on lines +112 to +119
"targets": [
{
"expr": "wandb_weightsandbiases_ready",
"legendFormat": "{{namespace}}/{{name}}",
"refId": "A",
"instant": true
}
],

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the operator-gauge queries to the operator job.

Every controller-runtime, workqueue, rest_client, webhook, and process target in this dashboard carries job=~"(.*/)?wandb-operator". The five targets that read the operator's own gauges carry no selector:

  • Line 114 wandb_weightsandbiases_ready
  • Line 168 wandb_infra_state{state!="Healthy"}
  • Line 223 wandb_application_info
  • Line 246 wandb_infra_state == 1
  • Line 315 wandb_application_info

Any other scrape target that exposes these metric names is included. "Distinct image versions" is the clearest failure: count(count by (tag) (wandb_application_info)) counts tags across all matching series, so a second W&B install in the same VictoriaMetrics instance reports false version drift and turns the tile red.

🐛 Proposed fix for the CR Ready target
       "targets": [
         {
-          "expr": "wandb_weightsandbiases_ready",
+          "expr": "wandb_weightsandbiases_ready{job=~\"(.*/)?wandb-operator\"}",
           "legendFormat": "{{namespace}}/{{name}}",
           "refId": "A",
           "instant": true
         }
       ],

Apply the same selector to the four remaining targets.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"targets": [
{
"expr": "wandb_weightsandbiases_ready",
"legendFormat": "{{namespace}}/{{name}}",
"refId": "A",
"instant": true
}
],
"targets": [
{
"expr": "wandb_weightsandbiases_ready{job=~\"(.*/)?wandb-operator\"}",
"legendFormat": "{{namespace}}/{{name}}",
"refId": "A",
"instant": true
}
],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/telemetry/dashboards/wandb-operator-health.json` around lines 112 -
119, Scope all five operator-gauge PromQL targets to the operator job by adding
the existing job=~"(.*/)?wandb-operator" selector: wandb_weightsandbiases_ready,
both wandb_application_info targets, and both wandb_infra_state targets.
Preserve each query’s existing filters and expressions while ensuring only the
W&B operator scrape series are included.

},
"targets": [
{
"expr": "sum(rate(container_cpu_cfs_throttled_periods_total{container=\"operator\", pod=~\"wandb-operator-[^-]+-[^-]+\"}[$__rate_interval])) / sum(rate(container_cpu_cfs_periods_total{container=\"operator\", pod=~\"wandb-operator-[^-]+-[^-]+\"}[$__rate_interval])) or vector(0)",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the operator Deployment name, container name, and namespace helper in the chart.
fd -e yaml -e tpl | xargs rg -nP -C6 'kind:\s*Deployment' -g '*operator*'
fd -e yaml -e tpl | xargs rg -nP -C3 'name:\s*operator\b|containers:'
rg -nP -C3 'define "telemetry.namespace"' -g '*.tpl'

Repository: wandb/operator

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -u

echo "== file context around dashboard selector =="
sed -n '1560,1640p' deploy/telemetry/dashboards/wandb-operator-health.json

echo
echo "== locate wandb-operator pod/controller deployment in deploy/operator and relevant telemetry helpers =="
rg -n -C 5 'kind:\s*Deployment|app.kubernetes.io/name:\s*wandb-operator|app.kubernetes.io/name:\s*operator|control-plane:\s*controller-manager|define "telemetry\.namespace"|telemetry\.namespace|fullname|name:{{|name:' deploy/operator templates deploy/telemetry -g '*.yaml' -g '*.tpl' -g 'Chart.yaml' | sed -n '1,260p'

echo
echo "== operator deployment templates =="
fd -e yaml -e tpl -d 3 . deploy/operator | rg 'deployment|manager|values|Chart\.yaml' | while read -r f; do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

echo
echo "== telemetry namespace/name helpers =="
rg -n -C 4 'define "telemetry\.namespace"|telemetry\.namespace|define "telemetry\.fullname"|telemetry\.fullname|define "telemetry\.name"|telemetry\.name|include ".*fullname|fullname' deploy/telemetry -g '*.tpl'

Repository: wandb/operator

Length of output: 10348


🏁 Script executed:

#!/bin/bash
set -u

echo "== operator deployment templates =="
for f in $(fd -e yaml -e tpl '.*' deploy/operator | rg 'deployment|manager|values|Chart\.yaml|_helpers|namespace|common' | head -50); do
  echo "--- $f"
  sed -n '1,240p' "$f"
done

echo
echo "== relevant telemetry helpers =="
for f in $(fd -e tpl '.*' deploy/telemetry | head -80); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

Repository: wandb/operator

Length of output: 12900


🏁 Script executed:

#!/bin/bash
set -u

echo "== wandb operator fullname helper files =="
rg -n -C 8 'define "wandb-base\.fullname"|define "wandb-operator\.fullname"|_helpers\.tpl|fullname' deploy -g '*.tpl' -g '*.yaml' -g 'Chart.yaml' | sed -n '1,260p'

echo
echo "== controller deployment in wandb-operator dependency manifests =="
rg -n -C 8 '^kind:\s*Deployment$|app\.kubernetes\.io/name:\s*wandb-operator|control-plane:\s*controller-manager|name:\s*operator|namespace:\s*wandb' helm dependency . -g '*.yaml' -g '*.tpl' | sed -n '1,260p' || true

echo
echo "== direct template references to deployment name/pod labels =="
fd -e tpl -e yaml . deploy/operator | xargs rg -n -C 6 'metadata:|name:|labels:|selector:|app\.kubernetes\.io/name|wandb-operator' | sed -n '1,300p'

Repository: wandb/operator

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -u

echo "== Wandb base helpers if present =="
fd -e tpl '.*' . | rg 'wandb-base|base|_helpers' | while read -r f; do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

echo
echo "== all wandb-operator.fullname references =="
rg -n 'wandb-operator\.fullname|wandb-base\.fullname|fullName|full-name' deploy -g '*.tpl' -g '*.yaml' | sed -n '1,220p'

echo
echo "== operator container names in templates =="
rg -n -C 4 '(^|[\s-])name:\s*operator\b|name:\s*manager\b|containerName:\s*operator' deploy/operator deploy/telemetry -g '*.tpl' -g '*.yaml' | sed -n '1,200p'

Repository: wandb/operator

Length of output: 8677


Harden the CPU throttling selector.

Derive this selector from the chart values instead of hardcoding wandb-operator. Use the rendered telemetry namespace and the operator fullname/prefix so non-default releases and telemetry.namespace values do not fall back to 0% or include operator pods from other namespaces.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/telemetry/dashboards/wandb-operator-health.json` at line 1615, Update
the CPU throttling Prometheus expression in the dashboard to derive the operator
pod selector from the chart values, using the rendered telemetry namespace and
operator fullname/prefix rather than hardcoded wandb-operator identifiers.
Ensure the selector remains scoped to the configured namespace and matches
non-default release names, preserving the existing zero fallback.

Comment thread internal/controller/reconciler/reconcile_v2.go
Comment thread internal/controller/reconciler/reconcile_v2.go Outdated
jthakkar04 and others added 5 commits August 5, 2026 14:22
Expose per-CR readiness and per-dependency state as gauges on the operator
metrics endpoint, mirroring the existing wandb_application_info pattern.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set wandb_weightsandbiases_ready wherever readiness is decided, and publish
each dependency's state after infra status inference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A dedicated "is the operator itself healthy?" board with a description header and
per-section notes explaining each panel in plain language. Covers reconcile
health (errors, results, latency, workers, terminal errors, panics), workqueue
backlog, Kubernetes API-client calls, admission/conversion webhooks, and Go
process health, plus CR readiness, unhealthy-dependency count, per-dependency
state, and image-version drift from the operator's wandb_* gauges.

Queries match job=~"(.*/)?wandb-operator" so they follow the operator to any
namespace and other controller-runtime operators don't bleed in. Current-state
tiles use instant queries so transient startup states don't linger. Panels the
operator's client-go build doesn't emit (rest-client and webhook latency
histograms) are left out rather than shown empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the operator-health board's Process & runtime section with up/liveness,
uptime, last-restarted and restarts-in-range (from the operator's own process
metrics) and container CPU-throttling (cAdvisor, scoped to the operator pod by
name so it stays namespace-portable). Drops the "who it's for" line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jthakkar04
jthakkar04 force-pushed the jthakkar/operator-health-telemetry branch from 0dafbe5 to ce50fe2 Compare August 5, 2026 19:22
@jthakkar04
jthakkar04 merged commit eb3abfe into main Aug 5, 2026
10 of 11 checks passed
@jthakkar04
jthakkar04 deleted the jthakkar/operator-health-telemetry branch August 5, 2026 19:40
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