Skip to content

fix(grafana): pin a stable uid on every provisioned dashboard - #195

Merged
vovinacci merged 2 commits into
mainfrom
fix/grafana-dashboard-uids
Jul 27, 2026
Merged

fix(grafana): pin a stable uid on every provisioned dashboard#195
vovinacci merged 2 commits into
mainfrom
fix/grafana-dashboard-uids

Conversation

@vovinacci

@vovinacci vovinacci commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Grafana's file provider identifies a dashboard by uid, and none of the seven dashboard files carried one, so each got a random uid at provision time. That left every dashboard without a durable identity, which shows up the moment anyone edits one in the UI -- something this stack allows on purpose (allowUiUpdates: true, so students can build a panel by clicking rather than by hand-writing JSON).

Saving from the UI then detached the dashboard from its file: on the next restart the edited copy was orphaned into the General folder with its provisioning link severed, and the provider built a second dashboard with the same title beside it. Measured against a clean stack: eight dashboards, two of them named "DevOps Demo", and which one a reader opens is a coin flip.

With the uid pinned the provider reclaims the same dashboard instead of creating a sibling: seven dashboards, no duplicate, and the file's content wins on restart. Losing an uncommitted UI edit at restart is the correct outcome -- the file is the source of truth -- and it is the behaviour the accompanying exercise will teach.

The uids read as URL paths (/d/devops-demo/, /d/analytics-history/) rather than generated hashes, so dashboard links are now stable across a rebuild and can be cited from docs and runbooks.

scripts/check-dashboard-uids.sh keeps this from regressing, in the same shape as the toolchain drift gate: it rejects a missing uid, a uid shared by two files, one longer than Grafana's 40-character limit, and unparseable JSON. Without a gate the next dashboard lands without a uid and the duplication returns silently, which is how this arrived in the first place.

Datasources needed no equivalent change: every dashboard resolves them through a datasource-type template variable queried by type, not by a hardcoded uid.

Summary by CodeRabbit

  • New Features

    • Added stable identifiers to Grafana dashboards for consistent linking and referencing.
    • Added automated validation to ensure dashboard identifiers are present, unique and within the supported length.
  • Chores

    • Added a pre-commit check to validate dashboard identifiers before changes are committed.

Grafana's file provider identifies a dashboard by uid, and none of the seven
dashboard files carried one, so each got a random uid at provision time. That
left every dashboard without a durable identity, which shows up the moment
anyone edits one in the UI -- something this stack allows on purpose
(allowUiUpdates: true, so students can build a panel by clicking rather than by
hand-writing JSON).

Saving from the UI then detached the dashboard from its file: on the next
restart the edited copy was orphaned into the General folder with its
provisioning link severed, and the provider built a second dashboard with the
same title beside it. Measured against a clean stack: eight dashboards, two of
them named "DevOps Demo", and which one a reader opens is a coin flip.

With the uid pinned the provider reclaims the same dashboard instead of
creating a sibling: seven dashboards, no duplicate, and the file's content wins
on restart. Losing an uncommitted UI edit at restart is the correct outcome --
the file is the source of truth -- and it is the behaviour the accompanying
exercise will teach.

The uids read as URL paths (/d/devops-demo/, /d/analytics-history/) rather than
generated hashes, so dashboard links are now stable across a rebuild and can be
cited from docs and runbooks.

scripts/check-dashboard-uids.sh keeps this from regressing, in the same shape as
the toolchain drift gate: it rejects a missing uid, a uid shared by two files,
one longer than Grafana's 40-character limit, and unparseable JSON. Without a
gate the next dashboard lands without a uid and the duplication returns
silently, which is how this arrived in the first place.

Datasources needed no equivalent change: every dashboard resolves them through
a datasource-type template variable queried by type, not by a hardcoded uid.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vovinacci, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 34b95af4-04db-412c-898a-8eeae3a9324a

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb13d1 and 76c7216.

📒 Files selected for processing (1)
  • scripts/check-dashboard-uids.sh

Walkthrough

The change assigns stable UIDs to seven Grafana dashboards, adds a validator for presence, length, parsing, and uniqueness, and runs that validator through a new local pre-commit hook.

Changes

Dashboard UID validation

Layer / File(s) Summary
UID contract and validator
observability/grafana/dashboards/*.json, scripts/check-dashboard-uids.sh
Dashboard JSON files define stable UIDs, and the validator checks UID presence, length, uniqueness, file reads, and JSON parsing.
Pre-commit integration
.pre-commit-config.yaml
A dashboard-uids hook runs the validator for Grafana dashboard JSON files without passing filenames.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit hops through dashboards bright,
Giving each a UID just right.
The checker sniffs duplicates away,
While pre-commit guards the day.
“Stable IDs!” the bunny cheers,
And nibbles validation errors.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: pinning stable UIDs on provisioned Grafana dashboards.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/grafana-dashboard-uids

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

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@observability/grafana/dashboards/analytics.json`:
- Line 2: Add one ADR or RFC amendment linked in the PR documenting the stable
dashboard UID and provisioning ownership contract for analytics-ingest,
devops-demo, analytics-history, load-k6, monitoring-layers, reports-ui, and
reports-jvm; no direct changes are required in the seven dashboard files beyond
documenting their existing UID assignments.

In `@scripts/check-dashboard-uids.sh`:
- Around line 35-45: Update the dashboard validation loop around dashboard.get
and by_uid in scripts/check-dashboard-uids.sh to first validate that each
dashboard is an object and that any present uid is a string. Append descriptive
failures for invalid dashboard shapes or UID types, and only perform truthiness,
length, and uniqueness checks after those validations pass.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 325187b8-4bc1-4e5d-8760-378787cfb378

📥 Commits

Reviewing files that changed from the base of the PR and between b5eab66 and 7bb13d1.

📒 Files selected for processing (9)
  • .pre-commit-config.yaml
  • observability/grafana/dashboards/analytics.json
  • observability/grafana/dashboards/devops-demo-dashboard.json
  • observability/grafana/dashboards/history.json
  • observability/grafana/dashboards/load.json
  • observability/grafana/dashboards/monitoring-layers.json
  • observability/grafana/dashboards/reports-ui.json
  • observability/grafana/dashboards/reports.json
  • scripts/check-dashboard-uids.sh

Comment thread observability/grafana/dashboards/analytics.json
Comment thread scripts/check-dashboard-uids.sh
The gate assumed every dashboard file parses into an object with a string uid.
Neither is guaranteed by JSON, so a file whose top level is an array raised
AttributeError on dashboard.get, and a numeric uid raised TypeError on len().

The exit code was already correct in both cases -- the gate failed closed, so
nothing malformed could pass -- but it failed with a Python traceback instead
of naming the file and the problem. For a check whose only job is to explain
what is wrong with a file, that is the failure mode that matters.

Each shape now gets its own message: a non-object root reports the type it
found, a non-string uid reports the type it found, and a null or empty uid
falls in with the missing-uid case since the remedy is identical. Verified
against every shape: array root, numeric uid, null uid, empty uid, missing uid,
over-long uid, unparseable JSON, and two files sharing a uid all exit 1 with a
named file, and the seven real dashboards still exit 0.
@vovinacci
vovinacci merged commit 37257ce into main Jul 27, 2026
5 checks passed
@vovinacci
vovinacci deleted the fix/grafana-dashboard-uids branch July 27, 2026 14:03
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