Compare commits

...

173 Commits

Author SHA1 Message Date
Isabel Matwawana 2a1f96f3f2 Fixed merge conflict 2025-12-12 23:02:33 -05:00
Isabel Matwawana dd65715082 Full edit 2025-12-12 22:28:35 -05:00
grafana-pr-automation[bot] c5345498b1 I18n: Download translations from Crowdin (#115291)
New Crowdin translations by GitHub Action

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-13 00:42:48 +00:00
Isabel Matwawana 1bcccd5e61 Docs: Update export as JSON task (#115288) 2025-12-12 22:26:28 +00:00
Oscar Kilhed 12b38d1b7a Dashboards: Never allow rows with hidden header to be collapsed (#115284)
Never allow rows with hidden header to be collapsed
2025-12-12 22:14:48 +00:00
Paul Marbach 359d097154 Table: Remove hardcoded assumption of __nestedFrames field name (#115117)
* Table: Remove hardcoded assumption of __nestedFrames field name

* E2E for nested tables

* Apply suggestion from @fastfrwrd
2025-12-12 21:57:47 +00:00
Haris Rozajac cfc5d96c34 Dashboard Schema V2: Fix panel query tab (#115276)
fix panel query tab for v2 schema
2025-12-12 14:39:43 -07:00
Larissa Wandzura 3459c67bfb DOCS: Overhaul Azure Monitor data source docs (#115121)
* continued edits

* authentication updates

* added more info to configure doc

* started work on query editor

* reviewed the configure doc, consolidated sections

* fixed issue with headings

* fixed errors

* updates to the template variables doc

* created initial troubleshooting doc

* removed gerunds and fixed heading issues

* new annotations doc added

* more updates to query editor

* fixed spelling

* fixed some linter issues

* fixed flow for the intro doc

* updates to the intro doc

* fixed transformation links

* added review date to front matter

* ran prettier

* added a new alerting doc

* linter updates

* some final edits

* ran prettier again

* Update docs/sources/datasources/azure-monitor/configure/index.md

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>

* Update docs/sources/datasources/azure-monitor/configure/index.md

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>

* Update docs/sources/datasources/azure-monitor/troubleshooting/index.md

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>

* edits based on feedback

* removed all relative reference links

* ran prettier

---------

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
2025-12-12 21:10:03 +00:00
Larissa Wandzura 37ccd8bc3d Docs: Added troubleshooting guide for the InfluxDB data source (#115191)
* Docs: Add troubleshooting guide for InfluxDB data source

* linter fixes, updates based on feedback
2025-12-12 20:21:50 +00:00
colin-stuart 5156177079 SCIM: show error if SCIM-provisioned user attempts login with non-SAML auth module (#115271) 2025-12-12 13:51:37 -06:00
Paul Marbach 4817ecf6a3 Sparkline: Guess decimals rather than going with 0 (#115246)
* Sparkline: Guess decimals rather than going with 0

* Update packages/grafana-ui/src/components/Sparkline/utils.test.ts
2025-12-12 13:59:54 -05:00
Renato Costa c73cab8eef chore: add cleanup task for duplicated provisioned dashboards (#115103)
* chore: add cleanup task for duplicated provisioned dashboards
2025-12-12 13:56:47 -05:00
Adela Almasan a37ebf609e VizSuggestions: Fix unique key warning (#115112) 2025-12-12 12:25:03 -06:00
Kristina Demeshchik b29e8ccb45 Dashboards: Generate default tab title when converting rows with empty titles to tabs (#115256)
Generate default title for empty row titles
2025-12-12 13:14:56 -05:00
Matias Chomicki 644f7b7001 Infinite scroll: Fix interaction with client-side filter (#115243)
Infinite scroll: fix interaction with client-side filter
2025-12-12 18:59:49 +01:00
Alexander Zobnin 629570926d Zanzana: Fix resource translation for dashboards (#115077) 2025-12-12 11:05:10 -06:00
Will Assis 1b59c82b74 Revert "Unified-storage: sql backend key path backfill (#115033)" (#115257)
This reverts commit b2dd095bd8.
2025-12-12 17:00:08 +00:00
Matias Chomicki f35447435f LogLineContext: remove broken permalink prop (#115252) 2025-12-12 16:24:36 +00:00
Paul Marbach c0dc92e8cd Gauge: Fit-and-finish tweaks to glows, text position, and sparkline size (#115173)
* Gauge: Fit-and-finish tweaks to glows, text position, and sparkline size

* adjust text height and positions a little more

* cohesive no data handling

* more tweaks

* fix migration test

* Fix JSON formatting by adding missing newline

* remove new line
2025-12-12 11:10:56 -05:00
Matias Chomicki 7114b9cd3b Log Line Details: Fix width calculation in dashboards (#115248)
* FieldSelector: rename functions to be more explicit

* LogDetailsContext: calculate width based on field selector visibility

* LogLineDetails: Fix sidebar max width calculation

* Update functions usage

* Add regression and fix context calculation
2025-12-12 16:56:23 +01:00
Kristina Demeshchik b40d0e6ff4 Dashboards: Fix accessible color palettes not being saved in v2 schema (#115244)
* Fix palette color v2 conversion

* v2->v1 conversion
2025-12-12 10:36:31 -05:00
Yunwen Zheng 584615cf3f RecentlyViewedDashboards: Set up container on browsing dashboards page (#115164)
* RecentlyViewedDashboards: Set up container on browsing dashboards page
2025-12-12 10:32:05 -05:00
William Wernert 5f80a29a28 Alerting: Prevent users from saving rules to git-synced folders (#114944)
---------

Co-authored-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
2025-12-12 15:25:08 +00:00
Bogdan Matei eab5d2b30e Dashboard: Fix rogue modal when exiting edit mode (#115240)
* Dashboard: Fix rogue modal when exiting edit mode

* Remove unnecessary change
2025-12-12 17:17:34 +02:00
Anna Urbiztondo f3421b9718 Docs: Git Sync scenarios (#115199)
* WIP

* Review

* Move scenarions

* Structure fix

* Edits, fix

* Vale, x-refs

* Feedback, tweaks

* Consolidate HA, titles

* Prettier

* Prettier

* Adding missing content

* Minor edits

* Links

* Prettier
2025-12-12 16:08:28 +01:00
Alex Khomenko 1addfd69b4 Provisioning: Fix duplicated breadcrumb (#115234)
* Provisioning: Fix duplicated breadcrumb

* translations
2025-12-12 15:00:40 +00:00
Gonzalo Trigueros Manzanas d4a627c5fc Provisioning: Add resource-level warning support. (#115023) 2025-12-12 15:59:45 +01:00
Johnny Kartheiser 46ef9aaa0a Alerting docs: Links fix (#115044)
* alerting docs: links fix

fixes 404 errors

* Alerting docs: Fix Slack integration links

Fixes Slack links and clarifies the first two steps.

* prettier
2025-12-12 08:58:10 -06:00
Serge Zaitsev 6ce672dd00 Chore: Fix mysql query for annotation migration (#115222)
fix mysql query for annotation migration
2025-12-12 15:37:43 +01:00
Matheus Macabu 403f4d41de APIServer: Add wiring for audit backend and policy rule evaluator (#115212) 2025-12-12 15:17:44 +01:00
Juan Cabanas 6512259acc DashboardLibrary: Restore New dashboard naming (#115184) 2025-12-12 10:10:05 -03:00
Will Assis b2dd095bd8 Unified-storage: sql backend key path backfill (#115033)
* unified-storage: add migration to backfill key_path in resource_history
2025-12-12 08:09:51 -05:00
Charandas e525b529a8 fix: Add panic for nil authorizer in installer (#115186) 2025-12-12 05:01:03 -08:00
Paul Marbach 7805e18368 Sparkline: Export a class component for now (#115189) 2025-12-12 07:56:31 -05:00
Levente Balogh 7a07a49ecc Dashboard: Update toolbar layout (option 2.) (#115210)
fix: dashboard toolbar layout updates
2025-12-12 12:22:04 +00:00
beejeebus 9a4e13800d Guard config CRUD metrics so it's safe for grafana-enterprise
Previous attempt to land this required this PR and a grafana-enterprise
PR to land at the ~same time.

This PR guards the use of `dsConfigHandlerRequestsDuration` with a nil
check, and doesn't change any existing APIs, so we can land it without
any timing issues with grafana-enterprise.

Once this has landed, we'll make a follow-up PR for grafana-enterprise.
2025-12-12 07:21:29 -05:00
Nathan Marrs a0c4e8b4f4 Suggested Dashboards: Add missing loaded event tracking for v1 of feature (#115195)
## Summary

Fixes a regression where the `loaded` analytics event was not being tracked for the `BasicProvisionedDashboardsEmptyPage` component, which is the component shown in production when the `suggestedDashboards` feature toggle is disabled (i.e. community dashboards disabled but v1 of feature enabled)

## Problem

Regression introduced by https://github.com/grafana/grafana/pull/112808/changes#diff-3a19d2e887a3344cb0bcd2449b570bd50a7d78d1d473f4a3cf623f9fe40f35fc adding community dashboard support to `SuggestedDashboards`, the `BasicProvisionedDashboardsEmptyPage` component was missing the `loaded` event tracking. Component is mounted here: https://github.com/grafana/grafana/pull/112808/changes#diff-fba79ed6f8bfb5f712bdd529155158977a3e081d1d6a5932a5fa90fb57a243e6R82. This caused analytics discrepancies where in the past 7 days (note: issue has been present for last several weeks but here is sample of data from previous week):

- 106 provisioned dashboard items were clicked
- Only 1 `loaded` event was received (from `SuggestedDashboards` when the feature toggle is enabled)
- The `loaded` events are missing for the production v1 flow (when `suggestedDashboards` feature toggle is off)

## Root Cause

The `BasicProvisionedDashboardsEmptyPage` component (used in v1 flow in production) was never updated with the `loaded` event tracking that was added to `SuggestedDashboards` in PR #113417. Since the `suggestedDashboards` feature toggle is not enabled in production, users were seeing `BasicProvisionedDashboardsEmptyPage` which had no tracking, resulting in missing analytics events.

## Solution

Added the `loaded` event tracking to `BasicProvisionedDashboardsEmptyPage` using the same approach that was previously used (tracking inside the async callback when dashboards are loaded). This ensures consistency with the existing pattern and restores analytics tracking for the production flow.

## Changes

- Added `DashboardLibraryInteractions.loaded()` call in `BasicProvisionedDashboardsEmptyPage` when dashboards are successfully loaded
- Uses the same tracking pattern as the original implementation (tracking inside async callback)
- Matches the event structure used in `SuggestedDashboards` for consistency

## Testing

- Verified that `loaded` events are now tracked when `BasicProvisionedDashboardsEmptyPage` loads dashboards
- Confirmed the event includes correct `contentKinds`, `datasourceTypes`, and `eventLocation` values
- No duplicate events are sent (tracking only occurs once per load)

## Related

- Original analytics implementation: #113417
- Related PR: #112808
- Component: [`BasicProvisionedDashboardsEmptyPage.tsx`](https://github.com/grafana/grafana/blob/main/public/app/features/dashboard/dashgrid/DashboardLibrary/BasicProvisionedDashboardsEmptyPage.tsx)
2025-12-12 09:16:55 -03:00
Victor Marin fa62113b41 Dashboards: Fix custom variable legacy model to return options when flag is set (#115154)
* fix custom var legacy model options property

* add test
2025-12-12 12:12:46 +00:00
Roberto Jiménez Sánchez b863acab05 Provisioning: Fix race condition causing unhealthy repository message to be lost (#115150)
* Fix race condition causing unhealthy repository message to be lost

This commit fixes a race condition in the provisioning repository controller
where the "Repository is unhealthy" message in the sync status could be lost
due to status updates being based on stale repository objects.

## Problem

The issue occurred in the `process` function when:
1. Repository object was fetched from cache with old status
2. `RefreshHealth` immediately patched the health status to "unhealthy"
3. `determineSyncStatusOps` used the stale object to check if unhealthy
   message was already set
4. A second patch operation based on stale data would overwrite the
   health status update

## Solution

Introduced `RefreshHealthWithPatchOps` method that returns patch operations
instead of immediately applying them. This allows batching all status updates
(health + sync) into a single atomic patch operation, eliminating the race
condition.

## Changes

- Added `HealthCheckerInterface` for better testability
- Added `RefreshHealthWithPatchOps` method to return patch ops without applying
- Updated `process` function to batch health and sync status updates
- Added comprehensive unit tests for the fix

Fixes the issue where unhealthy repositories don't show the "Repository is
unhealthy" message in their sync status.

* Fix staticcheck lint error: remove unnecessary nil check for slice
2025-12-12 13:24:58 +02:00
Ezequiel Victorero c7c052480d Chore: Bump grafana/llm 1.0.1 (#115175) 2025-12-12 11:22:37 +00:00
Gabriel MABILLE 478ae15f0e grafana-iam: Use parent folder to authorize ResourcePermissions (#115008)
* `grafana-iam`: Fetch target parent folder

* WIP add different ParentProviders

* Add version

* Move code to a different file

* Instantiate resourceParentProvider

* same import name

* imports

* Add tests

* Remove unecessary test

* forgot wire

* WIP integration tests

* Add test to cover list

* Fix caching problem in integration tests

* comments

* Logger and comments

* Add lazy creation and caching

* Instantiate clients only once

* Rerun wire gen
2025-12-12 11:43:12 +01:00
Erik Sundell 8ebb1c2bc9 NPM: Remove dist-tag code (#115209)
remove dist-tag
2025-12-12 11:41:57 +01:00
Marc M. 5572ce966a DynamicDashboards: Hide variables from outline in view mode (#115142) 2025-12-12 10:34:47 +00:00
Marc M. e3510f6eb3 DynamicDashboards: Replace discard changes modal (#114789) 2025-12-12 11:24:53 +01:00
Mihai Doarna a832e5f222 IAM: Add missing params to team search request (#115208)
add missing params to team search request
2025-12-12 12:13:43 +02:00
Levente Balogh c5a5482d7d Doc: Add docs for displaying links in the dashboard-controls menu (#115201)
* docs: add docs for displaying links in the dashboard-controls menu

* Update docs/sources/as-code/observability-as-code/schema-v2/links-schema.md

Co-authored-by: Anna Urbiztondo <anna.urbiztondo@grafana.com>

---------

Co-authored-by: Anna Urbiztondo <anna.urbiztondo@grafana.com>
2025-12-12 09:57:35 +00:00
Gareth 169ffc15c6 OpenTSDB: Run suggest queries through the data source backend (#114990)
* OpenTSDB: Run suggest queries through the data source backend

* use mux
2025-12-12 18:36:52 +09:00
Levente Balogh 296fe610ba Docs: Add docs for displaying variables in the dashboard-controls (#115205)
docs: update docs for adding a template variable
2025-12-12 10:34:13 +01:00
grafana-pr-automation[bot] eceff8d26e I18n: Download translations from Crowdin (#115193)
New Crowdin translations by GitHub Action

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-12 09:30:01 +00:00
Lauren 3cdfe34ec8 Alerting: Fix Alerts page filtering (#115178)
* Alerting: fix filtering on alerts page

* exclude __name__ from label filter dropdown list
2025-12-12 08:16:55 +00:00
Erik Sundell 35c214249f E2E Selectors: Fix comment typo (#115197)
fix typo
2025-12-12 08:59:10 +01:00
Erik Sundell c3224411c0 NPM: Use env var for OIDC token auth instead of direct npmrc (#115153)
* use env var

* ignore spellcheck
2025-12-12 07:45:04 +01:00
Steve Simpson b407f0062d Alerting: Add an authorizer to the historian app (#115188)
historian: add an authorizer

Co-authored-by: Charandas Batra <charandas.batra@grafana.com>
2025-12-11 23:34:37 +00:00
Haris Rozajac 0385a7a4a4 Dashboard Import: disable importing V2 dashboards when dashboardNewLayouts is disabled (#114188)
* Disable importing v2 dashboards when dynamic dashboards are disabled

* clean up

* Update error messaging
2025-12-11 15:54:06 -07:00
Jack Baldry 1611489b84 Fix path to generation and source content (#115095)
Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
2025-12-11 21:40:35 +00:00
Eric Hilse e8039d1c3d fix(topbar): remove minWidth property for better layout handling (#115166) 2025-12-11 13:28:30 -07:00
Andres Torres 652b4f2fab fix(setting): Add default scheme to handle k8s api errors (#115177) 2025-12-11 20:12:25 +00:00
Ezequiel Victorero c35642b04d Chore: Bump nodemailer with forced resolution (#115172) 2025-12-11 16:40:23 -03:00
Larissa Wandzura 91a72f2572 DOCS: Updates to Elasticsearch data source docs (#115021)
* created new configure folder, rewrote intro page

* updated configure doc

* updated query editor

* updates to template variables

* added troubleshooting doc, fixed heading issues

* fix linter issues

* added alerting doc

* corrected title

* final edits

* fixed linter issue

* added deprecation comment per feedback

* ran prettier
2025-12-11 19:21:33 +00:00
Bogdan Matei f8027e4d75 Dashboard: Implement modal to confirm layout change (#111093) 2025-12-11 19:17:23 +00:00
Paul Marbach f5b2dde4a1 Suggestions: Add keyboard support (#114517)
* Suggestions: hashes on suggestions, update logic to select first suggestion

* fix types

* Suggestions: New UI style updates

* update some styles

* getting styles just right

* remove grouping when not on flag

* adjust minimum width for sidebar

* CI cleanups

* updates from ad hoc review

* add loading and error states to suggestions

* remove unused import

* update header ui for panel editor

* restore back button to vizpicker

* fix e2e test

* fix e2e

* add i18n update

* use new util for setVisualization operation

* Apply suggestions from code review

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>

* comments from review

* updates from review

* Suggestions: Add keyboard support

* fix selector for PluginVisualization.item

---------

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2025-12-11 14:13:33 -05:00
Misi 0c264b7a5f IAM: Add user search endpoint (#114542)
* wip: initial changes, api registration

* wip

* LegacySearch working with sorting

* Revert mapper change for now

* Clean up

* Cleanup, add integration tests

* Improve tests

* OpenAPI def regen

* Use wildcard search, fix lastSeenAt handling, add lastSeenAtAge

* Add missing files

* Fix merge

* Fixes

* Add tests, regen openapi def

* Address feedback

* Address feedback batch 2

* Chores

* regen openapidef

* Address feedback

* Add tests for paging

* gen apis

* Revert go.mod, go.sum. go.work.sum

* Fix + remove extra tracer parameter
2025-12-11 19:54:48 +01:00
Ashley Harrison d83b216a32 FS: Fix rendering of public dashboards in MT frontend service (#115162)
* pass publicDashboardAccessToken to ST backend via bootdata

* slightly cleaner

* slightly tidy up go templating

* add HandleView middleware
2025-12-11 17:56:40 +00:00
Anna Urbiztondo ada14df9fd Add new glossary word (#115070)
* Docs: Add grafanactl term to glossary

* Edit to adapt to Glossary def length

* Fix

* Real fix

* Fix link

---------

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
2025-12-11 17:05:21 +00:00
Tobias Skarhed f63c2cb2dd Scopes: Don't use redirect if you're on an active scope navigation (#115149)
* Don't use redirectUrl if we are on an active scope navigation

* Remove superflous test
2025-12-11 17:42:47 +01:00
Tobias Skarhed fe4c615b3d Scopes: Sync nested scopes navigation open folders to URL (#114786)
* Sync nav_scope_path with url

* Let the current active scope remain if it is a child of the selected subscope

* Remove location updates based on nav_scope_path to maintain expanded folders

* Fix folder tests

* Remove console logs

* Better mock for changeScopes

* Update test to support the new calls

* Update test with function inputs

* Fix failinging test

* Add tests and add isEqual check for fetching new subscopes
2025-12-11 17:34:21 +01:00
grafana-pr-automation[bot] 02d3fd7b31 I18n: Download translations from Crowdin (#115123)
New Crowdin translations by GitHub Action

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-11 16:31:02 +00:00
Jesse David Peterson 5dcfc19060 Table: Add title attribute to make truncated headings legible (#115155)
* fix(table): add HTML title attribute to make truncated headings legible

* fix(table): avoid redundant display name calculation

Co-authored-by: Paul Marbach <paul.marbach@grafana.com>

---------

Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
2025-12-11 12:22:10 -04:00
Roberto Jiménez Sánchez 5bda17be3f Provisioning: Update provisioning docs to reflect kubernetesDashboards defaults to true (#115159)
Docs: Update provisioning docs to reflect kubernetesDashboards defaults to true

The kubernetesDashboards feature toggle now defaults to true, so users
don't need to explicitly enable it in their configuration. Updated
documentation and UI to reflect this:

- Removed kubernetesDashboards from configuration examples
- Added notes explaining it's enabled by default
- Clarified that users only need to take action if they've explicitly
  disabled it
- Kept validation checks to catch explicit disables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 17:08:57 +01:00
Usman Ahmad bc88796e6e Created Troubleshooting guide for MySQL data source plugin (#114737)
* created troubleshooting guide for mysql data source plugin

Signed-off-by: Usman Ahmad <usman.ahmad@grafana.com>

* Apply suggestions from code review

thanks for the code review

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>

* rename file from _index.md to index.md

Signed-off-by: Usman Ahmad <usman.ahmad@grafana.com>

* Update docs/sources/datasources/mysql/troubleshoot/index.md

---------

Signed-off-by: Usman Ahmad <usman.ahmad@grafana.com>
Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
2025-12-11 16:42:09 +01:00
Andres Torres 5d7b9c5050 fix(setting): Replacing dynamic client to reduce memory footprint (#115125) 2025-12-11 10:24:01 -05:00
Alexander Akhmetov 73bcfbcc74 Alerting: Collate alert_rule.namespace_uid column as binary (#115152)
Alerting: Collate namespace_uid column as binary
2025-12-11 16:05:13 +01:00
Erik Sundell 4ab198b201 E2E Selectors: Fix package description (#115148)
dummie change
2025-12-11 14:00:54 +00:00
Erik Sundell 0c82f92539 NPM: Attempt to fix e2e-selectors dist-tag after OIDC migration (#115012)
* fetch oidc token from github

* use same approach as electron
2025-12-11 14:35:27 +01:00
Ivana Huckova 73de5f98e1 Assistant: Update origin for analyze-rule-menu-item (#115147)
* Assistant: Update origin for analyze-rule-menu-item

* Update origin, not test id
2025-12-11 13:06:09 +00:00
Oscar Kilhed b6ba8a0fd4 Dashboards: Make variables selectable in controls menu (#115092)
* Dashboard: Make variables selectable in controls menu and improve spacing

- Add selection support for variables in controls menu (onPointerDown handler and selection classes)
- Add padding to variables and annotations in controls menu (theme.spacing(1))
- Reduce menu container padding from 1.5 to 1
- Remove margins between menu items

* fix: remove unused imports in DashboardControlsMenu
2025-12-11 13:55:03 +01:00
Oscar Kilhed 350c3578c7 Dynamic dashboards: Update variable set state when variable hide property changes (#115094)
fix: update variable set state when variable hide property changes

When changing a variable's positioning to show in controls menu using the edit side pane, the state of dashboardControls does not immediately update. This makes it seem to the user that nothing was changed.

The issue was that when a variable's hide property changes, only the variable's state was updated, but not the parent SceneVariableSet state. Components that subscribe to the variable set state (like useDashboardControls) didn't detect the change because the variables array reference remained the same.

This fix updates the parent SceneVariableSet state when a variable's hide property changes, ensuring components that subscribe to the variable set will re-render immediately.

Co-authored-by: grafakus <marc.mignonsin@grafana.com>
2025-12-11 13:54:30 +01:00
Andres Martinez Gotor e6b5ece559 Plugins Preinstall: Fix URL parsing when includes basic auth (#115143)
Preinstall: Fix URL setting when includes basic auth
2025-12-11 13:38:02 +01:00
Ryan McKinley eef14d2cee Dependencies: update glob@npm for dependabot (#115146) 2025-12-11 12:33:34 +00:00
Anna Urbiztondo c71c0b33ee Docs: Configure Git Sync using CLI (#115068)
* WIP

* WIP

* Edits, Claude

* Prettier

* Update docs/sources/as-code/observability-as-code/provision-resources/git-sync-setup.md

Co-authored-by: Roberto Jiménez Sánchez <roberto.jimenez@grafana.com>

* Update docs/sources/as-code/observability-as-code/provision-resources/git-sync-setup.md

Co-authored-by: Roberto Jiménez Sánchez <roberto.jimenez@grafana.com>

* WIP

* Restructuring

* Minor tweaks

* Fix

* Update docs/sources/as-code/observability-as-code/provision-resources/git-sync-setup.md

Co-authored-by: Roberto Jiménez Sánchez <roberto.jimenez@grafana.com>

* Feedback

* Prettier

* Links

---------

Co-authored-by: Roberto Jiménez Sánchez <roberto.jimenez@grafana.com>
2025-12-11 11:27:36 +00:00
Lauren d568798c64 Alerting: Improve instance count display (#114997)
* Update button text to Show All if filters are enabled

* Show state in text if filters enabled

* resolve PR comments
2025-12-11 11:01:53 +00:00
Ryan McKinley 9bec62a080 Live: simplify dependencies (#115130) 2025-12-11 13:37:45 +03:00
Roberto Jiménez Sánchez 7fe3214f16 Provisioning: Add fieldSelector regression tests for Repository and Jobs (#115135) 2025-12-11 13:36:01 +03:00
Alexander Zobnin e2d12f4cce Zanzana: Refactor remote client initialization (#114142)
* Zanzana: Refactor remote client

* rename config field URL to Addr

* Instrument grpc queries

* fix duplicated field
2025-12-11 10:55:12 +01:00
Victor Marin d48455cd20 Dashboards: Panel non applicable filters optimization (#115132)
optimisations
2025-12-11 11:31:19 +02:00
Alexander Akhmetov 439d2c806c Alerting: Add folder_uid label to the grafana_alerting_rule_group_rules metric (#115129) 2025-12-11 09:30:55 +01:00
Ivan Ortega Alba 8aab6302c5 Fix conversion error shallowed and normalize conversion status (#115086)
* Fix the conversion shallowed error and normalize the conversion status

* Add unit tests to ensure all permutations data loss detection

* Fix counting issue
2025-12-11 08:01:31 +00:00
Torkel Ödegaard 33c5cbf4de Dashboards: Update edit button and share button (#115093)
* Dashboards: Update edit button and share button

* update translations
2025-12-11 08:54:50 +01:00
Ryan McKinley d686a49cf7 Preferences: Enable preferences APIserver (#115128) 2025-12-11 09:33:13 +02:00
Victor Marin cedf08c9ce DashboardDS: Fix datasource annotations not hiding on toggle hide (#115024)
* fix dashboard datasource annotations not hiding on toggle hide

* cleanup
2025-12-11 09:03:38 +02:00
Stephanie Hingtgen 5ca221743f Dashboards: Prevent query for ID 0; improve logging (#115120) 2025-12-11 00:02:52 -07:00
Ryan McKinley 2fc1210b38 Stars: Enable the collections apiserver (#115076) 2025-12-11 06:36:09 +00:00
Ryan McKinley 8542b2f6a2 Live: Move dashboard events from the raw http server to the apiserver (#115066) 2025-12-11 09:26:35 +03:00
Ryan McKinley a6043deb33 UnifiedStorage: Include RV when fieldSelectors are processed in the backend (#115110) 2025-12-11 09:15:01 +03:00
Stephanie Hingtgen 3697c8dafc Dashboards: Fix logging for conversions (#115126) 2025-12-10 23:52:35 -06:00
Charandas 3a4022061d K8s: discourage nil authorizer return for APIBuilder as well (#115116) 2025-12-10 23:06:09 +00:00
beejeebus 2a65e0cdcb Revert "Wire up data source config metrics correctly"
This reverts commit e433cfa02d.
2025-12-10 17:38:00 -05:00
Paul Marbach 000c00aee9 Sparkline: Improve min/max logic to avoid issues for very narrow deltas (#115030)
* Sparkline: Prevent infinite loop when rendering a sparkline with a single value

* some tests for this case

* refactor out utils, experiment with getting highlightIndex working

* add comments throughout for #112977

* remove unused import

* Update Sparkline.test.tsx

* fix points mode rendering

* Sparkline: Improve min/max logic to avoid issues for very narrow deltas

* spread all config

* defaults deep

* delete unused import

* remove go.work.sum delta

* line break at end of file
2025-12-10 16:54:29 -05:00
beejeebus e433cfa02d Wire up data source config metrics correctly
Fix metrics for data source configuration CRUD.

Make sure to only create one histogram and only register it with prometheus once.
2025-12-10 16:16:22 -05:00
Will Assis 30045c02c0 unified-storage: add index on resource_history key_path column (#115113) 2025-12-10 16:03:58 -05:00
Paul Marbach 63bfc1596c Gauge: Updates to the design of the panel edit (#115097)
* Gauge: Updates to the design of the panel edit

* i18n

* remove unused const

* i18n

* fix gdev and migration tests

* reduce bar gauge glow factor
2025-12-10 21:03:44 +00:00
Charandas da14be859e Authorization: panic when specific authorizer returns nil (#114982) 2025-12-10 13:01:34 -08:00
ismail simsek 30d3bb39c0 Chore: Remove deprecated language_provider methods in prometheus package (#114361)
* remove deprecated language provider and its methods

* remove more deprecated code

* yarn lint:prune
2025-12-10 21:07:18 +01:00
Yunwen Zheng b3f98d4cc3 SaveProvisionedDashboard: Provisioned dashboard "Save as copy" flow (#114435)
* SaveProvisionedDashboard: Save as copy flow

* add copy tags toggle
2025-12-10 14:01:51 -05:00
Isabel Matwawana f368139802 Docs: Add permissions information (#115107) 2025-12-10 18:24:36 +00:00
Adela Almasan 7ae9f94de7 Suggestions: Handle errors (#114868)
Co-authored-by: Paul Marbach <paul.marbach@grafana.com>
2025-12-10 18:17:24 +00:00
Alexander Akhmetov a46f0a222e Alerting: Initialize rule routine with initial alert rule fingerprint (#114979)
Alerting: Initialize rule routine with initial fingerprint
2025-12-10 19:14:30 +01:00
Paul Marbach 1146ac790c Sparkline: Prevent infinite loop when rendering a sparkline with a single value (#114203)
* Sparkline: Prevent infinite loop when rendering a sparkline with a single value

* some tests for this case

* refactor out utils, experiment with getting highlightIndex working

* add comments throughout for #112977

* remove unused import

* Update Sparkline.test.tsx

* fix points mode rendering
2025-12-10 12:37:05 -05:00
Gábor Farkas a847f36df2 datasources: querier: log caller (#115087) 2025-12-10 18:23:59 +01:00
Gabriel MABILLE 9e1fe16873 AuthZ: Remove automatic Admin grant for root folders and dashboards (#115098) 2025-12-10 10:00:24 -07:00
Tania 3ec1c27ad4 Chore: Migrate pluginsAutoUpdate flag to OpenFeature (#114404)
* Chore: Migrate pluginsAutoUpdate flag to OpenFeature

* Update workspace

* fixup! Chore: Migrate pluginsAutoUpdate flag to OpenFeature

* Add a test

* Refactor

* Apply suggestion from @hairyhenderson

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Apply suggestions

* Update pkg/services/updatemanager/plugins_test.go

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>

* Reorder code blocks

---------

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2025-12-10 17:40:30 +01:00
Yunwen Zheng 094b6a36dc Add feature flag: recentlyViewedDashboards (#115042) 2025-12-10 11:28:19 -05:00
Yuri Tseretyan 47f7b3e095 Alerting: Dedicated permission for Template testing API (#115032) 2025-12-10 10:56:29 -05:00
owensmallwood 5e7b900416 Unified Storage: Adds readme for setting up quotas/overrides (#115031)
* adds readme for setting up quotas/overrides

* updates namespace wording

* updates docs

* update test

* Revert "update test"

This reverts commit ad43e355ba.
2025-12-10 09:21:52 -06:00
Rafael Bortolon Paulovic 5eae7d4f22 feat: legacy ListIterator with batches (#115038)
* feat: legacy ListIterator with batches

* chore: address code review

* chore: remove nil check in nextBatch

* chore: move close before count check

* chore: add err field to batchingIterator for its own errors

* chore: remove unused import
2025-12-10 16:12:08 +01:00
Rafael Bortolon Paulovic 8c6ccdd1ab feat(dashboard): Org-aware cache for schema migration (#115025)
* fix: use dsIndexProvider cache on migrations

* chore: use same comment as before

* feat: org-aware TTL cache for schemaversion migration and warmup for single tenant

* chore: use LRU cache

* chore: change DefaultCacheTTL to 1 minute

* chore: address copilot reviews

* chore: use expirable cache

* chore: remove unused import
2025-12-10 16:09:16 +01:00
Cauê Marcondes 85c643ece9 Elasticsearch: Add default query mode config setting (#112540)
* elasticsearch: Add default query mode config setting

* doc

* syncing default query mode with url

* addressing PR comments
2025-12-10 15:07:22 +00:00
Galen Kistler 8272edda96 Logs: Default columns API (#114309)
* Logs Drilldown(app-platform): add LogsDrilldownDefaultColumns api

---------

Co-authored-by: L2D2Grafana <liza.detrick@grafana.com>
Co-authored-by: Austin Pond <austin.pond@grafana.com>
2025-12-10 09:02:14 -06:00
Todd Treece e56c2c5156 Plugins App: Remove unused import (#115096) 2025-12-10 14:59:12 +00:00
Todd Treece ac55fad1ba Plugins App: Switch to resource authorizer (#115019) 2025-12-10 09:12:26 -05:00
Will Browne c4c1708e38 Plugins: Sync channel close for app installer readiness (#115078)
sync channel close
2025-12-10 14:07:55 +00:00
Sonia Aguilar 92a8dd8b53 Alerting: Add gh in CLAUDE.md (#114992)
add gh in CLAUDE.md
2025-12-10 14:52:48 +01:00
Ashley Harrison cc1bba85e4 VizLegend: Always display header for screenreader users (#115003)
always display vizlegend header for screenreader users
2025-12-10 13:44:21 +00:00
Ashley Harrison 27482194e3 InteractiveTable: Improve accessibility and reenable tests (#115002)
* attempt at fixing some stuff

* tidy up

* prettier

* fix suppressions
2025-12-10 13:44:08 +00:00
Ida Štambuk ea331dc0d3 Dashboards: Add variables with datasource to tracking (#114110) 2025-12-10 14:39:58 +01:00
Steve Simpson baee9fb214 Alerting: Add historian.alerting app permissions to service identity. (#115082) 2025-12-10 13:05:10 +00:00
Will Browne 39f4b2a959 Plugins: Rename current meta provider to catalog provider (#114966)
rename cloud provider to catalog provider
2025-12-10 12:22:05 +00:00
Will Assis 755b479be4 unified-storage: make sql backend update key_path for kv store (#114879)
* unified-storage: update resource_history_update_rv.sql to populate key_path in resource_history
2025-12-10 07:06:06 -05:00
Dominik Prokop 532a2e5f4d VariablesEditableElement: Set margin correctly (#115079) 2025-12-10 13:04:42 +01:00
Matias Chomicki a7bbca3451 Logs Panel: Emphasize log line, rename field (#114579)
* Logs: Rename attributes field

* LogLine: emphasize log line body

* LogLine: improve light mode

* Lint

* Update tests

* Only override colors if displayed fields are used

* Fix small font size ignored with displayed fields

* Fix types
2025-12-10 11:19:31 +01:00
dependabot[bot] 633332c750 deps(docker): bump alpine from 3.22.2 to 3.23.0 (#114816)
Bumps alpine from 3.22.2 to 3.23.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 3.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 10:34:23 +01:00
Misi 8911785fdf Chore: Regenerate iam app objects with 0.48.5 sdk (#115035)
* Regenerate iam app with 0.48.5 sdk

* update ws

---------

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2025-12-10 09:15:12 +00:00
Stephanie Hingtgen 4fd03bc05e Folders: Fix error handling for zanzana (#115056) 2025-12-10 11:45:22 +03:00
Hugo Häggmark d1761606fb Plugins: Add PluginContext to plugins when scenes is disabled (#114989)
Plugins: Add PluginContext to plugins when scenes is disabled
2025-12-10 06:44:46 +01:00
grafana-pr-automation[bot] 4fe481ec81 I18n: Download translations from Crowdin (#115054)
New Crowdin translations by GitHub Action

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-10 00:42:27 +00:00
owensmallwood 6b50e2d730 Unified Storage: Update yaml decoding for quotas to accomodate top-level overrides key (#115049)
* update yaml decoding for quotas to accomodate top-level overrides key

* update test

* fix test indentation
2025-12-09 23:14:22 +00:00
Isabel Matwawana ed5644be67 Fixed typo 2025-12-09 17:45:43 -05:00
Isabel Matwawana 5a5ff06fbc Updated resource v1 section 2025-12-09 17:43:03 -05:00
Isabel Matwawana 824b2edb44 Updated differences and schema v2 section and moved schema v1 and v1 resource sections 2025-12-09 17:22:17 -05:00
Stephanie Hingtgen ff43c175c8 Folders: Remove duplicate check; improve unit test (#115048) 2025-12-09 22:15:25 +00:00
Jacob Valdez ae03b08c25 Docs: Creating upgrade guide content for annotation table bloat (#114883) 2025-12-09 16:12:49 -06:00
Isabel Matwawana 12b26c4366 Updated access json section and added more aliases 2025-12-09 16:28:06 -05:00
Isabel Matwawana 911d2e48fc Moved content from schema v2 page and deleted folder and index file 2025-12-09 16:04:01 -05:00
Todd Treece 0088e55b8f Plugins App: PluginMeta -> Meta (#115034) 2025-12-09 16:01:22 -05:00
Isabel Matwawana b9fcd914e5 Copied aliases and deleted pages 2025-12-09 16:00:52 -05:00
Stephanie Hingtgen d9fc183e39 Folders: Prevent circular dependencies on apis level (#115040) 2025-12-09 15:00:01 -06:00
Isabel Matwawana 36e2bd34b2 Added new page sections 2025-12-09 15:30:41 -05:00
Isabel Matwawana 520d175f0c Added aliases 2025-12-09 15:27:56 -05:00
Nathan Marrs 6bbb00d0a2 Short URL: Change default expiration to never (#115029)
* Short Links: Change default expiration to never expire (-1)

Previously, short links defaulted to expiring after 7 days. This change
updates the default to -1 (never expire) to prevent automatic deletion
of shared dashboard links.

Changes:
- conf/defaults.ini: Set expire_time = -1 and update comment
- conf/sample.ini: Set expire_time = -1 and update comment
- pkg/setting/setting.go: Update MustInt default from 7 to -1

The cleanup logic already handles -1 correctly (only runs when > 0),
so no changes needed there.

This unblocks progress on short URL feature improvements by ensuring
shared links remain accessible indefinitely by default.

* fix go

* update docs / comments

* update missed comment in sample.ini

* Revert "fix go"

This reverts commit e0d099ae31.

* chore: update workspace dependencies

Run 'make update-workspace' to sync Go workspace dependencies.
This updates go.mod and go.sum files to match the current workspace state.

* chore: add modowner for apps/quotas dependency

Assign @grafana/grafana-search-and-storage as owner for apps/quotas
dependency to satisfy modowners CI check.
2025-12-09 20:23:27 +00:00
Paul Marbach 83b0b14af6 Suggestions: Hook project up to auto-triaging (#114984) 2025-12-09 14:53:55 -05:00
Todd Treece 18280e1aa6 Chore: Add owner for quotas in go.mod (#115039) 2025-12-09 19:48:19 +00:00
Mihai Doarna b3980eeec8 IAM: Add tracing for legacy stores (#114974) 2025-12-09 13:46:18 -06:00
owensmallwood b8acfade21 Unified Storage: Adds debug logs for checking quotas (#115036)
* adds debug logs for checking quotas

* make update-workspace
2025-12-09 18:59:46 +00:00
Jesse David Peterson 1013d74f13 TimeRange: Additional keyboard shortcut t = to complement t + for zoom in (#115022)
feat(time-range): additional keyboard shortcut "t =" for zoom in
2025-12-09 14:35:43 -04:00
Marc M. 4b999cd943 FeatureToggles: Add multiPropsVariables (#115020) 2025-12-09 18:11:49 +01:00
Stephanie Hingtgen 747da28fe4 Docs: Remove unused feature toggle logsinfinitescrolling (#114983) 2025-12-09 10:52:01 -06:00
Matias Chomicki 3197892094 Log Line Details: Header options and inline icons improvements (#114479)
* LogLineDetailsHeader: introduce divider

* LogLineDetails: improve icons spacing

* useKeyBindings: close sidebar details with escape
2025-12-09 17:15:05 +01:00
Matias Chomicki 6e4de0f81c LogLine: add trailing spaces for copying (#114543)
* LogLine: add trailing spaces for copying

* Lint
2025-12-09 17:08:09 +01:00
Kristina Demeshchik 533ee1f078 Dashboard : Allow applying variable regex to display text (#114426)
* Ability to apply regex to display text

* Frontend tests

* scenes-react version

* lock file

* adjust tests input

* adjust inputs

* unused variable

* change data type

* unit tests

* bump scenes

* bump scenes

* Update docs

* V2->V1 conversion

* re-generate files

* update openai snapshots
2025-12-09 10:55:51 -05:00
Rafael Bortolon Paulovic 45e679eeba fix: use dsIndexProvider cache on schema migrations (#115018)
* fix: use dsIndexProvider cache on migrations

* chore: use same comment as before
2025-12-09 16:54:54 +01:00
owensmallwood a3daf0e39d Unified storage: Add quotas app to apiserver (#114425)
* initial generation

* went through doc to add new resource

* added dummy kind so grafana will run

* added dummy handler and custom route

* fix app name

* gets custom route working - still a dummy route

* adds groupOverride to manifest

* adds quotas to grpc client and server

* WIP - trying to get api recognized - not working

* Gets route working

* fixes group and resource vars

* expects group and resource as separate params

* set content-type header on response

* removes Quotas kind and regens

* Update grafana-app-sdk to v0.48.5

* Update codegen

* updates manifest

* formatting

* updates grafana-app-sdk version to 0.48.5

* regen ResourceClient mocks

* adds tests

* remove commented code

* uncomment go mod tidy

* fix tests and make update workspace

* adds quotas app to codeowners

* formatting

* make gen-apps

* deletes temp file

* fix generated folder code

* make gofmt

* make gen-go

* make update-workspace

* add COPY apps/quotas to Dockerfile

* fix test mock

* fixes undefined NewFolderStatus()

* make gen-apps, and add func for NewFolderStatus

* make gen-apps again

* make update-workspace

* regen folder_object_gen.go

* gofmt

* fix linting

* apps/folder make update-workspace

* make gen-apps

* make gen-apps

* fixes enterprise_imports.go

* go get testcontainers

* adds feature toggle

* make update-workspace

* fix go mod

* fix another client mock

---------

Co-authored-by: Steve Simpson <steve@grafana.com>
2025-12-09 09:40:34 -06:00
Rafael Bortolon Paulovic 297e886e1b fix: remove dsIndexProvider from Convert_V2alpha1_to_V0 (#115017) 2025-12-09 16:33:43 +01:00
Mihai Doarna 8602ec7924 IAM: Add integration tests for team search (#114996)
add integration tests for team search
2025-12-09 17:31:38 +02:00
Renato Costa 83311049ad fix: create dashboard in legacy storage within transaction (#114808)
fix: create dashboard within transaction
2025-12-09 10:16:33 -05:00
Kristina Demeshchik 8c4b3d1702 Dashboard: Default dashboard folder to current folder when importing (#114929)
* Set default folder when importing dashboard

* console

* remove unused import
2025-12-09 09:59:21 -05:00
Santiago b8ad272159 Alerting: Fix header precedence in the remote writer (#114999) 2025-12-09 15:38:57 +01:00
Torkel Ödegaard b5f1573aef AppChrome: Increase header height from 40-48 (#115004)
AppChrome: Inrease header height from 40-48
2025-12-09 15:19:50 +01:00
Jean-Philippe Quéméner 1f5fd1c0da chore(unified-storage): align how we do tracing (#114998) 2025-12-09 14:53:53 +01:00
Christian Simon 3e66c7ed21 CI: Add Docker Hub authentication to ephemeral instances workflow (#114851)
* CI: Add Docker Hub authentication to ephemeral instances workflow

Add Docker Hub login step to avoid unauthenticated image pull
rate-limiting in the ephemeral-instances-pr-comment workflow.

* Use the correct vault path
2025-12-09 13:15:15 +00:00
Alexander Akhmetov c59d5d1c8e Alerting: Store instance annotations in alert rule state (#114975)
Alerting: Store annotations in alert instance state
2025-12-09 13:52:42 +01:00
Tobias Skarhed 6746c978b4 Scopes: Resolve path directly from leaf node bugfix (#114507)
* Resolve path directly from leaf node

* Add childrenLoaded field

* Add tests and remove parentNodeId from changeScopes

* Move parentNodeId patameter order

* Resotre call order

* Undo superflous change

* Add comments

* Make sure childrenLoaded state is properly set default to false

* Reference parent path

* Look for parent in state and fetch scopeNode if it is not avilable

* Check for undefined

* Add mock to test

* Set scopeNodeId with recent scopes

* Improve test selector

* Add scope node endpoint to mocks

* Never set childrenLoaded to true when inserting

* Remove unused import

* Pass on the already set childrenLoaded value

* Fix test
2025-12-09 13:44:00 +01:00
Oscar Kilhed 3cac27c611 Dashboards V2: Show banner when a dashboard has been converted from v2 to v1. (#114960)
* Show warning banner when convertion from v2 to v1

* fix edit state

* Reset eslint-suppression.json

* Always show, update copy

* reset eslint-supression.json

* Update copy

* reset eslint-supression.json
2025-12-09 13:38:26 +01:00
Ryan McKinley 8cae4eb0af Investigations: avoid useoldmanifestkinds and remove unused status (#114903) 2025-12-09 15:11:07 +03:00
211 changed files with 4031 additions and 3265 deletions
+1
View File
@@ -85,6 +85,7 @@
# Git Sync frontend owned by frontend team as a whole.
/apps/alerting/ @grafana/alerting-backend
/apps/quotas/ @grafana/grafana-search-and-storage
/apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad
/apps/folder/ @grafana/grafana-app-platform-squad
/apps/playlist/ @grafana/grafana-app-platform-squad
+8
View File
@@ -1226,5 +1226,13 @@
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/69"
}
},
{
"type": "label",
"name": "area/suggestions",
"action": "addToProject",
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/56"
}
}
]
+10
View File
@@ -469,5 +469,15 @@
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/190"
}
},
{
"type": "changedfiles",
"matches": [
"public/app/features/panel/suggestions/**/*",
"public/app/plugins/panel/**/suggestions.ts",
"packages/grafana-data/src/types/suggestions*"
],
"action": "updateLabel",
"addLabel": "area/suggestions"
}
]
@@ -85,6 +85,7 @@ area/scenes
area/search
area/security
area/streaming
area/suggestions
area/templating/repeating
area/tooltip
area/transformations
@@ -33,6 +33,16 @@ jobs:
GCOM_TOKEN=ephemeral-instances-bot:gcom-token
REGISTRY=ephemeral-instances-bot:registry
GCP_SA_ACCOUNT_KEY_BASE64=ephemeral-instances-bot:sa-key
# Secrets placed in the ci/common/<path> path in Vault
common_secrets: |
DOCKERHUB_USERNAME=dockerhub:username
DOCKERHUB_PASSWORD=dockerhub:password
- name: Log in to Docker Hub to avoid unauthenticated image pull rate-limiting
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ env.DOCKERHUB_PASSWORD }}
- name: Generate a GitHub app installation token
id: generate_token
+1
View File
@@ -93,6 +93,7 @@ COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
COPY pkg/semconv pkg/semconv
COPY pkg/aggregator pkg/aggregator
COPY apps/playlist apps/playlist
COPY apps/quotas apps/quotas
COPY apps/plugins apps/plugins
COPY apps/shorturl apps/shorturl
COPY apps/annotation apps/annotation
+2
View File
@@ -224,6 +224,8 @@ github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmF
github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU=
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM=
github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4=
github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 h1:/5LKSYgLmAhwA4m6iGUD4w1YkydEWWjazn9qxCFT8W0=
@@ -768,6 +768,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
VariableRegexApplyTo: *"value" | "text"
// Determine the origin of the adhoc variable filter
FilterOrigin: "dashboard"
@@ -803,6 +807,7 @@ QueryVariableSpec: {
datasource?: DataSourceRef
query: DataQueryKind
regex: string | *""
regexApplyTo?: VariableRegexApplyTo
sort: VariableSort
definition?: string
options: [...VariableOption] | *[]
@@ -772,6 +772,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing), `inControlsMenu` (show in a drop-down menu).
VariableHide: *"dontHide" | "hideLabel" | "hideVariable" | "inControlsMenu"
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
VariableRegexApplyTo: *"value" | "text"
// Determine the origin of the adhoc variable filter
FilterOrigin: "dashboard"
@@ -806,6 +810,7 @@ QueryVariableSpec: {
description?: string
query: DataQueryKind
regex: string | *""
regexApplyTo?: VariableRegexApplyTo
sort: VariableSort
definition?: string
options: [...VariableOption] | *[]
@@ -222,6 +222,8 @@ lineage: schemas: [{
// Optional field, if you want to extract part of a series name or metric node segment.
// Named capture groups can be used to separate the display text and value.
regex?: string
// Determine whether regex applies to variable value or display text
regexApplyTo?: #VariableRegexApplyTo
// Additional static options for query variable
staticOptions?: [...#VariableOption]
// Ordering of static options in relation to options returned from data source for query variable
@@ -249,6 +251,10 @@ lineage: schemas: [{
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
// Determine whether regex applies to variable value or display text
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
// Sort variable options
// Accepted values are:
// `0`: No sorting
@@ -222,6 +222,8 @@ lineage: schemas: [{
// Optional field, if you want to extract part of a series name or metric node segment.
// Named capture groups can be used to separate the display text and value.
regex?: string
// Determine whether regex applies to variable value or display text
regexApplyTo?: #VariableRegexApplyTo
// Additional static options for query variable
staticOptions?: [...#VariableOption]
// Ordering of static options in relation to options returned from data source for query variable
@@ -249,6 +251,10 @@ lineage: schemas: [{
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
// Determine whether regex applies to variable value or display text
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
// Sort variable options
// Accepted values are:
// `0`: No sorting
@@ -772,6 +772,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
VariableRegexApplyTo: *"value" | "text"
// Determine the origin of the adhoc variable filter
FilterOrigin: "dashboard"
@@ -807,6 +811,7 @@ QueryVariableSpec: {
datasource?: DataSourceRef
query: DataQueryKind
regex: string | *""
regexApplyTo?: VariableRegexApplyTo
sort: VariableSort
definition?: string
options: [...VariableOption] | *[]
@@ -1364,6 +1364,7 @@ type DashboardQueryVariableSpec struct {
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
Query DashboardDataQueryKind `json:"query"`
Regex string `json:"regex"`
RegexApplyTo *DashboardVariableRegexApplyTo `json:"regexApplyTo,omitempty"`
Sort DashboardVariableSort `json:"sort"`
Definition *string `json:"definition,omitempty"`
Options []DashboardVariableOption `json:"options"`
@@ -1393,6 +1394,7 @@ func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec {
SkipUrlSync: false,
Query: *NewDashboardDataQueryKind(),
Regex: "",
RegexApplyTo: (func(input DashboardVariableRegexApplyTo) *DashboardVariableRegexApplyTo { return &input })(DashboardVariableRegexApplyToValue),
Options: []DashboardVariableOption{},
Multi: false,
IncludeAll: false,
@@ -1443,6 +1445,16 @@ const (
DashboardVariableRefreshOnTimeRangeChanged DashboardVariableRefresh = "onTimeRangeChanged"
)
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
// +k8s:openapi-gen=true
type DashboardVariableRegexApplyTo string
const (
DashboardVariableRegexApplyToValue DashboardVariableRegexApplyTo = "value"
DashboardVariableRegexApplyToText DashboardVariableRegexApplyTo = "text"
)
// Sort variable options
// Accepted values are:
// `disabled`: No sorting
@@ -3646,6 +3646,12 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re
Format: "",
},
},
"regexApplyTo": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"sort": {
SchemaProps: spec.SchemaProps{
Default: "",
@@ -776,6 +776,10 @@ VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing), `inControlsMenu` (show in a drop-down menu).
VariableHide: *"dontHide" | "hideLabel" | "hideVariable" | "inControlsMenu"
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
VariableRegexApplyTo: *"value" | "text"
// Determine the origin of the adhoc variable filter
FilterOrigin: "dashboard"
@@ -810,6 +814,7 @@ QueryVariableSpec: {
description?: string
query: DataQueryKind
regex: string | *""
regexApplyTo?: VariableRegexApplyTo
sort: VariableSort
definition?: string
options: [...VariableOption] | *[]
@@ -1367,6 +1367,7 @@ type DashboardQueryVariableSpec struct {
Description *string `json:"description,omitempty"`
Query DashboardDataQueryKind `json:"query"`
Regex string `json:"regex"`
RegexApplyTo *DashboardVariableRegexApplyTo `json:"regexApplyTo,omitempty"`
Sort DashboardVariableSort `json:"sort"`
Definition *string `json:"definition,omitempty"`
Options []DashboardVariableOption `json:"options"`
@@ -1396,6 +1397,7 @@ func NewDashboardQueryVariableSpec() *DashboardQueryVariableSpec {
SkipUrlSync: false,
Query: *NewDashboardDataQueryKind(),
Regex: "",
RegexApplyTo: (func(input DashboardVariableRegexApplyTo) *DashboardVariableRegexApplyTo { return &input })(DashboardVariableRegexApplyToValue),
Options: []DashboardVariableOption{},
Multi: false,
IncludeAll: false,
@@ -1447,6 +1449,16 @@ const (
DashboardVariableRefreshOnTimeRangeChanged DashboardVariableRefresh = "onTimeRangeChanged"
)
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
// +k8s:openapi-gen=true
type DashboardVariableRegexApplyTo string
const (
DashboardVariableRegexApplyToValue DashboardVariableRegexApplyTo = "value"
DashboardVariableRegexApplyToText DashboardVariableRegexApplyTo = "text"
)
// Sort variable options
// Accepted values are:
// `disabled`: No sorting
@@ -3656,6 +3656,12 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableSpec(ref common.Ref
Format: "",
},
},
"regexApplyTo": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"sort": {
SchemaProps: spec.SchemaProps{
Default: "",
File diff suppressed because one or more lines are too long
@@ -12,13 +12,6 @@ import (
)
func RegisterConversions(s *runtime.Scheme, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error {
// Wrap the provider once with 10s caching for all conversions.
// This prevents repeated DB queries across multiple conversion calls while allowing
// the cache to refresh periodically, making it suitable for long-lived singleton usage.
dsIndexProvider = schemaversion.WrapIndexProviderWithCache(dsIndexProvider)
// Wrap library element provider with caching as well
leIndexProvider = schemaversion.WrapLibraryElementProviderWithCache(leIndexProvider)
// v0 conversions
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil),
withConversionMetrics(dashv0.APIVERSION, dashv1.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
@@ -62,13 +55,13 @@ func RegisterConversions(s *runtime.Scheme, dsIndexProvider schemaversion.DataSo
// v2alpha1 conversions
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv0.Dashboard)(nil),
withConversionMetrics(dashv2alpha1.APIVERSION, dashv0.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2alpha1_to_V0(a.(*dashv2alpha1.Dashboard), b.(*dashv0.Dashboard), scope, dsIndexProvider)
return Convert_V2alpha1_to_V0(a.(*dashv2alpha1.Dashboard), b.(*dashv0.Dashboard), scope)
})); err != nil {
return err
}
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv1.Dashboard)(nil),
withConversionMetrics(dashv2alpha1.APIVERSION, dashv1.APIVERSION, func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2alpha1_to_V1beta1(a.(*dashv2alpha1.Dashboard), b.(*dashv1.Dashboard), scope, dsIndexProvider)
return Convert_V2alpha1_to_V1beta1(a.(*dashv2alpha1.Dashboard), b.(*dashv1.Dashboard), scope)
})); err != nil {
return err
}
@@ -42,7 +42,7 @@
"regex": "",
"skipUrlSync": false,
"refresh": 1
},
},
{
"name": "query_var",
"type": "query",
@@ -81,6 +81,7 @@
"allValue": ".*",
"multi": true,
"regex": "/.*9090.*/",
"regexApplyTo": "text",
"skipUrlSync": false,
"refresh": 2,
"sort": 1,
@@ -107,7 +108,7 @@
},
{
"selected": false,
"text": "staging",
"text": "staging",
"value": "staging"
},
{
@@ -335,6 +336,7 @@
"allValue": "*",
"multi": true,
"regex": "/host[0-9]+/",
"regexApplyTo": "value",
"skipUrlSync": false,
"refresh": 1,
"sort": 2,
@@ -354,4 +356,4 @@
},
"links": []
}
}
}
@@ -94,6 +94,7 @@
"query": "label_values(up, instance)",
"refresh": 2,
"regex": "/.*9090.*/",
"regexApplyTo": "text",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
@@ -362,6 +363,7 @@
},
"refresh": 1,
"regex": "/host[0-9]+/",
"regexApplyTo": "value",
"skipUrlSync": false,
"sort": 2,
"tagValuesQuery": "",
@@ -110,6 +110,7 @@
}
},
"regex": "/.*9090.*/",
"regexApplyTo": "text",
"sort": "alphabeticalAsc",
"definition": "label_values(up, instance)",
"options": [
@@ -401,6 +402,7 @@
}
},
"regex": "/host[0-9]+/",
"regexApplyTo": "value",
"sort": "alphabeticalDesc",
"definition": "terms field:@host size:100",
"options": [],
@@ -111,6 +111,7 @@
}
},
"regex": "/.*9090.*/",
"regexApplyTo": "text",
"sort": "alphabeticalAsc",
"definition": "label_values(up, instance)",
"options": [
@@ -404,6 +405,7 @@
}
},
"regex": "/host[0-9]+/",
"regexApplyTo": "value",
"sort": "alphabeticalDesc",
"definition": "terms field:@host size:100",
"options": [],
@@ -229,6 +229,16 @@ func getBoolField(m map[string]interface{}, key string, defaultValue bool) bool
return defaultValue
}
func getUnionField[T ~string](m map[string]interface{}, key string) *T {
if val, ok := m[key]; ok {
if str, ok := val.(string); ok && str != "" {
result := T(str)
return &result
}
}
return nil
}
// Helper function to create int64 pointer
func int64Ptr(i int64) *int64 {
return &i
@@ -1195,6 +1205,7 @@ func buildQueryVariable(ctx context.Context, varMap map[string]interface{}, comm
Refresh: transformVariableRefreshToEnum(varMap["refresh"]),
Sort: transformVariableSortToEnum(varMap["sort"]),
Regex: schemaversion.GetStringValue(varMap, "regex"),
RegexApplyTo: getUnionField[dashv2alpha1.DashboardVariableRegexApplyTo](varMap, "regexApplyTo"),
Query: buildDataQueryKindForVariable(varMap["query"], datasourceType),
AllowCustomValue: getBoolField(varMap, "allowCustomValue", true),
},
@@ -11,10 +11,10 @@ import (
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
// Convert v2alpha1 → v1beta1 first, then v1beta1 → v0
v1beta1 := &dashv1.Dashboard{}
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, v1beta1, scope, dsIndexProvider); err != nil {
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, v1beta1, scope); err != nil {
out.ObjectMeta = in.ObjectMeta
out.APIVersion = dashv0.APIVERSION
out.Kind = in.Kind
@@ -53,13 +53,13 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s
return nil
}
func Convert_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
func Convert_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.APIVersion = dashv1.APIVERSION
out.Kind = in.Kind
// Convert the spec
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, out, scope, dsIndexProvider); err != nil {
if err := ConvertDashboard_V2alpha1_to_V1beta1(in, out, scope); err != nil {
out.Status = dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
StoredVersion: ptr.To(dashv2alpha1.VERSION),
@@ -179,7 +179,7 @@ func Convert_V2beta1_to_V1beta1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard
// Convert v2alpha1 → v1beta1
// Note: ConvertDashboard_V2alpha1_to_V1beta1 will set out.ObjectMeta from v2alpha1,
// but we've already set it from the original input, so it will be preserved
if err := ConvertDashboard_V2alpha1_to_V1beta1(v2alpha1, out, scope, dsIndexProvider); err != nil {
if err := ConvertDashboard_V2alpha1_to_V1beta1(v2alpha1, out, scope); err != nil {
out.Status = dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
StoredVersion: ptr.To(dashv2beta1.VERSION),
@@ -39,7 +39,7 @@ func TestV2alpha1ConversionErrorHandling(t *testing.T) {
}
target := &dashv1.Dashboard{}
err := Convert_V2alpha1_to_V1beta1(source, target, nil, dsProvider)
err := Convert_V2alpha1_to_V1beta1(source, target, nil)
// Convert_V2alpha1_to_V1beta1 doesn't return error, just sets status
require.NoError(t, err, "Convert_V2alpha1_to_V1beta1 doesn't return error")
@@ -1,14 +1,12 @@
package conversion
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/conversion"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
"k8s.io/apimachinery/pkg/conversion"
)
// ConvertDashboard_V2alpha1_to_V1beta1 converts a v2alpha1 dashboard to v1beta1 format.
@@ -16,19 +14,13 @@ import (
// that represents the v1 dashboard JSON format.
// The dsIndexProvider is used to resolve default datasources when queries/variables/annotations
// don't have explicit datasource references.
func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider) error {
func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.APIVersion = dashv1.APIVERSION
out.Kind = in.Kind // Preserve the Kind from input (should be "Dashboard")
// Get datasource index for resolving default datasources
var dsIndex *schemaversion.DatasourceIndex
if dsIndexProvider != nil {
dsIndex = dsIndexProvider.Index(context.Background())
}
// Convert the spec to v1beta1 unstructured format
dashboardJSON, err := convertDashboardSpec_V2alpha1_to_V1beta1(&in.Spec, dsIndex)
dashboardJSON, err := convertDashboardSpec_V2alpha1_to_V1beta1(&in.Spec)
if err != nil {
return fmt.Errorf("failed to convert dashboard spec: %w", err)
}
@@ -39,7 +31,7 @@ func ConvertDashboard_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv
return nil
}
func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec) (map[string]interface{}, error) {
dashboard := make(map[string]interface{})
// Convert basic fields
@@ -75,7 +67,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
}
// Convert panels from elements and layout
panels, err := convertPanelsFromElementsAndLayout(in.Elements, in.Layout, dsIndex)
panels, err := convertPanelsFromElementsAndLayout(in.Elements, in.Layout)
if err != nil {
return nil, fmt.Errorf("failed to convert panels: %w", err)
}
@@ -90,7 +82,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
}
// Convert variables
variables := convertVariablesToV1(in.Variables, dsIndex)
variables := convertVariablesToV1(in.Variables)
if len(variables) > 0 {
dashboard["templating"] = map[string]interface{}{
"list": variables,
@@ -98,7 +90,7 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec, ds
}
// Convert annotations - always include even if empty to prevent DashboardModel from adding built-in
annotations := convertAnnotationsToV1(in.Annotations, dsIndex)
annotations := convertAnnotationsToV1(in.Annotations)
dashboard["annotations"] = map[string]interface{}{
"list": annotations,
}
@@ -236,28 +228,28 @@ func countTotalPanels(panels []interface{}) int {
// - RowsLayout: Rows become row panels; nested structures are flattened
// - AutoGridLayout: Calculates gridPos based on column count and row height
// - TabsLayout: Tabs become expanded row panels; content is flattened
func convertPanelsFromElementsAndLayout(elements map[string]dashv2alpha1.DashboardElement, layout dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
func convertPanelsFromElementsAndLayout(elements map[string]dashv2alpha1.DashboardElement, layout dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind) ([]interface{}, error) {
if layout.GridLayoutKind != nil {
return convertGridLayoutToPanels(elements, layout.GridLayoutKind, dsIndex)
return convertGridLayoutToPanels(elements, layout.GridLayoutKind)
}
if layout.RowsLayoutKind != nil {
return convertRowsLayoutToPanels(elements, layout.RowsLayoutKind, dsIndex)
return convertRowsLayoutToPanels(elements, layout.RowsLayoutKind)
}
if layout.AutoGridLayoutKind != nil {
return convertAutoGridLayoutToPanels(elements, layout.AutoGridLayoutKind, dsIndex)
return convertAutoGridLayoutToPanels(elements, layout.AutoGridLayoutKind)
}
if layout.TabsLayoutKind != nil {
return convertTabsLayoutToPanels(elements, layout.TabsLayoutKind, dsIndex)
return convertTabsLayoutToPanels(elements, layout.TabsLayoutKind)
}
// No layout specified, return empty panels
return []interface{}{}, nil
}
func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, gridLayout *dashv2alpha1.DashboardGridLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, gridLayout *dashv2alpha1.DashboardGridLayoutKind) ([]interface{}, error) {
panels := make([]interface{}, 0, len(gridLayout.Spec.Items))
for _, item := range gridLayout.Spec.Items {
@@ -266,7 +258,7 @@ func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement
return nil, fmt.Errorf("panel with uid %s not found in the dashboard elements", item.Spec.Element.Name)
}
panel, err := convertPanelFromElement(&element, &item, dsIndex)
panel, err := convertPanelFromElement(&element, &item)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -279,21 +271,21 @@ func convertGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement
// convertRowsLayoutToPanels converts a RowsLayout to V1 panels.
// All nested structures (rows within rows, tabs within rows) are flattened to the root level.
// Each row becomes a row panel, and nested content is added sequentially after it.
func convertRowsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
return convertNestedLayoutToPanels(elements, rowsLayout, nil, dsIndex, 0)
func convertRowsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind) ([]interface{}, error) {
return convertNestedLayoutToPanels(elements, rowsLayout, nil, 0)
}
// convertNestedLayoutToPanels handles arbitrary nesting of RowsLayout and TabsLayout.
// It processes each row/tab in order, tracking Y position to ensure panels don't overlap.
// The function recursively flattens nested structures to produce a flat V1 panel array.
func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex, yOffset int64) ([]interface{}, error) {
func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, rowsLayout *dashv2alpha1.DashboardRowsLayoutKind, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, yOffset int64) ([]interface{}, error) {
panels := make([]interface{}, 0)
currentY := yOffset
// Process RowsLayout
if rowsLayout != nil {
for _, row := range rowsLayout.Spec.Rows {
rowPanels, newY, err := processRowItem(elements, &row, dsIndex, currentY)
rowPanels, newY, err := processRowItem(elements, &row, currentY)
if err != nil {
return nil, err
}
@@ -305,7 +297,7 @@ func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardEleme
// Process TabsLayout (tabs are converted to rows)
if tabsLayout != nil {
for _, tab := range tabsLayout.Spec.Tabs {
tabPanels, newY, err := processTabItem(elements, &tab, dsIndex, currentY)
tabPanels, newY, err := processTabItem(elements, &tab, currentY)
if err != nil {
return nil, err
}
@@ -324,7 +316,7 @@ func convertNestedLayoutToPanels(elements map[string]dashv2alpha1.DashboardEleme
// - Collapsed row: Panels stored inside row.panels with absolute Y positions
// - Expanded row: Panels added to top level after the row panel
// - Nested layouts: Parent row is preserved; nested content is flattened after it
func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dashv2alpha1.DashboardRowsLayoutRowKind, dsIndex *schemaversion.DatasourceIndex, startY int64) ([]interface{}, int64, error) {
func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dashv2alpha1.DashboardRowsLayoutRowKind, startY int64) ([]interface{}, int64, error) {
panels := make([]interface{}, 0)
currentY := startY
@@ -354,7 +346,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
}
// Then process nested rows
nestedPanels, err := convertNestedLayoutToPanels(elements, row.Spec.Layout.RowsLayoutKind, nil, dsIndex, currentY)
nestedPanels, err := convertNestedLayoutToPanels(elements, row.Spec.Layout.RowsLayoutKind, nil, currentY)
if err != nil {
return nil, 0, err
}
@@ -387,7 +379,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
}
// Then process nested tabs
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, row.Spec.Layout.TabsLayoutKind, dsIndex, currentY)
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, row.Spec.Layout.TabsLayoutKind, currentY)
if err != nil {
return nil, 0, err
}
@@ -429,7 +421,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
// Add collapsed panels if row is collapsed (panels use absolute Y positions)
if isCollapsed {
collapsedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY+1)
collapsedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY+1)
if err != nil {
return nil, 0, err
}
@@ -444,7 +436,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
// Add panels from row layout (only for expanded rows or hidden header rows)
if !isCollapsed || isHiddenHeader {
rowPanels, newY, err := extractExpandedPanels(elements, &row.Spec.Layout, dsIndex, currentY, isHiddenHeader, startY)
rowPanels, newY, err := extractExpandedPanels(elements, &row.Spec.Layout, currentY, isHiddenHeader, startY)
if err != nil {
return nil, 0, err
}
@@ -459,7 +451,7 @@ func processRowItem(elements map[string]dashv2alpha1.DashboardElement, row *dash
// Each tab becomes an expanded row panel (collapsed=false) with an empty panels array.
// The tab's content is flattened and added to the top level after the row panel.
// Nested layouts within the tab are recursively processed.
func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dashv2alpha1.DashboardTabsLayoutTabKind, dsIndex *schemaversion.DatasourceIndex, startY int64) ([]interface{}, int64, error) {
func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dashv2alpha1.DashboardTabsLayoutTabKind, startY int64) ([]interface{}, int64, error) {
panels := make([]interface{}, 0)
currentY := startY
@@ -487,7 +479,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
// Handle nested layouts inside the tab
if tab.Spec.Layout.RowsLayoutKind != nil {
// Nested RowsLayout inside tab
nestedPanels, err := convertNestedLayoutToPanels(elements, tab.Spec.Layout.RowsLayoutKind, nil, dsIndex, currentY)
nestedPanels, err := convertNestedLayoutToPanels(elements, tab.Spec.Layout.RowsLayoutKind, nil, currentY)
if err != nil {
return nil, 0, err
}
@@ -495,7 +487,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
currentY = getMaxYFromPanels(nestedPanels, currentY)
} else if tab.Spec.Layout.TabsLayoutKind != nil {
// Nested TabsLayout inside tab
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, tab.Spec.Layout.TabsLayoutKind, dsIndex, currentY)
nestedPanels, err := convertNestedLayoutToPanels(elements, nil, tab.Spec.Layout.TabsLayoutKind, currentY)
if err != nil {
return nil, 0, err
}
@@ -512,7 +504,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
adjustedItem := item
adjustedItem.Spec.Y = item.Spec.Y + currentY
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
panel, err := convertPanelFromElement(&element, &adjustedItem)
if err != nil {
return nil, 0, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -525,7 +517,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
}
} else if tab.Spec.Layout.AutoGridLayoutKind != nil {
// AutoGridLayout inside tab - convert with Y offset
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, tab.Spec.Layout.AutoGridLayoutKind, dsIndex, currentY)
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, tab.Spec.Layout.AutoGridLayoutKind, currentY)
if err != nil {
return nil, 0, err
}
@@ -540,7 +532,7 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
// Panels are positioned with absolute Y coordinates (baseY + relative Y).
// This matches V1 behavior where collapsed row panels store their children
// with Y positions as if the row were expanded at that location.
func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex, baseY int64) ([]interface{}, error) {
func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, baseY int64) ([]interface{}, error) {
panels := make([]interface{}, 0)
if layout.GridLayoutKind != nil {
@@ -552,7 +544,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
// Create a copy with adjusted Y position
adjustedItem := item
adjustedItem.Spec.Y = item.Spec.Y + baseY
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
panel, err := convertPanelFromElement(&element, &adjustedItem)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -561,7 +553,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
}
// Handle AutoGridLayout for collapsed rows with Y offset
if layout.AutoGridLayoutKind != nil {
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, baseY)
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, baseY)
if err != nil {
return nil, err
}
@@ -571,7 +563,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
if layout.RowsLayoutKind != nil {
currentY := baseY
for _, row := range layout.RowsLayoutKind.Spec.Rows {
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY)
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY)
if err != nil {
return nil, err
}
@@ -582,7 +574,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
if layout.TabsLayoutKind != nil {
currentY := baseY
for _, tab := range layout.TabsLayoutKind.Spec.Tabs {
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, dsIndex, currentY)
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, currentY)
if err != nil {
return nil, err
}
@@ -596,7 +588,7 @@ func extractCollapsedPanelsWithAbsoluteY(elements map[string]dashv2alpha1.Dashbo
// extractCollapsedPanelsFromTabLayoutWithAbsoluteY extracts panels from a tab layout with absolute Y.
// Similar to extractCollapsedPanelsWithAbsoluteY but handles the tab-specific layout type.
func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex, baseY int64) ([]interface{}, error) {
func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind, baseY int64) ([]interface{}, error) {
panels := make([]interface{}, 0)
if layout.GridLayoutKind != nil {
@@ -607,7 +599,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
}
adjustedItem := item
adjustedItem.Spec.Y = item.Spec.Y + baseY
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
panel, err := convertPanelFromElement(&element, &adjustedItem)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -615,7 +607,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
}
}
if layout.AutoGridLayoutKind != nil {
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, baseY)
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, baseY)
if err != nil {
return nil, err
}
@@ -624,7 +616,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
if layout.RowsLayoutKind != nil {
currentY := baseY
for _, row := range layout.RowsLayoutKind.Spec.Rows {
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, dsIndex, currentY)
nestedPanels, err := extractCollapsedPanelsWithAbsoluteY(elements, &row.Spec.Layout, currentY)
if err != nil {
return nil, err
}
@@ -635,7 +627,7 @@ func extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements map[string]dashv2
if layout.TabsLayoutKind != nil {
currentY := baseY
for _, tab := range layout.TabsLayoutKind.Spec.Tabs {
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, dsIndex, currentY)
nestedPanels, err := extractCollapsedPanelsFromTabLayoutWithAbsoluteY(elements, &tab.Spec.Layout, currentY)
if err != nil {
return nil, err
}
@@ -679,7 +671,7 @@ func getLayoutHeightFromTab(layout *dashv2alpha1.DashboardGridLayoutKindOrRowsLa
// - Explicit row: Add (currentY - 1) to relative Y for absolute positioning
//
// Returns the panels and the new Y position for the next row.
func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, dsIndex *schemaversion.DatasourceIndex, currentY int64, isHiddenHeader bool, startY int64) ([]interface{}, int64, error) {
func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind, currentY int64, isHiddenHeader bool, startY int64) ([]interface{}, int64, error) {
panels := make([]interface{}, 0)
// For hidden headers, don't track Y changes (matches original behavior)
maxY := startY
@@ -700,7 +692,7 @@ func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, la
}
// For hidden headers: don't adjust Y, keep item.Spec.Y as-is
panel, err := convertPanelFromElement(&element, &adjustedItem, dsIndex)
panel, err := convertPanelFromElement(&element, &adjustedItem)
if err != nil {
return nil, 0, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -725,7 +717,7 @@ func extractExpandedPanels(elements map[string]dashv2alpha1.DashboardElement, la
yOffset = currentY - 1
}
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, dsIndex, yOffset)
autoGridPanels, err := convertAutoGridLayoutToPanelsWithOffset(elements, layout.AutoGridLayoutKind, yOffset)
if err != nil {
return nil, 0, err
}
@@ -788,7 +780,7 @@ func getLayoutHeight(layout *dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayou
// convertAutoGridLayoutToPanelsWithOffset converts AutoGridLayout with a Y offset.
// Same as convertAutoGridLayoutToPanels but starts at yOffset instead of 0.
// Used when AutoGridLayout appears inside rows or tabs.
func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, dsIndex *schemaversion.DatasourceIndex, yOffset int64) ([]interface{}, error) {
func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, yOffset int64) ([]interface{}, error) {
panels := make([]interface{}, 0, len(autoGridLayout.Spec.Items))
const (
@@ -850,7 +842,7 @@ func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.Da
},
}
panel, err := convertPanelFromElement(&element, &gridItem, dsIndex)
panel, err := convertPanelFromElement(&element, &gridItem)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -876,7 +868,7 @@ func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.Da
//
// Width: 24 / maxColumnCount (default 3 columns = 8 units wide)
// Height: Predefined grid units per mode (see pixelsToGridUnits for custom)
func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, autoGridLayout *dashv2alpha1.DashboardAutoGridLayoutKind) ([]interface{}, error) {
panels := make([]interface{}, 0, len(autoGridLayout.Spec.Items))
const (
@@ -963,7 +955,7 @@ func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardEle
}
}
panel, err := convertPanelFromElement(&element, &gridItem, dsIndex)
panel, err := convertPanelFromElement(&element, &gridItem)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
}
@@ -984,11 +976,11 @@ func convertAutoGridLayoutToPanels(elements map[string]dashv2alpha1.DashboardEle
// V1 has no native tab concept, so tabs are converted to expanded row panels.
// Each tab becomes a row panel (collapsed=false, panels=[]) with its content
// flattened to the top level. Tab order is preserved in the output.
func convertTabsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind, dsIndex *schemaversion.DatasourceIndex) ([]interface{}, error) {
return convertNestedLayoutToPanels(elements, nil, tabsLayout, dsIndex, 0)
func convertTabsLayoutToPanels(elements map[string]dashv2alpha1.DashboardElement, tabsLayout *dashv2alpha1.DashboardTabsLayoutKind) ([]interface{}, error) {
return convertNestedLayoutToPanels(elements, nil, tabsLayout, 0)
}
func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem *dashv2alpha1.DashboardGridLayoutItemKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem *dashv2alpha1.DashboardGridLayoutItemKind) (map[string]interface{}, error) {
panel := make(map[string]interface{})
// Set grid position
@@ -1017,7 +1009,7 @@ func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem
}
if element.PanelKind != nil {
return convertPanelKindToV1(element.PanelKind, panel, dsIndex)
return convertPanelKindToV1(element.PanelKind, panel)
}
if element.LibraryPanelKind != nil {
@@ -1027,7 +1019,7 @@ func convertPanelFromElement(element *dashv2alpha1.DashboardElement, layoutItem
return nil, fmt.Errorf("element has neither PanelKind nor LibraryPanelKind")
}
func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[string]interface{}, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[string]interface{}) (map[string]interface{}, error) {
spec := panelKind.Spec
panel["id"] = int(spec.Id)
@@ -1069,14 +1061,14 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[
// Convert queries (targets)
targets := make([]map[string]interface{}, 0, len(spec.Data.Spec.Queries))
for _, query := range spec.Data.Spec.Queries {
target := convertPanelQueryToV1(&query, dsIndex)
target := convertPanelQueryToV1(&query)
targets = append(targets, target)
}
panel["targets"] = targets
// Detect mixed datasource - set panel.datasource to "mixed" if queries use different datasources
// This matches the frontend behavior in getPanelDataSource (layoutSerializers/utils.ts)
if mixedDS := detectMixedDatasource(spec.Data.Spec.Queries, dsIndex); mixedDS != nil {
if mixedDS := detectMixedDatasource(spec.Data.Spec.Queries); mixedDS != nil {
panel["datasource"] = mixedDS
}
@@ -1125,7 +1117,7 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[
return panel, nil
}
func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex *schemaversion.DatasourceIndex) map[string]interface{} {
func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind) map[string]interface{} {
target := make(map[string]interface{})
// Copy query spec (excluding refId, hide, datasource which are handled separately)
@@ -1150,7 +1142,7 @@ func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex
}
// Resolve datasource based on V2 input (reuse shared function)
datasource := getDataSourceForQuery(query.Spec.Datasource, query.Spec.Query.Kind, nil)
datasource := getDataSourceForQuery(query.Spec.Datasource, query.Spec.Query.Kind)
if datasource != nil {
target["datasource"] = datasource
}
@@ -1164,7 +1156,7 @@ func convertPanelQueryToV1(query *dashv2alpha1.DashboardPanelQueryKind, dsIndex
// - Else if queryKind (type) is non-empty → return {type} only
// - Else → return nil (no datasource)
// Used for variables and annotations. Panel queries use convertPanelQueryToV1Target.
func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, queryKind string, _ *schemaversion.DatasourceIndex) map[string]interface{} {
func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, queryKind string) map[string]interface{} {
// Case 1: Explicit datasource with UID provided
if explicitDS != nil && explicitDS.Uid != nil && *explicitDS.Uid != "" {
datasource := map[string]interface{}{
@@ -1195,7 +1187,7 @@ func getDataSourceForQuery(explicitDS *dashv2alpha1.DashboardDataSourceRef, quer
// Compares based on V2 input without runtime resolution:
// - If query has explicit datasource.uid → use that UID and type
// - Else → use query.Kind as type (empty UID)
func detectMixedDatasource(queries []dashv2alpha1.DashboardPanelQueryKind, _ *schemaversion.DatasourceIndex) map[string]interface{} {
func detectMixedDatasource(queries []dashv2alpha1.DashboardPanelQueryKind) map[string]interface{} {
if len(queries) == 0 {
return nil
}
@@ -1254,7 +1246,7 @@ func convertLibraryPanelKindToV1(libPanelKind *dashv2alpha1.DashboardLibraryPane
return panel, nil
}
func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsIndex *schemaversion.DatasourceIndex) []map[string]interface{} {
func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(variables))
for _, variable := range variables {
@@ -1262,7 +1254,7 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
var err error
if variable.QueryVariableKind != nil {
varMap, err = convertQueryVariableToV1(variable.QueryVariableKind, dsIndex)
varMap, err = convertQueryVariableToV1(variable.QueryVariableKind)
} else if variable.DatasourceVariableKind != nil {
varMap, err = convertDatasourceVariableToV1(variable.DatasourceVariableKind)
} else if variable.CustomVariableKind != nil {
@@ -1274,9 +1266,9 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
} else if variable.TextVariableKind != nil {
varMap, err = convertTextVariableToV1(variable.TextVariableKind)
} else if variable.GroupByVariableKind != nil {
varMap, err = convertGroupByVariableToV1(variable.GroupByVariableKind, dsIndex)
varMap, err = convertGroupByVariableToV1(variable.GroupByVariableKind)
} else if variable.AdhocVariableKind != nil {
varMap, err = convertAdhocVariableToV1(variable.AdhocVariableKind, dsIndex)
varMap, err = convertAdhocVariableToV1(variable.AdhocVariableKind)
} else if variable.SwitchVariableKind != nil {
varMap, err = convertSwitchVariableToV1(variable.SwitchVariableKind)
}
@@ -1289,7 +1281,7 @@ func convertVariablesToV1(variables []dashv2alpha1.DashboardVariableKind, dsInde
return result
}
func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind) (map[string]interface{}, error) {
spec := variable.Spec
varMap := map[string]interface{}{
"name": spec.Name,
@@ -1320,6 +1312,9 @@ func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind,
if spec.Definition != nil {
varMap["definition"] = *spec.Definition
}
if spec.RegexApplyTo != nil {
varMap["regexApplyTo"] = string(*spec.RegexApplyTo)
}
varMap["allowCustomValue"] = spec.AllowCustomValue
// Convert query - handle LEGACY_STRING_VALUE_KEY
@@ -1336,7 +1331,7 @@ func convertQueryVariableToV1(variable *dashv2alpha1.DashboardQueryVariableKind,
}
// Resolve datasource - use explicit datasource or resolve from query kind (datasource type)/default
datasource := getDataSourceForQuery(spec.Datasource, spec.Query.Kind, dsIndex)
datasource := getDataSourceForQuery(spec.Datasource, spec.Query.Kind)
if datasource != nil {
varMap["datasource"] = datasource
}
@@ -1486,7 +1481,7 @@ func convertTextVariableToV1(variable *dashv2alpha1.DashboardTextVariableKind) (
return varMap, nil
}
func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableKind) (map[string]interface{}, error) {
spec := variable.Spec
varMap := map[string]interface{}{
"name": spec.Name,
@@ -1509,7 +1504,7 @@ func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableK
}
// Resolve datasource - GroupBy variables don't have a query kind, so use empty string (will fall back to default)
datasource := getDataSourceForQuery(spec.Datasource, "", dsIndex)
datasource := getDataSourceForQuery(spec.Datasource, "")
if datasource != nil {
varMap["datasource"] = datasource
}
@@ -1517,7 +1512,7 @@ func convertGroupByVariableToV1(variable *dashv2alpha1.DashboardGroupByVariableK
return varMap, nil
}
func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind, dsIndex *schemaversion.DatasourceIndex) (map[string]interface{}, error) {
func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind) (map[string]interface{}, error) {
spec := variable.Spec
varMap := map[string]interface{}{
"name": spec.Name,
@@ -1536,7 +1531,7 @@ func convertAdhocVariableToV1(variable *dashv2alpha1.DashboardAdhocVariableKind,
varMap["allowCustomValue"] = spec.AllowCustomValue
// Resolve datasource - Adhoc variables don't have a query kind, so use empty string (will fall back to default)
datasource := getDataSourceForQuery(spec.Datasource, "", dsIndex)
datasource := getDataSourceForQuery(spec.Datasource, "")
if datasource != nil {
varMap["datasource"] = datasource
}
@@ -1663,7 +1658,7 @@ func convertSwitchVariableToV1(variable *dashv2alpha1.DashboardSwitchVariableKin
return varMap, nil
}
func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryKind, dsIndex *schemaversion.DatasourceIndex) []map[string]interface{} {
func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryKind) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(annotations))
for _, annotation := range annotations {
@@ -1686,7 +1681,7 @@ func convertAnnotationsToV1(annotations []dashv2alpha1.DashboardAnnotationQueryK
if annotation.Spec.Query != nil {
queryKind = annotation.Spec.Query.Kind
}
datasource := getDataSourceForQuery(annotation.Spec.Datasource, queryKind, dsIndex)
datasource := getDataSourceForQuery(annotation.Spec.Datasource, queryKind)
if datasource != nil {
annotationMap["datasource"] = datasource
}
@@ -767,6 +767,7 @@ func convertQueryVariableSpec_V2alpha1_to_V2beta1(in *dashv2alpha1.DashboardQuer
out.SkipUrlSync = in.SkipUrlSync
out.Description = in.Description
out.Regex = in.Regex
out.RegexApplyTo = (*dashv2beta1.DashboardVariableRegexApplyTo)(in.RegexApplyTo)
out.Sort = dashv2beta1.DashboardVariableSort(in.Sort)
out.Definition = in.Definition
out.Options = convertVariableOptions_V2alpha1_to_V2beta1(in.Options)
@@ -806,6 +806,7 @@ func convertQueryVariableSpec_V2beta1_to_V2alpha1(in *dashv2beta1.DashboardQuery
out.SkipUrlSync = in.SkipUrlSync
out.Description = in.Description
out.Regex = in.Regex
out.RegexApplyTo = (*dashv2alpha1.DashboardVariableRegexApplyTo)(in.RegexApplyTo)
out.Sort = dashv2alpha1.DashboardVariableSort(in.Sort)
out.Definition = in.Definition
out.Options = convertVariableOptions_V2beta1_to_V2alpha1(in.Options)
+7 -3
View File
@@ -61,9 +61,13 @@ type migrator struct {
func (m *migrator) init(dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) {
initOnce.Do(func() {
m.dsIndexProvider = dsIndexProvider
m.leIndexProvider = leIndexProvider
m.migrations = schemaversion.GetMigrations(dsIndexProvider, leIndexProvider)
// Wrap the provider once with 10s caching for all conversions.
// This prevents repeated DB queries across multiple conversion calls while allowing
// the cache to refresh periodically, making it suitable for long-lived singleton usage.
m.dsIndexProvider = schemaversion.WrapIndexProviderWithCache(dsIndexProvider)
// Wrap library element provider with caching as well
m.leIndexProvider = schemaversion.WrapLibraryElementProviderWithCache(leIndexProvider)
m.migrations = schemaversion.GetMigrations(m.dsIndexProvider, m.leIndexProvider)
close(m.ready)
})
}
+5 -5
View File
@@ -34,7 +34,7 @@ manifest: {
v0alpha1: {
kinds: [examplev0alpha1]
// This is explicitly set to false to keep the example app disabled by default.
// This is explicitly set to false to keep the example app disabled by default.
// It can be enabled via conf overrides, or by setting this value to true and regenerating.
served: false
}
@@ -48,14 +48,14 @@ v1alpha1: {
// served indicates whether this particular version is served by the API server.
// served should be set to false before a version is removed from the manifest entirely.
// served defaults to true if not present.
// This is explicitly set to false to keep the example app disabled by default.
// This is explicitly set to false to keep the example app disabled by default.
// It can be enabled via conf overrides, or by setting this value to true and regenerating.
served: false
// routes contains resource routes for the version, which are split into 'namespaced' and 'cluster' scoped routes.
// This allows you to add additional non-storage- and non-kind- based handlers for your app.
// These should only be used if the behavior cannot be accomplished by reconciliation on storage events or subresource routes on a kind.
routes: {
// namespaced contains namespace-scoped resource routes for the version,
// namespaced contains namespace-scoped resource routes for the version,
// which are exposed as HTTP handlers on '<version>/namespaces/<namespace>/<route>'.
namespaced: {
"/something": {
@@ -72,7 +72,7 @@ v1alpha1: {
}
}
}
// cluster contains cluster-scoped resource routes for the version,
// cluster contains cluster-scoped resource routes for the version,
// which are exposed as HTTP handlers on '<version>/<route>'.
cluster: {
"/other": {
@@ -113,4 +113,4 @@ v1alpha1: {
enabled: true
}
}
}
}
+6 -4
View File
@@ -499,8 +499,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
@@ -853,6 +853,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
@@ -1416,8 +1418,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 h1:/4/IJi5iyTdh6mqOUaASW148HQpujYiHl0Wl78dSOSc=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+7 -2
View File
@@ -1,5 +1,10 @@
include ../sdk.mk
.PHONY: generate
.PHONY: generate # Run Grafana App SDK code generation
generate: install-app-sdk update-app-sdk
@$(APP_SDK_BIN) generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none --useoldmanifestkinds
@$(APP_SDK_BIN) generate \
--source=./kinds/ \
--gogenpath=./pkg/apis \
--grouping=group \
--genoperatorstate=false \
--defencoding=none
+11 -32
View File
@@ -1,39 +1,18 @@
package investigations
// This is our Investigation definition, which contains metadata about the kind, and the kind's schema
investigation: {
investigationV0alpha1: {
kind: "Investigation"
group: "investigations.grafana.app"
apiResource: {
groupOverride: "investigations.grafana.app"
}
pluralName: "Investigations"
current: "v0alpha1"
versions: {
"v0alpha1": {
codegen: {
frontend: true
backend: true
options: {
generateObjectMeta: true
generateClient: true
k8sLike: true
package: "github.com/grafana/grafana/apps/investigations"
}
}
schema: {
// spec is the schema of our resource
spec: {
title: string
createdByProfile: #Person
hasCustomName: bool
isFavorite: bool
overviewNote: string
overviewNoteUpdatedAt: string
collectables: [...#Collectable] // +listType=atomic
viewMode: #ViewMode
}
}
schema: {
spec: {
title: string
createdByProfile: #Person
hasCustomName: bool
isFavorite: bool
overviewNote: string
overviewNoteUpdatedAt: string
collectables: [...#Collectable] // +listType=atomic
viewMode: #ViewMode
}
}
}
@@ -1,37 +1,18 @@
package investigations
investigationIndex: {
investigationIndexV0alpha1:{
kind: "InvestigationIndex"
group: "investigations.grafana.app"
apiResource: {
groupOverride: "investigations.grafana.app"
}
pluralName: "InvestigationIndexes"
current: "v0alpha1"
versions: {
"v0alpha1": {
codegen: {
frontend: true
backend: true
options: {
generateObjectMeta: true
generateClient: true
k8sLike: true
package: "github.com/grafana/grafana/apps/investigations"
}
}
schema: {
spec: {
// Title of the index, e.g. 'Favorites' or 'My Investigations'
title: string
schema: {
spec: {
// Title of the index, e.g. 'Favorites' or 'My Investigations'
title: string
// The Person who owns this investigation index
owner: #Person
// The Person who owns this investigation index
owner: #Person
// Array of investigation summaries
investigationSummaries: [...#InvestigationSummary] // +listType=atomic
}
}
// Array of investigation summaries
investigationSummaries: [...#InvestigationSummary] // +listType=atomic
}
}
}
+13 -5
View File
@@ -3,8 +3,16 @@ package investigations
manifest: {
appName: "investigations"
groupOverride: "investigations.grafana.app"
kinds: [
investigation,
investigationIndex,
]
}
versions: {
"v0alpha1": {
codegen: {
ts: {enabled: false}
go: {enabled: true}
}
kinds: [
investigationV0alpha1,
investigationIndexV0alpha1,
]
}
}
}
@@ -4,7 +4,6 @@ import (
"context"
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type InvestigationClient struct {
@@ -76,24 +75,6 @@ func (c *InvestigationClient) Patch(ctx context.Context, identifier resource.Ide
return c.client.Patch(ctx, identifier, req, opts)
}
func (c *InvestigationClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus InvestigationStatus, opts resource.UpdateOptions) (*Investigation, error) {
return c.client.Update(ctx, &Investigation{
TypeMeta: metav1.TypeMeta{
Kind: InvestigationKind().Kind(),
APIVersion: GroupVersion.Identifier(),
},
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: opts.ResourceVersion,
Namespace: identifier.Namespace,
Name: identifier.Name,
},
Status: newStatus,
}, resource.UpdateOptions{
Subresource: "status",
ResourceVersion: opts.ResourceVersion,
})
}
func (c *InvestigationClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
return c.client.Delete(ctx, identifier, opts)
}
@@ -21,8 +21,6 @@ type Investigation struct {
// Spec is the spec of the Investigation
Spec InvestigationSpec `json:"spec" yaml:"spec"`
Status InvestigationStatus `json:"status" yaml:"status"`
}
func (o *Investigation) GetSpec() any {
@@ -39,15 +37,11 @@ func (o *Investigation) SetSpec(spec any) error {
}
func (o *Investigation) GetSubresources() map[string]any {
return map[string]any{
"status": o.Status,
}
return map[string]any{}
}
func (o *Investigation) GetSubresource(name string) (any, bool) {
switch name {
case "status":
return o.Status, true
default:
return nil, false
}
@@ -55,13 +49,6 @@ func (o *Investigation) GetSubresource(name string) (any, bool) {
func (o *Investigation) SetSubresource(name string, value any) error {
switch name {
case "status":
cast, ok := value.(InvestigationStatus)
if !ok {
return fmt.Errorf("cannot set status type %#v, not of type InvestigationStatus", value)
}
o.Status = cast
return nil
default:
return fmt.Errorf("subresource '%s' does not exist", name)
}
@@ -233,7 +220,6 @@ func (o *Investigation) DeepCopyInto(dst *Investigation) {
dst.TypeMeta.Kind = o.TypeMeta.Kind
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
o.Spec.DeepCopyInto(&dst.Spec)
o.Status.DeepCopyInto(&dst.Status)
}
// Interface compliance compile-time check
@@ -305,15 +291,3 @@ func (s *InvestigationSpec) DeepCopy() *InvestigationSpec {
func (s *InvestigationSpec) DeepCopyInto(dst *InvestigationSpec) {
resource.CopyObjectInto(dst, s)
}
// DeepCopy creates a full deep copy of InvestigationStatus
func (s *InvestigationStatus) DeepCopy() *InvestigationStatus {
cpy := &InvestigationStatus{}
s.DeepCopyInto(cpy)
return cpy
}
// DeepCopyInto deep copies InvestigationStatus into another InvestigationStatus object
func (s *InvestigationStatus) DeepCopyInto(dst *InvestigationStatus) {
resource.CopyObjectInto(dst, s)
}
@@ -84,7 +84,6 @@ func NewInvestigationViewMode() *InvestigationViewMode {
return &InvestigationViewMode{}
}
// spec is the schema of our resource
// +k8s:openapi-gen=true
type InvestigationSpec struct {
Title string `json:"title"`
@@ -1,44 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
// +k8s:openapi-gen=true
type InvestigationstatusOperatorState struct {
// lastEvaluation is the ResourceVersion last evaluated
LastEvaluation string `json:"lastEvaluation"`
// state describes the state of the lastEvaluation.
// It is limited to three possible states for machine evaluation.
State InvestigationStatusOperatorStateState `json:"state"`
// descriptiveState is an optional more descriptive state field which has no requirements on format
DescriptiveState *string `json:"descriptiveState,omitempty"`
// details contains any extra information that is operator-specific
Details map[string]interface{} `json:"details,omitempty"`
}
// NewInvestigationstatusOperatorState creates a new InvestigationstatusOperatorState object.
func NewInvestigationstatusOperatorState() *InvestigationstatusOperatorState {
return &InvestigationstatusOperatorState{}
}
// +k8s:openapi-gen=true
type InvestigationStatus struct {
// operatorStates is a map of operator ID to operator state evaluations.
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
OperatorStates map[string]InvestigationstatusOperatorState `json:"operatorStates,omitempty"`
// additionalFields is reserved for future use
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
}
// NewInvestigationStatus creates a new InvestigationStatus object.
func NewInvestigationStatus() *InvestigationStatus {
return &InvestigationStatus{}
}
// +k8s:openapi-gen=true
type InvestigationStatusOperatorStateState string
const (
InvestigationStatusOperatorStateStateSuccess InvestigationStatusOperatorStateState = "success"
InvestigationStatusOperatorStateStateInProgress InvestigationStatusOperatorStateState = "in_progress"
InvestigationStatusOperatorStateStateFailed InvestigationStatusOperatorStateState = "failed"
)
@@ -4,7 +4,6 @@ import (
"context"
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type InvestigationIndexClient struct {
@@ -76,24 +75,6 @@ func (c *InvestigationIndexClient) Patch(ctx context.Context, identifier resourc
return c.client.Patch(ctx, identifier, req, opts)
}
func (c *InvestigationIndexClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus InvestigationIndexStatus, opts resource.UpdateOptions) (*InvestigationIndex, error) {
return c.client.Update(ctx, &InvestigationIndex{
TypeMeta: metav1.TypeMeta{
Kind: InvestigationIndexKind().Kind(),
APIVersion: GroupVersion.Identifier(),
},
ObjectMeta: metav1.ObjectMeta{
ResourceVersion: opts.ResourceVersion,
Namespace: identifier.Namespace,
Name: identifier.Name,
},
Status: newStatus,
}, resource.UpdateOptions{
Subresource: "status",
ResourceVersion: opts.ResourceVersion,
})
}
func (c *InvestigationIndexClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
return c.client.Delete(ctx, identifier, opts)
}
@@ -21,8 +21,6 @@ type InvestigationIndex struct {
// Spec is the spec of the InvestigationIndex
Spec InvestigationIndexSpec `json:"spec" yaml:"spec"`
Status InvestigationIndexStatus `json:"status" yaml:"status"`
}
func (o *InvestigationIndex) GetSpec() any {
@@ -39,15 +37,11 @@ func (o *InvestigationIndex) SetSpec(spec any) error {
}
func (o *InvestigationIndex) GetSubresources() map[string]any {
return map[string]any{
"status": o.Status,
}
return map[string]any{}
}
func (o *InvestigationIndex) GetSubresource(name string) (any, bool) {
switch name {
case "status":
return o.Status, true
default:
return nil, false
}
@@ -55,13 +49,6 @@ func (o *InvestigationIndex) GetSubresource(name string) (any, bool) {
func (o *InvestigationIndex) SetSubresource(name string, value any) error {
switch name {
case "status":
cast, ok := value.(InvestigationIndexStatus)
if !ok {
return fmt.Errorf("cannot set status type %#v, not of type InvestigationIndexStatus", value)
}
o.Status = cast
return nil
default:
return fmt.Errorf("subresource '%s' does not exist", name)
}
@@ -233,7 +220,6 @@ func (o *InvestigationIndex) DeepCopyInto(dst *InvestigationIndex) {
dst.TypeMeta.Kind = o.TypeMeta.Kind
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
o.Spec.DeepCopyInto(&dst.Spec)
o.Status.DeepCopyInto(&dst.Status)
}
// Interface compliance compile-time check
@@ -305,15 +291,3 @@ func (s *InvestigationIndexSpec) DeepCopy() *InvestigationIndexSpec {
func (s *InvestigationIndexSpec) DeepCopyInto(dst *InvestigationIndexSpec) {
resource.CopyObjectInto(dst, s)
}
// DeepCopy creates a full deep copy of InvestigationIndexStatus
func (s *InvestigationIndexStatus) DeepCopy() *InvestigationIndexStatus {
cpy := &InvestigationIndexStatus{}
s.DeepCopyInto(cpy)
return cpy
}
// DeepCopyInto deep copies InvestigationIndexStatus into another InvestigationIndexStatus object
func (s *InvestigationIndexStatus) DeepCopyInto(dst *InvestigationIndexStatus) {
resource.CopyObjectInto(dst, s)
}
+2 -2
View File
@@ -20,10 +20,10 @@ import (
)
var (
rawSchemaInvestigationv0alpha1 = []byte(`{"Collectable":{"additionalProperties":false,"description":"Collectable represents an item collected during investigation","properties":{"createdAt":{"type":"string"},"datasource":{"$ref":"#/components/schemas/DatasourceRef"},"fieldConfig":{"type":"string"},"id":{"type":"string"},"logoPath":{"type":"string"},"note":{"type":"string"},"noteUpdatedAt":{"type":"string"},"origin":{"type":"string"},"queries":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"timeRange":{"$ref":"#/components/schemas/TimeRange"},"title":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["id","createdAt","title","origin","type","queries","timeRange","datasource","url","note","noteUpdatedAt","fieldConfig"],"type":"object"},"DatasourceRef":{"additionalProperties":false,"description":"DatasourceRef is a reference to a datasource","properties":{"uid":{"type":"string"}},"required":["uid"],"type":"object"},"Investigation":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"Person":{"additionalProperties":false,"description":"Person represents a user profile with basic information","properties":{"gravatarUrl":{"description":"URL to user's Gravatar image","type":"string"},"name":{"description":"Display name of the user","type":"string"},"uid":{"description":"Unique identifier for the user","type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"TimeRange":{"additionalProperties":false,"description":"TimeRange represents a time range with both absolute and relative values","properties":{"from":{"type":"string"},"raw":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"to":{"type":"string"}},"required":["from","to","raw"],"type":"object"},"ViewMode":{"additionalProperties":false,"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"},"spec":{"additionalProperties":false,"description":"spec is the schema of our resource","properties":{"collectables":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Collectable"},"type":"array"},"createdByProfile":{"$ref":"#/components/schemas/Person"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"$ref":"#/components/schemas/ViewMode"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","collectables","viewMode"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
rawSchemaInvestigationv0alpha1 = []byte(`{"Collectable":{"additionalProperties":false,"description":"Collectable represents an item collected during investigation","properties":{"createdAt":{"type":"string"},"datasource":{"$ref":"#/components/schemas/DatasourceRef"},"fieldConfig":{"type":"string"},"id":{"type":"string"},"logoPath":{"type":"string"},"note":{"type":"string"},"noteUpdatedAt":{"type":"string"},"origin":{"type":"string"},"queries":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"timeRange":{"$ref":"#/components/schemas/TimeRange"},"title":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["id","createdAt","title","origin","type","queries","timeRange","datasource","url","note","noteUpdatedAt","fieldConfig"],"type":"object"},"DatasourceRef":{"additionalProperties":false,"description":"DatasourceRef is a reference to a datasource","properties":{"uid":{"type":"string"}},"required":["uid"],"type":"object"},"Investigation":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"Person":{"additionalProperties":false,"description":"Person represents a user profile with basic information","properties":{"gravatarUrl":{"description":"URL to user's Gravatar image","type":"string"},"name":{"description":"Display name of the user","type":"string"},"uid":{"description":"Unique identifier for the user","type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"TimeRange":{"additionalProperties":false,"description":"TimeRange represents a time range with both absolute and relative values","properties":{"from":{"type":"string"},"raw":{"additionalProperties":false,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"to":{"type":"string"}},"required":["from","to","raw"],"type":"object"},"ViewMode":{"additionalProperties":false,"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"collectables":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Collectable"},"type":"array"},"createdByProfile":{"$ref":"#/components/schemas/Person"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"$ref":"#/components/schemas/ViewMode"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","collectables","viewMode"],"type":"object"}}`)
versionSchemaInvestigationv0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaInvestigationv0alpha1, &versionSchemaInvestigationv0alpha1)
rawSchemaInvestigationIndexv0alpha1 = []byte(`{"CollectableSummary":{"additionalProperties":false,"properties":{"id":{"type":"string"},"logoPath":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"}},"required":["id","title","logoPath","origin"],"type":"object"},"InvestigationIndex":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"InvestigationSummary":{"additionalProperties":false,"description":"Type definition for investigation summaries","properties":{"collectableSummaries":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/CollectableSummary"},"type":"array"},"createdByProfile":{"$ref":"#/components/schemas/Person"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"$ref":"#/components/schemas/ViewMode"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","viewMode","collectableSummaries"],"type":"object"},"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"Person":{"additionalProperties":false,"description":"Person represents a user profile with basic information","properties":{"gravatarUrl":{"description":"URL to user's Gravatar image","type":"string"},"name":{"description":"Display name of the user","type":"string"},"uid":{"description":"Unique identifier for the user","type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"ViewMode":{"additionalProperties":false,"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"investigationSummaries":{"description":"Array of investigation summaries\n+listType=atomic","items":{"$ref":"#/components/schemas/InvestigationSummary"},"type":"array"},"owner":{"$ref":"#/components/schemas/Person","description":"The Person who owns this investigation index"},"title":{"description":"Title of the index, e.g. 'Favorites' or 'My Investigations'","type":"string"}},"required":["title","owner","investigationSummaries"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
rawSchemaInvestigationIndexv0alpha1 = []byte(`{"CollectableSummary":{"additionalProperties":false,"properties":{"id":{"type":"string"},"logoPath":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"}},"required":["id","title","logoPath","origin"],"type":"object"},"InvestigationIndex":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"InvestigationSummary":{"additionalProperties":false,"description":"Type definition for investigation summaries","properties":{"collectableSummaries":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/CollectableSummary"},"type":"array"},"createdByProfile":{"$ref":"#/components/schemas/Person"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"$ref":"#/components/schemas/ViewMode"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","viewMode","collectableSummaries"],"type":"object"},"Person":{"additionalProperties":false,"description":"Person represents a user profile with basic information","properties":{"gravatarUrl":{"description":"URL to user's Gravatar image","type":"string"},"name":{"description":"Display name of the user","type":"string"},"uid":{"description":"Unique identifier for the user","type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"ViewMode":{"additionalProperties":false,"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"investigationSummaries":{"description":"Array of investigation summaries\n+listType=atomic","items":{"$ref":"#/components/schemas/InvestigationSummary"},"type":"array"},"owner":{"$ref":"#/components/schemas/Person","description":"The Person who owns this investigation index"},"title":{"description":"Title of the index, e.g. 'Favorites' or 'My Investigations'","type":"string"}},"required":["title","owner","investigationSummaries"],"type":"object"}}`)
versionSchemaInvestigationIndexv0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaInvestigationIndexv0alpha1, &versionSchemaInvestigationIndexv0alpha1)
)
+9
View File
@@ -0,0 +1,9 @@
include ../sdk.mk
.PHONY: generate # Run Grafana App SDK code generation
generate: install-app-sdk update-app-sdk
@$(APP_SDK_BIN) generate \
--source=./kinds/ \
--gogenpath=./pkg/apis \
--grouping=group \
--defencoding=none
+92
View File
@@ -0,0 +1,92 @@
module github.com/grafana/grafana/apps/quotas
go 1.25.3
require (
github.com/grafana/grafana-app-sdk v0.48.5
github.com/grafana/grafana-app-sdk/logging v0.48.3
k8s.io/apimachinery v0.34.2
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/getkin/kin-openapi v0.133.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.22.1 // indirect
github.com/go-openapi/jsonreference v0.21.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
github.com/go-test/deep v1.1.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/onsi/gomega v1.36.2 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.3 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/woodsbury/decimal128 v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.33.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.14.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
google.golang.org/grpc v1.77.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.34.2 // indirect
k8s.io/apiextensions-apiserver v0.34.2 // indirect
k8s.io/client-go v0.34.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
+252
View File
@@ -0,0 +1,252 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU=
github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU=
github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/grafana-app-sdk v0.48.2 h1:CQQDhwo1fWaXQVKvxxOcK6azbuY3E2TgJHNAZlYYn7U=
github.com/grafana/grafana-app-sdk v0.48.2/go.mod h1:LDOvQ7OOyHLcXdSa0InATCa5OMoYAd6E1+rGLrMgHuk=
github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0=
github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM=
github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y=
github.com/grafana/grafana-app-sdk/logging v0.48.1 h1:veM0X5LAPyN3KsDLglWjIofndbGuf7MqnrDuDN+F/Ng=
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q=
github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU=
github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090 h1:d8Nakh1G+ur7+P3GcMjpRDEkoLUcLW2iU92XVqR+XMQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=
k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=
k8s.io/apiextensions-apiserver v0.34.2 h1:WStKftnGeoKP4AZRz/BaAAEJvYp4mlZGN0UCv+uvsqo=
k8s.io/apiextensions-apiserver v0.34.2/go.mod h1:398CJrsgXF1wytdaanynDpJ67zG4Xq7yj91GrmYN2SE=
k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=
k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=
k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
+2
View File
@@ -0,0 +1,2 @@
module: "github.com/grafana/grafana/apps/quotas/kinds"
language: version: "v0.8.2"
+92
View File
@@ -0,0 +1,92 @@
package kinds
manifest: {
// appName is the unique name of your app. It is used to reference the app from other config objects,
// and to generate the group used by your app in the app platform API.
appName: "quotas"
// groupOverride can be used to specify a non-appName-based API group.
// By default, an app's API group is LOWER(REPLACE(appName, '-', '')).ext.grafana.com,
// but there are cases where this needs to be changed.
// Keep in mind that changing this after an app is deployed can cause problems with clients and/or kind data.
groupOverride: "quotas.grafana.app"
// versions is a map of versions supported by your app. Version names should follow the format "v<integer>" or
// "v<integer>(alpha|beta)<integer>". Each version contains the kinds your app manages for that version.
// If your app needs access to kinds managed by another app, use permissions.accessKinds to allow your app access.
versions: {
"v0alpha1": v0alpha1
}
// extraPermissions contains any additional permissions your app may require to function.
// Your app will always have all permissions for each kind it manages (the items defined in 'kinds').
extraPermissions: {
// If your app needs access to additional kinds supplied by other apps, you can list them here
accessKinds: [
// Here is an example for your app accessing the playlist kind for reads and watch
// {
// group: "playlist.grafana.app"
// resource: "playlists"
// actions: ["get","list","watch"]
// }
]
}
}
// v1alpha1 is the v1alpha1 version of the app's API.
// It includes kinds which the v1alpha1 API serves, and (future) custom routes served globally from the v1alpha1 version.
v0alpha1: {
// kinds is the list of kinds served by this version
kinds: []
// [OPTIONAL]
// served indicates whether this particular version is served by the API server.
// served should be set to false before a version is removed from the manifest entirely.
// served defaults to true if not present.
served: true
routes: {
// namespaced contains namespace-scoped resource routes for the version,
// which are exposed as HTTP handlers on '<version>/namespaces/<namespace>/<route>'.
namespaced: {
"/usage": {
"GET": {
response: {
namespace: string
resource: string
group: string
usage: int64
limit: int64
}
request: {
query: {
group: string
resource: string
}
}
}
}
}
}
// [OPTIONAL]
// Codegen is a trait that tells the grafana-app-sdk, or other code generation tooling, how to process this kind.
// If not present, default values within the codegen trait are used.
// If you wish to specify codegen per-version, put this section in the version's object
// (for example, <no value>v1alpha1) instead.
codegen: {
// [OPTIONAL]
// ts contains TypeScript code generation properties for the kind
ts: {
// [OPTIONAL]
// enabled indicates whether the CLI should generate front-end TypeScript code for the kind.
// Defaults to true if not present.
enabled: true
}
// [OPTIONAL]
// go contains go code generation properties for the kind
go: {
// [OPTIONAL]
// enabled indicates whether the CLI should generate back-end go code for the kind.
// Defaults to true if not present.
enabled: true
}
}
}
@@ -0,0 +1,33 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type GetUsageRequestParamsObject struct {
metav1.TypeMeta `json:",inline"`
GetUsageRequestParams `json:",inline"`
}
func NewGetUsageRequestParamsObject() *GetUsageRequestParamsObject {
return &GetUsageRequestParamsObject{}
}
func (o *GetUsageRequestParamsObject) DeepCopyObject() runtime.Object {
dst := NewGetUsageRequestParamsObject()
o.DeepCopyInto(dst)
return dst
}
func (o *GetUsageRequestParamsObject) DeepCopyInto(dst *GetUsageRequestParamsObject) {
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
dst.TypeMeta.Kind = o.TypeMeta.Kind
dstGetUsageRequestParams := GetUsageRequestParams{}
_ = resource.CopyObjectInto(&dstGetUsageRequestParams, &o.GetUsageRequestParams)
}
var _ runtime.Object = NewGetUsageRequestParamsObject()
@@ -0,0 +1,13 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
type GetUsageRequestParams struct {
Group string `json:"group"`
Resource string `json:"resource"`
}
// NewGetUsageRequestParams creates a new GetUsageRequestParams object.
func NewGetUsageRequestParams() *GetUsageRequestParams {
return &GetUsageRequestParams{}
}
@@ -0,0 +1,17 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
// +k8s:openapi-gen=true
type GetUsageBody struct {
Namespace string `json:"namespace"`
Resource string `json:"resource"`
Group string `json:"group"`
Usage int64 `json:"usage"`
Limit int64 `json:"limit"`
}
// NewGetUsageBody creates a new GetUsageBody object.
func NewGetUsageBody() *GetUsageBody {
return &GetUsageBody{}
}
@@ -0,0 +1,37 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// +k8s:openapi-gen=true
type GetUsage struct {
metav1.TypeMeta `json:",inline"`
GetUsageBody `json:",inline"`
}
func NewGetUsage() *GetUsage {
return &GetUsage{}
}
func (t *GetUsageBody) DeepCopyInto(dst *GetUsageBody) {
_ = resource.CopyObjectInto(dst, t)
}
func (o *GetUsage) DeepCopyObject() runtime.Object {
dst := NewGetUsage()
o.DeepCopyInto(dst)
return dst
}
func (o *GetUsage) DeepCopyInto(dst *GetUsage) {
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
dst.TypeMeta.Kind = o.TypeMeta.Kind
o.GetUsageBody.DeepCopyInto(&dst.GetUsageBody)
}
var _ runtime.Object = NewGetUsage()
+213
View File
@@ -0,0 +1,213 @@
//
// This file is generated by grafana-app-sdk
// DO NOT EDIT
//
package apis
import (
"fmt"
"strings"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/resource"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/validation/spec"
v0alpha1 "github.com/grafana/grafana/apps/quotas/pkg/apis/quotas/v0alpha1"
)
var appManifestData = app.ManifestData{
AppName: "quotas",
Group: "quotas.grafana.app",
PreferredVersion: "v0alpha1",
Versions: []app.ManifestVersion{
{
Name: "v0alpha1",
Served: true,
Kinds: []app.ManifestVersionKind{},
Routes: app.ManifestVersionRoutes{
Namespaced: map[string]spec3.PathProps{
"/usage": {
Get: &spec3.Operation{
OperationProps: spec3.OperationProps{
OperationId: "getUsage",
Parameters: []*spec3.Parameter{
{
ParameterProps: spec3.ParameterProps{
Name: "group",
In: "query",
Required: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
},
},
{
ParameterProps: spec3.ParameterProps{
Name: "resource",
In: "query",
Required: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
},
},
},
Responses: &spec3.Responses{
ResponsesProps: spec3.ResponsesProps{
Default: &spec3.Response{
ResponseProps: spec3.ResponseProps{
Description: "Default OK response",
Content: map[string]*spec3.MediaType{
"application/json": {
MediaTypeProps: spec3.MediaTypeProps{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"apiVersion": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
},
},
"group": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
"kind": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
},
},
"limit": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
},
},
"namespace": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
"resource": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
},
},
"usage": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
},
},
},
Required: []string{
"namespace",
"resource",
"group",
"usage",
"limit",
"apiVersion",
"kind",
},
}},
}},
},
},
},
}},
},
},
},
},
Cluster: map[string]spec3.PathProps{},
Schemas: map[string]spec.Schema{},
},
},
},
}
func LocalManifest() app.Manifest {
return app.NewEmbeddedManifest(appManifestData)
}
func RemoteManifest() app.Manifest {
return app.NewAPIServerManifest("quotas")
}
var kindVersionToGoType = map[string]resource.Kind{}
// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists.
// If there is no association for the provided Kind and Version, exists will return false.
func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) {
goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)]
return goType, exists
}
var customRouteToGoResponseType = map[string]any{
"v0alpha1||<namespace>/usage|GET": v0alpha1.GetUsage{},
}
// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists.
// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths.
// If there is no association for the provided kind, version, custom route path, and method, exists will return false.
// Resource routes (those without a kind) should prefix their route with "<namespace>/" if the route is namespaced (otherwise the route is assumed to be cluster-scope)
func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) {
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
return goType, exists
}
var customRouteToGoParamsType = map[string]runtime.Object{}
func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) {
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
return goType, exists
}
var customRouteToGoRequestBodyType = map[string]any{}
func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) {
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
return goType, exists
}
type GoTypeAssociator struct{}
func NewGoTypeAssociator() *GoTypeAssociator {
return &GoTypeAssociator{}
}
func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) {
return ManifestGoTypeAssociator(kind, version)
}
func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) {
return ManifestCustomRouteResponsesAssociator(kind, version, path, verb)
}
func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) {
return ManifestCustomRouteQueryAssociator(kind, version, path, verb)
}
func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) {
return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb)
}
+123
View File
@@ -0,0 +1,123 @@
package app
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-app-sdk/operator"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
unifiedStorage "github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana-app-sdk/simple"
quotasv0alpha1 "github.com/grafana/grafana/apps/quotas/pkg/apis/quotas/v0alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type QuotasAppConfig struct {
ResourceClient unifiedStorage.ResourceClient
}
type QuotasHandler struct {
ResourceClient unifiedStorage.ResourceClient
}
func NewQuotasHandler(cfg *QuotasAppConfig) *QuotasHandler {
return &QuotasHandler{
ResourceClient: cfg.ResourceClient,
}
}
// GetQuota handles requests for the GET /usage resource route
func (h *QuotasHandler) GetQuota(ctx context.Context, writer app.CustomRouteResponseWriter, request *app.CustomRouteRequest) error {
if !request.URL.Query().Has("group") {
// TODO its returning a 500 instead of 400 bad request
writer.WriteHeader(http.StatusBadRequest)
return fmt.Errorf("missing required query parameters: group")
}
if !request.URL.Query().Has("resource") {
writer.WriteHeader(http.StatusBadRequest)
return fmt.Errorf("missing required query parameters: resource")
}
group := request.URL.Query().Get("group")
res := request.URL.Query().Get("resource")
quotaReq := &resourcepb.QuotaUsageRequest{
Key: &resourcepb.ResourceKey{
Namespace: request.ResourceIdentifier.Namespace,
Group: group,
Resource: res,
},
}
quota, err := h.ResourceClient.GetQuotaUsage(ctx, quotaReq)
if err != nil {
return err
}
writer.Header().Set("Content-Type", "application/json")
return json.NewEncoder(writer).Encode(quotasv0alpha1.GetUsage{
TypeMeta: metav1.TypeMeta{
APIVersion: "quotas.grafana.com/v0alpha1",
Kind: "Quotas",
},
GetUsageBody: quotasv0alpha1.GetUsageBody{
Namespace: request.ResourceIdentifier.Namespace,
Group: group,
Resource: res,
Usage: quota.Usage,
Limit: quota.Limit,
},
})
}
func New(cfg app.Config) (app.App, error) {
appConfig, ok := cfg.SpecificConfig.(*QuotasAppConfig)
if !ok {
return nil, fmt.Errorf("expected QuotasAppConfig but got %T", cfg.SpecificConfig)
}
handler := NewQuotasHandler(appConfig)
simpleConfig := simple.AppConfig{
Name: "quotas",
KubeConfig: cfg.KubeConfig,
InformerConfig: simple.AppInformerConfig{
InformerOptions: operator.InformerOptions{
ErrorHandler: func(ctx context.Context, err error) {
logging.FromContext(ctx).Error("Informer processing error", "error", err)
},
},
},
ManagedKinds: []simple.AppManagedKind{},
VersionedCustomRoutes: map[string]simple.AppVersionRouteHandlers{
"v0alpha1": {
{
Namespaced: true,
Path: "usage",
Method: "GET",
}: handler.GetQuota,
},
},
}
a, err := simple.NewApp(simpleConfig)
if err != nil {
return nil, err
}
err = a.ValidateManifest(cfg.ManifestData)
if err != nil {
return nil, err
}
return a, nil
}
func GetKinds() map[schema.GroupVersion][]resource.Kind {
return map[schema.GroupVersion][]resource.Kind{}
}
+71
View File
@@ -0,0 +1,71 @@
package app
import (
"context"
"net/http/httptest"
"net/url"
"testing"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestGetQuota(t *testing.T) {
t.Run("will return error when resource param is missing", func(t *testing.T) {
clientMock := resource.NewMockResourceClient(t)
handler := NewQuotasHandler(&QuotasAppConfig{
ResourceClient: clientMock,
})
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?group=dashboard.grafana.app")
require.NoError(t, err)
req := &app.CustomRouteRequest{
URL: url,
Method: "GET",
}
recorder := &httptest.ResponseRecorder{}
err = handler.GetQuota(context.Background(), recorder, req)
require.Error(t, err)
})
t.Run("will return error when group param is missing", func(t *testing.T) {
clientMock := resource.NewMockResourceClient(t)
handler := NewQuotasHandler(&QuotasAppConfig{
ResourceClient: clientMock,
})
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?resource=dashboards")
require.NoError(t, err)
req := &app.CustomRouteRequest{
URL: url,
Method: "GET",
}
recorder := &httptest.ResponseRecorder{}
err = handler.GetQuota(context.Background(), recorder, req)
require.Error(t, err)
})
t.Run("will return quotas response when params are valid", func(t *testing.T) {
clientMock := resource.NewMockResourceClient(t)
clientMock.On("GetQuotaUsage", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.QuotaUsageResponse{
Error: nil,
Usage: 1,
Limit: 2,
}, nil)
handler := NewQuotasHandler(&QuotasAppConfig{
ResourceClient: clientMock,
})
url, err := url.Parse("http://localhost:3000/apis/quotas.grafana.app/v0alpha1/namespaces/stacks-1/usage?group=dashboard.grafana.app&resource=dashboards")
require.NoError(t, err)
req := &app.CustomRouteRequest{
URL: url,
Method: "GET",
}
recorder := &httptest.ResponseRecorder{}
err = handler.GetQuota(context.Background(), recorder, req)
require.NoError(t, err)
require.Equal(t, 200, recorder.Code)
})
}
@@ -0,0 +1,49 @@
/*
* This file was generated by grafana-app-sdk. DO NOT EDIT.
*/
import { Spec } from './types.spec.gen';
import { Status } from './types.status.gen';
export interface Metadata {
name: string;
namespace: string;
generateName?: string;
selfLink?: string;
uid?: string;
resourceVersion?: string;
generation?: number;
creationTimestamp?: string;
deletionTimestamp?: string;
deletionGracePeriodSeconds?: number;
labels?: Record<string, string>;
annotations?: Record<string, string>;
ownerReferences?: OwnerReference[];
finalizers?: string[];
managedFields?: ManagedFieldsEntry[];
}
export interface OwnerReference {
apiVersion: string;
kind: string;
name: string;
uid: string;
controller?: boolean;
blockOwnerDeletion?: boolean;
}
export interface ManagedFieldsEntry {
manager?: string;
operation?: string;
apiVersion?: string;
time?: string;
fieldsType?: string;
subresource?: string;
}
export interface Quota {
kind: string;
apiVersion: string;
metadata: Metadata;
spec: Spec;
status: Status;
}
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// metadata contains embedded CommonMetadata and can be extended with custom string fields
// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
// without external reference as using the CommonMetadata reference breaks thema codegen.
export interface Metadata {
updateTimestamp: string;
createdBy: string;
uid: string;
creationTimestamp: string;
deletionTimestamp?: string;
finalizers: string[];
resourceVersion: string;
generation: number;
updatedBy: string;
labels: Record<string, string>;
}
export const defaultMetadata = (): Metadata => ({
updateTimestamp: "",
createdBy: "",
uid: "",
creationTimestamp: "",
finalizers: [],
resourceVersion: "",
generation: 0,
updatedBy: "",
labels: {},
});
@@ -0,0 +1,14 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface Spec {
count: string;
limit: string;
kind: string;
}
export const defaultSpec = (): Spec => ({
count: "",
limit: "",
kind: "",
});
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface OperatorState {
// lastEvaluation is the ResourceVersion last evaluated
lastEvaluation: string;
// state describes the state of the lastEvaluation.
// It is limited to three possible states for machine evaluation.
state: "success" | "in_progress" | "failed";
// descriptiveState is an optional more descriptive state field which has no requirements on format
descriptiveState?: string;
// details contains any extra information that is operator-specific
details?: Record<string, any>;
}
export const defaultOperatorState = (): OperatorState => ({
lastEvaluation: "",
state: "success",
});
export interface Status {
// operatorStates is a map of operator ID to operator state evaluations.
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
operatorStates?: Record<string, OperatorState>;
// additionalFields is reserved for future use
additionalFields?: Record<string, any>;
}
export const defaultStatus = (): Status => ({
});
@@ -1,243 +0,0 @@
---
description: A reference for the JSON dashboard schemas used with Observability as Code, including the experimental V2 schema.
keywords:
- configuration
- as code
- dashboards
- git integration
- git sync
- github
labels:
products:
- cloud
- enterprise
- oss
title: JSON schema v2
weight: 500
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/
aliases:
- ../../observability-as-code/schema-v2/ # /docs/grafana/next/observability-as-code/schema-v2/
---
# Dashboard JSON schema v2
{{< admonition type="caution" >}}
Dashboard JSON schema v2 is an [experimental](https://grafana.com/docs/release-life-cycle/) feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
**Do not enable this feature in production environments as it may result in the irreversible loss of data.**
{{< /admonition >}}
Grafana dashboards are represented as JSON objects that store metadata, panels, variables, and settings.
Observability as Code works with all versions of the JSON model, and it's fully compatible with version 2.
## Before you begin
Schema v2 is automatically enabled with the Dynamic Dashboards feature toggle.
To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
It also requires the new dashboards API feature toggle, `kubernetesDashboards`, to be enabled as well.
For more information on how dashboards behave depending on your feature flag configuration, refer to [Notes and limitations](#notes-and-limitations).
## Accessing the JSON Model
To view the JSON representation of a dashboard:
1. Toggle on the edit mode switch in the top-right corner of the dashboard.
1. Click the gear icon in the top navigation bar to go to **Settings**.
1. Select the **JSON Model** tab.
1. Copy or edit the JSON structure as needed.
## JSON fields
```json
{
"annotations": [],
"cursorSync": "Off",
"editable": true,
"elements": {},
"layout": {
"kind": GridLayout, // Can also be AutoGridLayout, RowsLayout, or TabsLayout
"spec": {
"items": []
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [], // Tags associated with the dashboard.
"timeSettings": {
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"fiscalYearStartMonth": 0,
"from": "now-6h",
"hideTimepicker": false,
"timezone": "browser",
"to": "now"
},
"title": "",
"variables": []
},
```
The dashboard JSON sample shown uses the default `GridLayoutKind`.
The JSON in a new dashboard for the other three layout options, `AutoGridLayout`, `RowsLayout`, and `TabsLayout`, are as follows:
**`AutoGridLayout`**
```json
"layout": {
"kind": "AutoGridLayout",
"spec": {
"columnWidthMode": "standard",
"items": [],
"fillScreen": false,
"maxColumnCount": 3,
"rowHeightMode": "standard"
}
},
```
**`RowsLayout`**
```json
"layout": {
"kind": "RowsLayout",
"spec": {
"rows": []
},
```
**`TabsLayout`**
```json
"layout": {
"kind": "TabsLayout",
"spec": {
"tabs": []
},
```
### `DashboardSpec`
The following table explains the usage of the dashboard JSON fields.
The table includes default and other fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ------------ | ------------------------------------------------------------------------- |
| annotations | Contains the list of annotations that are associated with the dashboard. |
| cursorSync | Dashboard cursor sync behavior.<ul><li>`Off` - No shared crosshair or tooltip (default)</li><li>`Crosshair` - Shared crosshair</li><li>`Tooltip` - Shared crosshair and shared tooltip</li></ul> |
| editable | bool. Whether or not a dashboard is editable. |
| elements | Contains the list of elements included in the dashboard. Supported dashboard elements are: PanelKind and LibraryPanelKind. |
| layout | The dashboard layout. Supported layouts are:<ul><li>GridLayoutKind</li><li>AutoGridLayoutKind</li><li>RowsLayoutKind</li><li>TabsLayoutKind</li></ul> |
| links | Links with references to other dashboards or external websites. |
| liveNow | bool. When set to `true`, the dashboard redraws panels at an interval matching the pixel width. This keeps data "moving left" regardless of the query refresh rate. This setting helps avoid dashboards presenting stale live data. |
| preload | bool. When set to `true`, the dashboard loads all panels when the dashboard is loaded. |
| tags | Contains the list of tags associated with dashboard. |
| timeSettings | All time settings for the dashboard. |
| title | Title of the dashboard. |
| variables | Contains the list of configured template variables. |
<!-- prettier-ignore-end -->
### `annotations`
The configuration for the list of annotations that are associated with the dashboard.
For the JSON and field usage notes, refer to the [annotations schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/annotations-schema/).
### `elements`
Dashboards can contain the following elements:
- [PanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/)
- [LibraryPanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/librarypanel-schema/)
### `layout`
Dashboards can have four layout options:
- [GridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#gridlayoutkind)
- [AutoGridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#autogridlayoutkind)
- [RowsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#rowslayoutkind)
- [TabsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#tabslayoutkind)
For the JSON and field usage notes about each of these, refer to the [layout schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/).
### `links`
The configuration for links with references to other dashboards or external websites.
For the JSON and field usage notes, refer to the [links schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/links-schema/).
### `tags`
The tags associated with the dashboard:
` [...string]`
### `timesettings`
The `TimeSettingsSpec` defines the default time configuration for the time picker and the refresh picker for the specific dashboard.
For the JSON and field usage notes about the `TimeSettingsSpec`, refer to the [timesettings schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/timesettings-schema/).
### `variables`
The `variables` schema defines which variables are used in the dashboard.
There are eight variables types:
- QueryVariableKind
- TextVariableKind
- ConstantVariableKind
- DatasourceVariableKind
- IntervalVariableKind
- CustomVariableKind
- GroupByVariableKind
- AdhocVariableKind
For the JSON and field usage notes about the `variables` spec, refer to the [variables schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/variables-schema/).
## Notes and limitations
### Existing dashboards
With schema v2 enabled, you can still open and view your pre-existing dashboards.
Upon saving, theyll be updated to the new schema where you can take advantage of the new features and functionalities.
### Dashboard behavior with disabled feature flags
If you disable the Dynamic dashboards or `kubernetesDashboards` feature flags, you should be aware of how dashboards will behave.
#### Disable Dynamic dashboards
If the Dynamic dashboards feature toggle is disabled, depending on how the dashboard was built, it will behave differently:
- Dashboards built on the new schema through the UI - View only
- Dashboards built on Schema v1 - View and edit
- Dashboards built on the new schema by way of Terraform or the CLI - View and edit
- Provisioned dashboards built on the new schema - View and edit, but the edit experience will be the old experience
#### Disable Dynamic dashboards and `kubernetesDashboards`
Youll be unable to view or edit dashboards created or updated in the new schema.
### Import and export
From the UI, dashboards created on schema v2 can be exported and imported like other dashboards.
When you export them to use in another instance, references of data sources are not persisted but data source types are.
Youll have the option to select the data source of your choice in the import UI.
@@ -1,86 +0,0 @@
---
description: A reference for the JSON annotations schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- annotations
labels:
products:
- cloud
- enterprise
- oss
menuTitle: annotations schema
title: annotations
weight: 100
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/annotations-schema/
aliases:
- ../../../observability-as-code/schema-v2/annotations-schema/ # /docs/grafana/next/observability-as-code/schema-v2/annotations-schema/
---
# `annotations`
The configuration for the list of annotations that are associated with the dashboard.
```json
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"builtIn": false,
"datasource": {
"type": "",
"uid": ""
},
"enable": false,
"hide": false,
"iconColor": "",
"name": ""
}
}
],
```
`AnnotationsQueryKind` consists of:
- kind: "AnnotationQuery"
- spec: [AnnotationQuerySpec](#annotationqueryspec)
## `AnnotationQuerySpec`
| Name | Type/Definition |
| ---------- | ----------------------------------------------------------------- |
| datasource | [`DataSourceRef`](#datasourceref) |
| query | [`DataQueryKind`](#dataquerykind) |
| enable | bool |
| hide | bool |
| iconColor | string |
| name | string |
| builtIn | bool. Default is `false`. |
| filter | [`AnnotationPanelFilter`](#annotationpanelfilter) |
| options | `[string]`: A catch-all field for datasource-specific properties. |
### `DataSourceRef`
| Name | Usage |
| ----- | ---------------------------------- |
| type? | string. The plugin type-id. |
| uid? | The specific data source instance. |
### `DataQueryKind`
| Name | Type |
| ---- | ------ |
| kind | string |
| spec | string |
### `AnnotationPanelFilter`
| Name | Type/Definition |
| -------- | ------------------------------------------------------------------------------ |
| exclude? | bool. Should the specified panels be included or excluded. Default is `false`. |
| ids | `[...uint8]`. Panel IDs that should be included or excluded. |
@@ -1,339 +0,0 @@
---
description: A reference for the JSON layout schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- layout
labels:
products:
- cloud
- enterprise
- oss
menuTitle: layout schema
title: layout
weight: 400
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/layout-schema/
aliases:
- ../../../observability-as-code/schema-v2/layout-schema/ # /docs/grafana/next/observability-as-code/schema-v2/layout-schema/
---
# `layout`
There are four layout options offering two types of panel control:
**Panel layout options**
These options control the size and position of panels:
- [GridLayoutKind](#gridlayoutkind) - Corresponds to the **Custom** option in the UI. You define panel size and panel positions using x- and y- settings.
- [AutoGridLayoutKind](#autogridlayoutkind) - Corresponds to the **Auto grid** option in the UI. Panel size and position are automatically set based on column and row parameters.
**Panel grouping options**
These options control the grouping of panels:
- [RowsLayoutKind](#rowslayoutkind) - Groups panels into rows.
- [TabsLayoutKind](#tabslayoutkind) - Groups panels into tabs.
## `GridLayoutKind`
The grid layout allows you to manually size and position grid items by setting the height, width, x, and y of each item.
This layout corresponds to the **Custom** option in the UI.
Following is the JSON for a default grid layout, a grid layout item, and a grid layout row:
```json
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"element": {...},
"height": 0,
"width": 0,
"x": 0,
"y": 0
}
},
{
"kind": "GridLayoutRow",
"spec": {
"collapsed": false,
"elements": [],
"title": "",
"y": 0
}
},
]
}
```
`GridLayoutKind` consists of:
- kind: "GridLayout"
- spec: GridLayoutSpec
- items: GridLayoutItemKind` or GridLayoutRowKind`
- GridLayoutItemKind
- kind: "GridLayoutItem"
- spec: [GridLayoutItemSpec](#gridlayoutitemspec)
- GridLayoutRowKind
- kind: "GridLayoutRow"
- spec: [GridLayoutRowSpec](#gridlayoutrowspec)
### `GridLayoutItemSpec`
The following table explains the usage of the grid layout item JSON fields:
| Name | Usage |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| x | integer. Position of the item x-axis. |
| y | integer. Position of the item y-axis. |
| width | Width of the item in pixels. |
| height | Height of the item in pixels. |
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
| repeat? | [RepeatOptions](#repeatoptions). Configured repeat options, if any |
#### `RepeatOptions`
The following table explains the usage of the repeat option JSON fields:
| Name | Usage |
| ---------- | ---------------------------------------------------- |
| mode | `RepeatMode` - "variable" |
| value | string |
| direction? | Options are `h` for horizontal and `v` for vertical. |
| maxPerRow? | integer |
### `GridLayoutRowSpec`
The following table explains the usage of the grid layout row JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| y | integer. Position of the row y-axis |
| collapsed | bool. Whether or not the row is collapsed |
| title | Row title |
| elements | [`[...GridLayoutItemKind]`](#gridlayoutitemspec). Grid items in the row will have their y value be relative to the row's y value. This means a panel positioned at `y: 0` in a row with `y: 10` will be positioned at `y: 11` (row header has a height of 1) in the dashboard. |
| repeat? | [RowRepeatOptions](#rowrepeatoptions) Configured row repeat options, if any</p> |
<!-- prettier-ignore-end -->
#### `RowRepeatOptions`
| Name | Usage |
| ----- | ------------------------- |
| mode | `RepeatMode` - "variable" |
| value | string |
## `AutoGridLayoutKind`
With an auto grid, Grafana sizes and positions your panels for the best fit based on the column and row constraints that you set.
This layout corresponds to the **Auto grid** option in the UI.
Following is the JSON for a default auto grid layout and a grid layout item:
<!-- prettier-ignore-end -->
```json
"kind": "AutoGridLayout",
"spec": {
"columnWidthMode": "standard",
"fillScreen": false,
"items": [
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {...},
}
}
],
"maxColumnCount": 3,
"rowHeightMode": "standard"
}
```
`AutoGridLayoutKind` consists of:
- kind: "AutoGridLayout"
- spec: [AutoGridLayoutSpec](#autogridlayoutspec)
### `AutoGridLayoutSpec`
The following table explains the usage of the auto grid layout JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| maxColumnCount? | number. Default is `3`. |
| columnWidthMode | Options are: `narrow`, `standard`, `wide`, and `custom`. Default is `standard`. |
| columnWidth? | number |
| rowHeightMode | Options are: `short`, `standard`, `tall`, and `custom`. Default is `standard`. |
| rowHeight? | number |
| fillScreen? | bool. Default is `false`. |
| items | `AutoGridLayoutItemKind`. Consists of:<ul><li>kind: "AutoGridLayoutItem"</li><li>spec: [AutoGridLayoutItemSpec](#autogridlayoutitemspec)</li></ul> |
<!-- prettier-ignore-end -->
#### `AutoGridLayoutItemSpec`
The following table explains the usage of the auto grid layout item JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
| repeat? | [AutoGridRepeatOptions](#autogridrepeatoptions). Configured repeat options, if any. |
| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing panels, if any. Consists of:<ul><li>kind: "ConditionalRenderingGroup"</li><li>spec: [ConditionalRenderingGroupSpec](#conditionalrenderinggroupspec)</li></ul> |
<!-- prettier-ignore-end -->
##### `AutoGridRepeatOptions`
The following table explains the usage of the auto grid repeat option JSON fields:
| Name | Usage |
| ----- | ------------------------- |
| mode | `RepeatMode` - "variable" |
| value | String |
##### `ConditionalRenderingGroupSpec`
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| visibility | Options are `show` and `hide` |
| condition | Options are `and` and `or` |
| items | Options are:<ul><li>ConditionalRenderingVariableKind<ul><li>kind: "ConditionalRenderingVariable"</li><li>spec: [ConditionalRenderingVariableSpec](#conditionalrenderingvariablespec)</li></ul></li><li>ConditionalRenderingDataKind<ul><li>kind: "ConditionalRenderingData"</li><li>spec: [ConditionalRenderingDataSpec](#conditionalrenderingdataspec)</li></ul></li><li>ConditionalRenderingTimeRangeSizeKind<ul><li>kind: "ConditionalRenderingTimeRangeSize"</li><li>spec: [ConditionalRenderingTimeRangeSizeSpec](#conditionalrenderingtimerangesizespec)</li></ul></li></ul> |
<!-- prettier-ignore-end -->
###### `ConditionalRenderingVariableSpec`
| Name | Usage |
| -------- | ------------------------------------ |
| variable | string |
| operator | Options are `equals` and `notEquals` |
| value | string |
###### `ConditionalRenderingDataSpec`
| Name | Type |
| ----- | ---- |
| value | bool |
###### `ConditionalRenderingTimeRangeSizeSpec`
| Name | Type |
| ----- | ------ |
| value | string |
## `RowsLayoutKind`
The `RowsLayoutKind` is one of two options that you can use to group panels.
You can nest any other kind of layout inside a layout row.
Rows can also be nested in auto grids or tabs.
Following is the JSON for a default rows layout row:
```json
"kind": "RowsLayout",
"spec": {
"rows": [
{
"kind": "RowsLayoutRow",
"spec": {
"layout": {
"kind": "GridLayout", // Can also be AutoGridLayout or TabsLayout
"spec": {...}
},
"title": ""
}
}
]
}
```
`RowsLayoutKind` consists of:
- kind: RowsLayout
- spec: RowsLayoutSpec
- rows: RowsLayoutRowKind
- kind: RowsLayoutRow
- spec: [RowsLayoutRowSpec](#rowslayoutrowspec)
### `RowsLayoutRowSpec`
The following table explains the usage of the rows layout row JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| title? | Title of the row. |
| collapse | bool. Whether or not the row is collapsed. |
| hideHeader? | bool. Whether the row header is hidden or shown. |
| fullScreen? | bool. Whether or not the row takes up the full screen. |
| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing rows, if any. Consists of:<ul><li>kind: "ConditionalRenderingGroup"</li><li>spec: [ConditionalRenderingGroupSpec](#conditionalrenderinggroupspec)</li></ul> |
| repeat? | [RowRepeatOptions](#rowrepeatoptions). Configured repeat options, if any. |
| layout | Supported layouts are:<ul><li>[GridLayoutKind](#gridlayoutkind)</li><li>[RowsLayoutKind](#rowslayoutkind)</li><li>[AutoGridLayoutKind](#autogridlayoutkind)</li><li>[TabsLayoutKind](#tabslayoutkind)</li></ul> |
<!-- prettier-ignore-end -->
## `TabsLayoutKind`
The `TabsLayoutKind` is one of two options that you can use to group panels.
You can nest any other kind of layout inside a tab.
Tabs can also be nested in auto grids or rows.
Following is the JSON for a default tabs layout tab and a tab:
```json
"kind": "TabsLayout",
"spec": {
"tabs": [
{
"kind": "TabsLayoutTab",
"spec": {
"layout": {
"kind": "GridLayout", // Can also be AutoGridLayout or RowsLayout
"spec": {...}
},
"title": "New tab"
}
}
]
}
```
`TabsLayoutKind` consists of:
- kind: TabsLayout
- spec: TabsLayoutSpec
- tabs: TabsLayoutTabKind
- kind: TabsLayoutTab
- spec: [TabsLayoutTabSpec](#tabslayouttabspec)
### `TabsLayoutTabSpec`
The following table explains the usage of the tabs layout tab JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| title? | The title of the tab. |
| layout | Supported layouts are:<ul><li>[GridLayoutKind](#gridlayoutkind)</li><li>[RowsLayoutKind](#rowslayoutkind)</li><li>[AutoGridLayoutKind](#autogridlayoutkind)</li><li>[TabsLayoutKind](#tabslayoutkind)</li></ul> |
| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing panels, if any. Consists of:<ul><li>kind: "ConditionalRenderingGroup"</li><li>spec: [ConditionalRenderingGroupSpec](#conditionalrenderinggroupspec)</li></ul> |
<!-- prettier-ignore-end -->
@@ -1,68 +0,0 @@
---
description: A reference for the JSON library panel schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- library panel
labels:
products:
- cloud
- enterprise
- oss
menuTitle: LibraryPanelKind schema
title: LibraryPanelKind
weight: 300
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/librarypanel-schema/
aliases:
- ../../../observability-as-code/schema-v2/librarypanel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/librarypanel-schema/
---
# `LibraryPanelKind`
A library panel is a reusable panel that you can use in any dashboard.
When you make a change to a library panel, that change propagates to all instances of where the panel is used.
Library panels streamline reuse of panels across multiple dashboards.
Following is the default library panel element JSON:
```json
"kind": "LibraryPanel",
"spec": {
"id": 0,
"libraryPanel": {
name: "",
uid: "",
}
"title": ""
}
```
The `LibraryPanelKind` consists of:
- kind: "LibraryPanel"
- spec: [LibraryPanelKindSpec](#librarypanelkindspec)
- libraryPanel: [LibraryPanelRef](#librarypanelref)
## `LibraryPanelKindSpec`
The following table explains the usage of the library panel element JSON fields:
| Name | Usage |
| ------------ | ------------------------------------------------ |
| id | Panel ID for the library panel in the dashboard. |
| libraryPanel | [`LibraryPanelRef`](#librarypanelref) |
| title | Title for the library panel in the dashboard. |
### `LibraryPanelRef`
The following table explains the usage of the library panel reference JSON fields:
| Name | Usage |
| ---- | ------------------ |
| name | Library panel name |
| uid | Library panel uid |
@@ -1,66 +0,0 @@
---
description: A reference for the JSON links schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- links
labels:
products:
- cloud
- enterprise
- oss
menuTitle: links schema
title: links
weight: 500
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/links-schema/
aliases:
- ../../../observability-as-code/schema-v2/links-schema/ # /docs/grafana/next/observability-as-code/schema-v2/links-schema/
---
# `links`
The `links` schema is the configuration for links with references to other dashboards or external websites.
Following are the default JSON fields:
```json
"links": [
{
"asDropdown": false,
"icon": "",
"includeVars": false,
"keepTime": false,
"tags": [],
"targetBlank": false,
"title": "",
"tooltip": "",
"type": "link",
},
],
```
## `DashboardLink`
The following table explains the usage of the dashboard link JSON fields.
The table includes default and other fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ----------- | --------------------------------------- |
| title | string. Title to display with the link. |
| type | `DashboardLinkType`. Link type. Accepted values are:<ul><li>dashboards - To refer to another dashboard</li><li>link - To refer to an external resource</li></ul> |
| icon | string. Icon name to be displayed with the link. |
| tooltip | string. Tooltip to display when the user hovers their mouse over it. |
| url? | string. Link URL. Only required/valid if the type is link. |
| tags | string. List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards. |
| asDropdown | bool. If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards. Default is `false`. |
| targetBlank | bool. If true, the link will be opened in a new tab. Default is `false`. |
| includeVars | bool. If true, includes current template variables values in the link as query params. Default is `false`. |
| keepTime | bool. If true, includes current time range in the link as query params. Default is `false`. |
<!-- prettier-ignore-end -->
@@ -1,305 +0,0 @@
---
description: A reference for the JSON panel schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- panels
labels:
products:
- cloud
- enterprise
- oss
menuTitle: PanelKind schema
title: PanelKind
weight: 200
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/panel-schema/
aliases:
- ../../../observability-as-code/schema-v2/panel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/panel-schema/
---
# `PanelKind`
The panel element contains all the information about the panel including the visualization type, panel and visualization configuration, queries, and transformations.
There's a panel element for each panel contained in the dashboard.
Following is the default panel element JSON:
```json
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {...},
"description": "",
"id": 0,
"links": [],
"title": "",
"vizConfig": {
"kind": "",
"spec": {...},
}
}
```
The `PanelKind` consists of:
- kind: "Panel"
- spec: [PanelSpec](#panelspec)
## `PanelSpec`
The following table explains the usage of the panel element JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ------------ | --------------------------------------------------------------------- |
| data | `QueryGroupKind`, which includes queries and transformations. Consists of:<ul><li>kind: "QueryGroup"</li><li>spec: [QueryGroupSpec](#querygroupspec)</li></ul> |
| description | The panel description. |
| id | The panel ID. |
| links | Links with references to other dashboards or external websites. |
| title | The panel title. |
| vizConfig | `VizConfigKind`. Includes visualization type, field configuration options, and all other visualization options. Consists of:<ul><li>kind: string. Plugin ID.</li><li>spec: [VizConfigSpec](#vizconfigspec)</li></ul> |
| transparent? | bool. Controls whether or not the panel background is transparent. |
<!-- prettier-ignore-end -->
### `QueryGroupSpec`
<!-- prettier-ignore-start -->
| Name | Usage |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| queries | `PanelQueryKind`. Consists of:<ul><li>kind: PanelQuery</li><li>spec: [PanelQuerySpec](#panelqueryspec)</li></ul> |
| transformations | `TransformationKind`. Consists of:<ul><li>kind: string. The transformation ID.</li><li>spec: [DataTransformerConfig](#datatransformerconfig)</li></ul> |
| queryOptions | [`QueryOptionsSpec`](#queryoptionsspec) |
<!-- prettier-ignore-end -->
#### `PanelQuerySpec`
| Name | Usage |
| ----------- | --------------------------------- |
| query | [`DataQueryKind`](#dataquerykind) |
| datasource? | [`DataSourceRef`](#datasourceref) |
##### `DataQueryKind`
| Name | Type |
| ---- | ------ |
| kind | string |
| spec | string |
##### `DataSourceRef`
| Name | Usage |
| ----- | ---------------------------------- |
| type? | string. The plugin type-id. |
| uid? | The specific data source instance. |
#### `DataTransformerConfig`
Transformations allow you to manipulate data returned by a query before the system applies a visualization.
Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, or use the output of one transformation as the input to another transformation.
<!-- prettier-ignore-start -->
| Name | Usage |
| --------- | ------------------------------------------- |
| id | string. Unique identifier of transformer. |
| disabled? | bool. Disabled transformations are skipped. |
| filter? | [`MatcherConfig`](#matcherconfig). Optional frame matcher. When missing it will be applied to all results. |
| topic? | `DataTopic`. Where to pull `DataFrames` from as input to transformation. Options are: `series`, `annotations`, and `alertStates`. |
| options | Options to be passed to the transformer. Valid options depend on the transformer id. |
<!-- prettier-ignore-end -->
##### `MatcherConfig`
Matcher is a predicate configuration.
Based on the configuration a set of field or values, it's filtered to apply an override or transformation.
It comes with in id (to resolve implementation from registry) and a configuration thats specific to a particular matcher type.
| Name | Usage |
| -------- | -------------------------------------------------------------------------------------- |
| id | string. The matcher id. This is used to find the matcher implementation from registry. |
| options? | The matcher options. This is specific to the matcher implementation. |
#### `QueryOptionsSpec`
| Name | Type |
| ----------------- | ------- |
| timeFrom? | string |
| maxDataPoints? | integer |
| timeShift? | string |
| queryCachingTTL? | integer |
| interval? | string |
| cacheTimeout? | string |
| hideTimeOverride? | bool |
### `VizConfigSpec`
| Name | Type/Definition |
| ------------- | --------------------------------------- |
| pluginVersion | string |
| options | string |
| fieldConfig | [FieldConfigSource](#fieldconfigsource) |
#### `FieldConfigSource`
The data model used in Grafana, namely the _data frame_, is a columnar-oriented table structure that unifies both time series and table query results.
Each column within this structure is called a field.
A field can represent a single time series or table column.
Field options allow you to change how the data is displayed in your visualizations.
<!-- prettier-ignore-start -->
| Name | Type/Definition |
| ---------- | ------------------------------------- |
| defaults | [`FieldConfig`](#fieldconfig). Defaults are the options applied to all fields. |
| overrides | The options applied to specific fields overriding the defaults. |
| matcher | [`MatcherConfig`](#matcherconfig). Optional frame matcher. When missing it will be applied to all results. |
| properties | `DynamicConfigValue`. Consists of:<ul><li>`id` - string</li><li>value?</li></ul> |
<!-- prettier-ignore-end -->
##### `FieldConfig`
<!-- prettier-ignore-start -->
| Name | Type/Definition |
| ------------------ | --------------------------------------- |
| displayName? | string. The display value for this field. This supports template variables where empty is auto. |
| displayNameFromDS? | string. This can be used by data sources that return an explicit naming structure for values and labels. When this property is configured, this value is used rather than the default naming strategy. |
| description? | string. Human readable field metadata. |
| path? | string. An explicit path to the field in the data source. When the frame meta includes a path, this will default to `${frame.meta.path}/${field.name}`. When defined, this value can be used as an identifier within the data source scope, and may be used to update the results. |
| writeable? | bool. True if the data source can write a value to the path. Auth/authz are supported separately. |
| filterable? | bool. True if the data source field supports ad-hoc filters. |
| unit? | string. Unit a field should use. The unit you select is applied to all fields except time. You can use the unit's ID available in Grafana or a custom unit. [Available units in Grafana](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts). As custom units, you can use the following formats:<ul><li>`suffix:<suffix>` for custom unit that should go after value.</li><li>`prefix:<prefix>` for custom unit that should go before value.</li><li> `time:<format>` for custom date time formats type for example</li><li>`time:YYYY-MM-DD`</li><li>`si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. You can specify both a unit and the source data scale, so if your source data is represented as milli (thousands of) something, prefix the unit with that SI scale character.</li><li>`count:<unit>` for a custom count unit.</li><li>`currency:<unit>` for custom a currency unit.</li></ul> |
| decimals? | number. Specify the number of decimals Grafana includes in the rendered value. If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. For example 1.1234 will display as 1.12 and 100.456 will display as 100. To display all decimals, set the unit to `string`. |
| min? | number. The minimum value used in percentage threshold calculations. Leave empty for auto calculation based on all series and fields. |
| max? | number. The maximum value used in percentage threshold calculations. Leave empty for auto calculation based on all series and fields. |
| mappings? | `[...ValueMapping]`. Convert input values into a display string. Options are: [`ValueMap`](#valuemap), [`RangeMap`](#rangemap), [`RegexMap`](#rangemap), [`SpecialValueMap`](#specialvaluemap). |
| thresholds? | `ThresholdsConfig`. Map numeric values to states. Consists of:<ul><li>`mode` - `ThresholdsMode`. Options are: `absolute` and `percentage`.</li><li>`steps` - `[...Threshold]`</li></ul> |
| color? | [`FieldColor`](#fieldcolor). Panel color configuration. |
| links? | `[...]`. The behavior when clicking a result. |
| noValue? | string. Alternative to an empty string. |
| custom? | `{...}`. Specified by the `FieldConfig` field in panel plugin schemas. |
<!-- prettier-ignore-end -->
###### `ValueMap`
Maps text values to a color or different display text and color.
For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
<!-- prettier-ignore-start -->
| Name | Usage |
| ------- | -------- |
| type | `MappingType` & "value". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
| options | string. [`ValueMappingResult`](#valuemappingresult). Map with `<value_to_match>`: `ValueMappingResult`. For example: `{ "10": { text: "Perfection!", color: "green" } }`. |
<!-- prettier-ignore-end -->
###### `RangeMap`
Maps numerical ranges to a display text and color.
For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
<!-- prettier-ignore-start -->
| Name | Usage |
| ------- | ---------------------------------------------------------------------------------------------------- |
| type | `MappingType` & "range". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
| options | Range to match against and the result to apply when the value is within the range. Spec:<ul><li>`from` - `float64` or `null`. Min value of the range. It can be null which means `-Infinity`.</li><li>`to` - `float64` or `null`. Max value of the range. It can be null which means `+Infinity`.</li><li>`result` - [`ValueMappingResult`](#valuemappingresult) |
<!-- prettier-ignore-end -->
###### `RegexMap`
Maps regular expressions to replacement text and a color.
For example, if a value is `www.example.com`, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
<!-- prettier-ignore-start -->
| Name | Usage |
| ------- | --------------------------------------------------------------------------------------------- |
| type | `MappingType` & "regex". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
| options | Regular expression to match against and the result to apply when the value matches the regex. Spec:<ul><li>`pattern` - string. Regular expression to match against.</li><li>`result` - [`ValueMappingResult`](#valuemappingresult) |
<!-- prettier-ignore-end -->
###### `SpecialValueMap`
Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.
See `SpecialValueMatch` in the following table to see the list of special values.
For example, you can configure a special value mapping so that null values appear as N/A.
<!-- prettier-ignore-start -->
| Name | Usage |
| ------- | ----------------------------------------------------------------------------------------------- |
| type | `MappingType` & "special". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
| options | Spec:<ul><li>`match` - `SpecialValueMatch`. Special value to match against. Types are:<ul><li>true</li><li>false</li><li>null</li><li>nan</li><li>empty</li></ul> </li><li>`result` - [`ValueMappingResult`](#valuemappingresult) |
<!-- prettier-ignore-end -->
###### `ValueMappingResult`
Result used as replacement with text and color when the value matches.
| Name | Usage |
| ----- | ----------------------------------------------------------------------------- |
| text | string. Text to display when the value matches. |
| color | string. Color to use when the value matches. |
| icon | string. Icon to display when the value matches. Only specific visualizations. |
| index | int32. Position in the mapping array. Only used internally. |
###### `FieldColor`
Map a field to a color.
<!-- prettier-ignore-start -->
| Name | Usage |
| ----------- | -------------------------------------------------------------------- |
| mode | [`FieldColorModeId`](#fieldcolormodeid). The main color scheme mode. |
| FixedColor? | string. The fixed color value for fixed or shades color modes. |
| seriesBy? | `FieldColorSeriesByMode`. Some visualizations need to know how to assign a series color from by value color schemes. Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. Options are: `min`, `max`, and `last`. |
<!-- prettier-ignore-end -->
###### `FieldColorModeId`
Color mode for a field.
You can specify a single color, or select a continuous (gradient) color schemes, based on a value.
Continuous color interpolates a color using the percentage of a value relative to min and max.
Accepted values are:
<!-- prettier-ignore-start -->
| Name | Description |
| --- | ---- |
| thresholds | From thresholds. Informs Grafana to take the color from the matching threshold. |
| palette-classic | Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for graphs and pie charts and other categorical data visualizations. |
| palette-classic-by-name | Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations |
| continuous-GrYlRd | Continuous Green-Yellow-Red palette mode |
| continuous-RdYlGr | Continuous Red-Yellow-Green palette mode |
| continuous-BlYlRd | Continuous Blue-Yellow-Red palette mode |
| continuous-YlRd | Continuous Yellow-Red palette mode |
| continuous-BlPu | Continuous Blue-Purple palette mode |
| continuous-YlBl | Continuous Yellow-Blue palette mode |
| continuous-blues | Continuous Blue palette mode |
| continuous-reds | Continuous Red palette mode |
| continuous-greens | Continuous Green palette mode |
| continuous-purples | Continuous Purple palette mode |
| shades | Shades of a single color. Specify a single color, useful in an override rule. |
| fixed | Fixed color mode. Specify a single color, useful in an override rule. |
<!-- prettier-ignore-end -->
@@ -1,87 +0,0 @@
---
description: A reference for the JSON timesettings schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- time settings
labels:
products:
- cloud
- enterprise
- oss
menuTitle: timesettings schema
title: timesettings
weight: 600
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/timesettings-schema/
aliases:
- ../../../observability-as-code/schema-v2/timesettings-schema/ # /docs/grafana/next/observability-as-code/schema-v2/timesettings-schema/
---
# `timeSettings`
The `TimeSettingsSpec` defines the default time configuration for the time picker and the refresh picker for the specific dashboard.
Following is the JSON for default time settings:
```json
"timeSettings": {
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"fiscalYearStartMonth": 0,
"from": "now-6h",
"hideTimepicker": false,
"timezone": "browser",
"to": "now"
},
```
`timeSettings` consists of:
- [TimeSettingsSpec](#timesettingsspec)
## `TimeSettingsSpec`
The following table explains the usage of the time settings JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ---- | ----- |
| timezone? | string. Timezone of dashboard. Accepted values are IANA TZDB zone ID, `browser`, or `utc`. Default is `browser`. |
| from | string. Start time range for dashboard. Accepted values are relative time strings like `now-6h` or absolute time strings like `2020-07-10T08:00:00.000Z`. Default is `now-6h`. |
| to | string. End time range for dashboard. Accepted values are relative time strings like `now-6h` or absolute time strings like `2020-07-10T08:00:00.000Z`. Default is `now`. |
| autoRefresh | string. Refresh rate of dashboard. Represented by interval string. For example: `5s`, `1m`, `1h`, `1d`. No default. In schema v1: `refresh`. |
| autoRefreshIntervals | string. Interval options available in the refresh picker drop-down menu. The default array is `["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]`. |
|quickRanges? | Selectable options available in the time picker drop-down menu. Has no effect on provisioned dashboard. Defined in the [`TimeRangeOption`](#timerangeoption) spec. In schema v1: `timepicker.quick_ranges`, not exposed in the UI. |
| hideTimepicker | bool. Whether or not the time picker is visible. Default is `false`. In schema v1: `timepicker.hidden`. |
| weekStart? | Day when the week starts. Expressed by the name of the day in lowercase. For example: `monday`. Options are `saturday`, `monday`, and `sunday`. |
| fiscalYearStartMonth | The month that the fiscal year starts on. `0` = January, `11` = December |
| nowDelay? | string. Override the "now" time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. In schema v1: `timepicker.nowDelay`. |
<!-- prettier-ignore-end -->
### `TimeRangeOption`
The following table explains the usage of the time range option JSON fields:
| Name | Usage |
| ------- | ---------------------------------- |
| display | string. Default is `Last 6 hours`. |
| from | string. Default is `now-6h`. |
| to | string. Default is `now`. |
@@ -1,501 +0,0 @@
---
description: A reference for the JSON variables schema used with Observability as Code.
keywords:
- configuration
- as code
- as-code
- dashboards
- git integration
- git sync
- github
- variables
labels:
products:
- cloud
- enterprise
- oss
menuTitle: variables schema
title: variables
weight: 700
canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/variables-schema/
aliases:
- ../../../observability-as-code/schema-v2/variables-schema/ # /docs/grafana/next/observability-as-code/schema-v2/variables-schema/
---
# `variables`
The available variable types described in the following sections:
- [QueryVariableKind](#queryvariablekind)
- [TextVariableKind](#textvariablekind)
- [ConstantVariableKind](#constantvariablekind)
- [DatasourceVariableKind](#datasourcevariablekind)
- [IntervalVariableKind](#intervalvariablekind)
- [CustomVariableKind](#customvariablekind)
- [SwitchVariableKind](#switchvariablekind)
- [GroupByVariableKind](#groupbyvariablekind)
- [AdhocVariableKind](#adhocvariablekind)
## `QueryVariableKind`
Following is the JSON for a default query variable:
```json
"variables": [
{
"kind": "QueryVariable",
"spec": {
"current": {
"text": "",
"value": ""
},
"hide": "dontHide",
"includeAll": false,
"multi": false,
"name": "",
"options": [],
"query": defaultDataQueryKind(),
"refresh": "never",
"regex": "",
"skipUrlSync": false,
"sort": "disabled"
}
}
]
```
`QueryVariableKind` consists of:
- kind: "QueryVariable"
- spec: [QueryVariableSpec](#queryvariablespec)
### `QueryVariableSpec`
The following table explains the usage of the query variable JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| ------------ | ---------------------------------------------- |
| name | string. Name of the variable. |
| current | "Text" and a "value" or [`VariableOption`](#variableoption) |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
| datasource? | [`DataSourceRef`](#datasourceref) |
| query | `DataQueryKind`. Consists of:<ul><li>kind: string</li><li>spec: string</li></ul> |
| regex | string |
| sort | `VariableSort`. Options are:<ul><li>disabled</li><li>alphabeticalAsc</li><li>alphabeticalDesc</li><li>numericalAsc</li><li>numericalDesc</li><li>alphabeticalCaseInsensitiveAsc</li><li>alphabeticalCaseInsensitiveDesc</li><li>naturalAsc</li><li>naturalDesc</li></ul> |
| definition? | string |
| options | [`VariableOption`](#variableoption) |
| multi | bool. Default is `false`. |
| includeAll | bool. Default is `false`. |
| allValue? | string |
| placeholder? | string |
<!-- prettier-ignore-end -->
#### `VariableOption`
| Name | Usage |
| -------- | -------------------------------------------- |
| selected | bool. Whether or not the option is selected. |
| text | string. Text to be displayed for the option. |
| value | string. Value of the option. |
#### `DataSourceRef`
| Name | Usage |
| ----- | ---------------------------------- |
| type? | string. The plugin type-id. |
| uid? | The specific data source instance. |
## `TextVariableKind`
Following is the JSON for a default text variable:
```json
"variables": [
{
"kind": "TextVariable",
"spec": {
"current": {
"text": "",
"value": ""
},
"hide": "dontHide",
"name": "",
"query": "",
"skipUrlSync": false
}
}
]
```
`TextVariableKind` consists of:
- kind: TextVariableKind
- spec: [TextVariableSpec](#textvariablespec)
### `TextVariableSpec`
The following table explains the usage of the query variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| current | "Text" and a "value" or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| query | string |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
## `ConstantVariableKind`
Following is the JSON for a default constant variable:
```json
"variables": [
{
"kind": "ConstantVariable",
"spec": {
"current": {
"text": "",
"value": ""
},
"hide": "hideVariable",
"name": "",
"query": "",
"skipUrlSync": true
}
}
]
```
`ConstantVariableKind` consists of:
- kind: "ConstantVariable"
- spec: [ConstantVariableSpec](#constantvariablespec)
### `ConstantVariableSpec`
The following table explains the usage of the constant variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| query | string |
| current | "Text" and a "value" or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
## `DatasourceVariableKind`
Following is the JSON for a default data source variable:
```json
"variables": [
{
"kind": "DatasourceVariable",
"spec": {
"current": {
"text": "",
"value": ""
},
"hide": "dontHide",
"includeAll": false,
"multi": false,
"name": "",
"options": [],
"pluginId": "",
"refresh": "never",
"regex": "",
"skipUrlSync": false
}
}
]
```
`DatasourceVariableKind` consists of:
- kind: "DatasourceVariable"
- spec: [DatasourceVariableSpec](#datasourcevariablespec)
### `DatasourceVariableSpec`
The following table explains the usage of the data source variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| pluginId | string |
| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
| regex | string |
| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| multi | bool. Default is `false`. |
| includeAll | bool. Default is `false`. |
| allValue? | string |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
## `IntervalVariableKind`
Following is the JSON for a default interval variable:
```json
"variables": [
{
"kind": "IntervalVariable",
"spec": {
"auto": false,
"auto_count": 0,
"auto_min": "",
"current": {
"text": "",
"value": ""
},
"hide": "dontHide",
"name": "",
"options": [],
"query": "",
"refresh": "never",
"skipUrlSync": false
}
}
]
```
`IntervalVariableKind` consists of:
- kind: "IntervalVariable"
- spec: [IntervalVariableSpec](#intervalvariablespec)
### `IntervalVariableSpec`
The following table explains the usage of the interval variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| query | string |
| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| auto | bool. Default is `false`. |
| auto_count | integer. Default is `0`. |
| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false` |
| description? | string |
## `CustomVariableKind`
Following is the JSON for a default custom variable:
```json
"variables": [
{
"kind": "CustomVariable",
"spec": {
"current": defaultVariableOption(),
"hide": "dontHide",
"includeAll": false,
"multi": false,
"name": "",
"options": [],
"query": "",
"skipUrlSync": false
}
}
]
```
`CustomVariableKind` consists of:
- kind: "CustomVariable"
- spec: [CustomVariableSpec](#customvariablespec)
### `CustomVariableSpec`
The following table explains the usage of the custom variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| query | string |
| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| multi | bool. Default is `false`. |
| includeAll | bool. Default is `false`. |
| allValue? | string |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
## `SwitchVariableKind`
Following is the JSON for a default switch variable:
```json
"variables": [
{
"kind": "SwitchVariable",
"spec": {
"current": "false",
"enabledValue": "true",
"disabledValue": "false",
"hide": "dontHide",
"name": "",
"skipUrlSync": false
}
}
]
```
`SwitchVariableKind` consists of:
- kind: "SwitchVariable"
- spec: [SwitchVariableSpec](#switchvariablespec)
### `SwitchVariableSpec`
The following table explains the usage of the switch variable JSON fields:
<!-- prettier-ignore-start -->
| Name | Usage |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| current | string. Current value of the switch variable (either `enabledValue` or `disabledValue`). |
| enabledValue | string. Value when the switch is in the enabled state. |
| disabledValue | string. Value when the switch is in the disabled state. |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
<!-- prettier-ignore-end -->
## `GroupByVariableKind`
Following is the JSON for a default group by variable:
```json
"variables": [
{
"kind": "GroupByVariable",
"spec": {
"current": {
"text": [
""
],
"value": [
""
]
},
"datasource": {},
"hide": "dontHide",
"multi": false,
"name": "",
"options": [],
"skipUrlSync": false
}
}
]
```
`GroupByVariableKind` consists of:
- kind: "GroupByVariable"
- spec: [GroupByVariableSpec](#groupbyvariablespec)
### `GroupByVariableSpec`
The following table explains the usage of the group by variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable |
| datasource? | `DataSourceRef`. Refer to the [`DataSourceRef` definition](#datasourceref) under `QueryVariableKind`. |
| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
| multi | bool. Default is `false`. |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string. |
## `AdhocVariableKind`
Following is the JSON for a default ad hoc variable:
```json
"variables": [
{
"kind": "AdhocVariable",
"spec": {
"baseFilters": [],
"defaultKeys": [],
"filters": [],
"hide": "dontHide",
"name": "",
"skipUrlSync": false
}
}
]
```
`AdhocVariableKind` consists of:
- kind: "AdhocVariable"
- spec: [AdhocVariableSpec](#adhocvariablespec)
### `AdhocVariableSpec`
The following table explains the usage of the ad hoc variable JSON fields:
| Name | Usage |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| name | string. Name of the variable. |
| datasource? | `DataSourceRef`. Consists of:<ul><li>type? - string. The plugin type-id.</li><li>uid? - string. The specific data source instance.</li></ul> |
| baseFilters | [AdHocFilterWithLabels](#adhocfilterswithlabels) |
| filters | [AdHocFilterWithLabels](#adhocfilterswithlabels) |
| defaultKeys | [MetricFindValue](#metricfindvalue) |
| label? | string |
| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
| skipUrlSync | bool. Default is `false`. |
| description? | string |
#### `AdHocFiltersWithLabels`
The following table explains the usage of the ad hoc variable with labels JSON fields:
| Name | Type |
| ------------ | ------------- |
| key | string |
| operator | string |
| value | string |
| values? | `[...string]` |
| keyLabel | string |
| valueLabels? | `[...string]` |
| forceEdit? | bool |
#### `MetricFindValue`
The following table explains the usage of the metric find value JSON fields:
| Name | Type |
| ----------- | ---------------- |
| text | string |
| value? | string or number |
| group? | string |
| expandable? | bool |
@@ -3,12 +3,29 @@ aliases:
- ../../../reference/dashboard/ # /docs/grafana/next/reference/dashboard/
- ../../../dashboards/json-model/ # /docs/grafana/next/dashboards/json-model/
- ../../../dashboards/build-dashboards/view-dashboard-json-model/ # /docs/grafana/next/dashboards/build-dashboards/view-dashboard-json-model/
- ../../../as-code/observability-as-code/schema-v2/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/
- ../../../as-code/observability-as-code/schema-v2/annotations-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/annotations-schema/
- ../../../as-code/observability-as-code/schema-v2/panel-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/panel-schema/
- ../../../as-code/observability-as-code/schema-v2/librarypanel-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/librarypanel-schema/
- ../../../as-code/observability-as-code/schema-v2/layout-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/layout-schema/
- ../../../as-code/observability-as-code/schema-v2/links-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/links-schema/
- ../../../as-code/observability-as-code/schema-v2/timesettings-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/timesettings-schema/
- ../../../as-code/observability-as-code/schema-v2/variables-schema/ # /docs/grafana/latest/as-code/observability-as-code/schema-v2/variables-schema/
- ../../../observability-as-code/schema-v2/ # /docs/grafana/latest/observability-as-code/schema-v2/
- ../../../../next/observability-as-code/schema-v2/annotations-schema/ # /docs/grafana/next/observability-as-code/schema-v2/annotations-schema/
- ../../../../next/observability-as-code/schema-v2/panel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/panel-schema/
- ../../../../next/observability-as-code/schema-v2/librarypanel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/librarypanel-schema/
- ../../../../next/observability-as-code/schema-v2/layout-schema/ # /docs/grafana/next/observability-as-code/schema-v2/layout-schema/
- ../../../../next/observability-as-code/schema-v2/links-schema/ # /docs/grafana/next/observability-as-code/schema-v2/links-schema/
- ../../../../next/observability-as-code/schema-v2/timesettings-schema/ # /docs/grafana/next/observability-as-code/schema-v2/timesettings-schema/
- ../../../../next/observability-as-code/schema-v2/variables-schema/ # /docs/grafana/next/observability-as-code/schema-v2/variables-schema/
keywords:
- grafana
- dashboard
- documentation
- json
- model
- schema v2
labels:
products:
- cloud
@@ -27,21 +44,37 @@ refs:
# Dashboard JSON model
A dashboard in Grafana is represented by a JSON object, which stores metadata of its dashboard. Dashboard metadata includes dashboard properties, metadata from panels, template variables, panel queries, etc.
Grafana dashboards are represented as JSON objects that store metadata, panels, variables, and settings.
To view the JSON of a dashboard:
## Different dashboard schema models
1. Click **Edit** in the top-right corner of the dashboard.
1. Click **Settings**.
1. Go to the **JSON Model** tab.
1. When you've finished viewing the JSON, click **Back to dashboard** and **Exit edit**.
There are currently three dashboard JSON schema models:
## JSON fields
When a user creates a new dashboard, a new dashboard JSON object is initialized with the following fields:
- [Classic](#classic-model) - The default schema in self-managed Grafana. (Also referred to as the JSON schema v1.).
- [V1 Resource](#v1-resource-model) - The Classic dashboard schema formatted as a Kubernetes-style resource. The `spec` property of the schema contains the Classic-style model of the schema. Dashboards created using the Classic model can be exported using either that model or this one.
- [V2 Resource](#v2-resource-model) - Kubernetes-style resource schema. The default schema in Grafana Cloud. (Also referred to as the JSON schema v2.)
{{< admonition type="note" >}}
In the following JSON, id is shown as null which is the default value assigned to it until a dashboard is saved. Once a dashboard is saved, an integer value is assigned to the `id` field.
[Observability as Code](https://grafana.com/docs/grafana/latest/as-code/observability-as-code/) works with all versions of the JSON model, and it's fully compatible with version 2.
{{< /admonition >}}
## Access and update the JSON model (#view-json)
To access the JSON representation of a dashboard:
1. Click **Edit** in the top-right corner of the dashboard.
1. Click the gear icon in the right sidebar and click **Settings** in the secondary sidebar.
1. Select the **JSON Model** tab.
1. Update the JSON structure as needed.
1. Click **Save changes**.
## Classic model
When you create a new dashboard in self-managed Grafana, a new dashboard JSON object was initialized with the following fields:
{{< admonition type="note" >}}
In the following JSON, id is shown as null which is the default value assigned to it until a dashboard is saved.
After a dashboard is saved, an integer value is assigned to the `id` field.
{{< /admonition >}}
```json
@@ -76,26 +109,30 @@ In the following JSON, id is shown as null which is the default value assigned t
Each field in the dashboard JSON is explained below with its usage:
| Name | Usage |
| ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| **id** | unique numeric identifier for the dashboard. (generated by the db) |
| **uid** | unique dashboard identifier that can be generated by anyone. string (8-40) |
| **title** | current title of dashboard |
| **tags** | tags associated with dashboard, an array of strings |
| **style** | theme of dashboard, i.e. `dark` or `light` |
| **timezone** | timezone of dashboard, i.e. `utc` or `browser` |
| **editable** | whether a dashboard is editable or not |
| **graphTooltip** | 0 for no shared crosshair or tooltip (default), 1 for shared crosshair, 2 for shared crosshair AND shared tooltip |
| **time** | time range for dashboard, i.e. last 6 hours, last 7 days, etc |
| **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details |
| **templating** | templating metadata, see [templating section](#templating) for details |
| **annotations** | annotations metadata, see [annotations](ref:annotations) for how to add them |
| **refresh** | auto-refresh interval |
| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to said schema |
| **version** | version of the dashboard (integer), incremented each time the dashboard is updated |
| **panels** | panels array, see below for detail. |
<!--prettier-ignore-start -->
## Panels
| Name | Usage |
| ----------------- | ------------------------------------------------------------------------------------------ |
| **id** | unique numeric identifier for the dashboard. (generated by the db) |
| **uid** | unique dashboard identifier that can be generated by anyone. string (8-40) |
| **title** | current title of dashboard |
| **tags** | tags associated with dashboard, an array of strings |
| **style** | theme of dashboard, i.e. `dark` or `light` |
| **timezone** | timezone of dashboard, i.e. `utc` or `browser` |
| **editable** | whether a dashboard is editable or not |
| **graphTooltip** | 0 for no shared crosshair or tooltip (default), 1 for shared crosshair, 2 for shared crosshair AND shared tooltip |
| **time** | time range for dashboard, i.e. last 6 hours, last 7 days, etc |
| **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details |
| **templating** | templating metadata, see [templating section](#templating) for details |
| **annotations** | annotations metadata, see [annotations](ref:annotations) for how to add them |
| **refresh** | auto-refresh interval|
| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to said schema |
| **version** | version of the dashboard (integer), incremented each time the dashboard is updated |
| **panels** | panels array, see below for detail. |
<!--prettier-ignore-end -->
### Panels
Panels are the building blocks of a dashboard. It consists of data source queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depend on the panel type. Following is an example of panel JSON of a text panel.
@@ -168,18 +205,22 @@ The grid has a negative gravity that moves panels up if there is empty space abo
Usage of the fields is explained below:
| Name | Usage |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **collapse** | whether timepicker is collapsed or not |
| **enable** | whether timepicker is enabled or not |
| **notice** | |
| **now** | |
| **hidden** | whether timepicker is hidden or not |
| **nowDelay** | override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
| **quick_ranges** | custom quick ranges |
| **refresh_intervals** | interval options available in the refresh picker dropdown |
| **status** | |
| **type** | |
<!--prettier-ignore-start -->
| Name | Usage |
| --------------------- | --------------------------------------------------------- |
| **collapse** | whether timepicker is collapsed or not |
| **enable** | whether timepicker is enabled or not |
| **notice** | |
| **now** | |
| **hidden** | whether timepicker is hidden or not |
| **nowDelay** | override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
| **quick_ranges** | custom quick ranges |
| **refresh_intervals** | interval options available in the refresh picker dropdown |
| **status** | |
| **type** | |
<!--prettier-ignore-end -->
### templating
@@ -270,3 +311,87 @@ Usage of the above mentioned fields in the templating section is explained below
| **refresh** | configures when to refresh a variable |
| **regex** | extracts part of a series name or metric node segment |
| **type** | type of variable, i.e. `custom`, `query` or `interval` |
## V1 Resource model
The V1 Resource schema model formats the [Classic JSON model](#classic-model) schema as a Kubernetes-style resource.
The `spec` property of the schema contains the Classic-style model of the schema.
<!-- Following text is from limitations section of schema v2 docs. What do you see when you open a dashboard that was created before DD was enabled? A v1 Resource schema or a V2 resource schema?
With schema v2 enabled, you can still open and view your pre-existing dashboards.
Upon saving, theyll be updated to the new schema where you can take advantage of the new features and functionalities.-->
Dashboards created using the Classic model can be exported using either this model or the Classic one.
The following code snippet shows the fields included in the V1 Resource model.
For the detailed V1 Resource schema, refer to the [Swagger documentation](https://play.grafana.org/swagger?api=dashboard.grafana.app-v1beta1).
```json
{
"apiVersion": "dashboard.grafana.app/v1beta1",
"kind": "Dashboard",
"metadata": {
"name": "isnt5ss",
"namespace": "stacks-521104",
"uid": "92674c0e-0360-4bb4-99ab-fb150581376d",
"resourceVersion": "1764705030717045",
"generation": 1,
"creationTimestamp": "2025-12-02T19:50:30Z",
"labels": {
"grafana.app/deprecatedInternalID": "1329"
},
"annotations": {
"grafana.app/createdBy": "user:u000000002",
"grafana.app/folder": "",
"grafana.app/saved-from-ui": "Grafana Cloud (instant)"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1329,
"links": [],
"panels": [],
"preload": false,
"schemaVersion": 42,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "Africa/Abidjan",
"title": "Graphite suggestions",
"uid": "isnt5ss",
"version": 1,
"weekStart": ""
},
"status": {}
}
```
## V2 Resource model
{{< docs/public-preview product="Dashboard JSON schema v2" >}}
For the detailed V2 Resource model schema, refer to the [Swagger documentation](https://play.grafana.org/swagger?api=dashboard.grafana.app-v2beta1).
@@ -171,6 +171,7 @@ Query expressions are different for each data source. For more information, refe
- If you need more room in a single input field query editor, then hover your cursor over the lines in the lower right corner of the field and drag downward to expand.
1. (Optional) In the **Regex** field, type a regular expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with a regular expression](#filter-variables-with-regex).
1. Under **Apply regex to**, select **Variable value** or **Display text** to choose where the regex pattern is applied. The default is **Variable value**.
1. In the **Sort** drop-down list, select the sort order for values to be displayed in the dropdown list. The default option, **Disabled**, means that the order of options returned by your data source query is used.
1. Under **Refresh**, select when the variable should update options:
- **On dashboard load** - Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized.
@@ -79,6 +79,16 @@ test.describe(
await expect(regexInput).toHaveAttribute('placeholder', '/.*-(?<text>.*)-(?<value>.*)-.*/');
await expect(regexInput).toHaveValue('');
// Check regex apply to field - should default to "Variable value"
const regexApplyToField = dashboardPage.getByGrafanaSelector(
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExApplyToSelectV2
);
await expect(regexApplyToField).toBeVisible();
const variableValueRadio = page.getByRole('radio', { name: 'Variable value' });
await expect(variableValueRadio).toBeChecked();
const displayTextRadio = page.getByRole('radio', { name: 'Display text' });
await expect(displayTextRadio).not.toBeChecked();
const sortSelect = dashboardPage.getByGrafanaSelector(
selectors.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2
);
-8
View File
@@ -2001,11 +2001,6 @@
"count": 1
}
},
"public/app/features/dashboard-scene/settings/variables/components/VariableTextAreaField.tsx": {
"no-restricted-syntax": {
"count": 1
}
},
"public/app/features/dashboard-scene/settings/variables/components/VariableTextField.tsx": {
"no-restricted-syntax": {
"count": 1
@@ -2062,9 +2057,6 @@
},
"@typescript-eslint/no-explicit-any": {
"count": 3
},
"no-restricted-syntax": {
"count": 3
}
},
"public/app/features/dashboard-scene/v2schema/ImportDashboardOverviewV2.tsx": {
+3 -2
View File
@@ -250,6 +250,7 @@ require (
github.com/grafana/grafana/apps/plugins v0.0.0 // @grafana/plugins-platform-backend
github.com/grafana/grafana/apps/preferences v0.0.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/provisioning v0.0.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 // @grafana/grafana-search-and-storage
github.com/grafana/grafana/apps/scope v0.0.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/apps/secret v0.0.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/apps/shorturl v0.0.0 // @grafana/sharing-squad
@@ -665,7 +666,7 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/swag/conv v0.25.1 // indirect
github.com/go-openapi/swag/fileutils v0.25.1 // indirect
@@ -686,7 +687,7 @@ require (
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/shirou/gopsutil/v4 v4.25.3 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
+6 -4
View File
@@ -1142,8 +1142,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
@@ -1653,6 +1653,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
@@ -2393,8 +2395,8 @@ github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 h1:/4/IJi5iyTdh6mqOUaASW148HQpujYiHl0Wl78dSOSc=
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3/go.mod h1:aJIMhRsunltJR926EB2MUg8qHemFQDreSB33pyto2Ps=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+1
View File
@@ -23,6 +23,7 @@ use (
./apps/plugins
./apps/preferences
./apps/provisioning
./apps/quotas
./apps/scope
./apps/secret
./apps/shorturl
+6 -14
View File
@@ -267,6 +267,8 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06
git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=
git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk=
github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas=
@@ -602,6 +604,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
@@ -680,8 +684,6 @@ github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws=
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b h1:ZHiD4/yE4idlbqvAO6iYCOYRzOMRpxkW+FKasRA3tsQ=
github.com/efficientgo/tools/core v0.0.0-20220225185207-fe763185946b/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M=
github.com/elastic/elastic-transport-go/v8 v8.6.1 h1:h2jQRqH6eLGiBSN4eZbQnJLtL4bC5b4lfVFRjw2R4e4=
@@ -873,7 +875,6 @@ github.com/grafana/grafana-app-sdk v0.41.0 h1:SYHN3U7B1myRKY3UZZDkFsue9TDmAOap0U
github.com/grafana/grafana-app-sdk v0.41.0/go.mod h1:Wg/3vEZfok1hhIWiHaaJm+FwkosfO98o8KbeLFEnZpY=
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
github.com/grafana/grafana-app-sdk v0.47.0/go.mod h1:kywXmkppq0oReUMzkjTW8Fq2EBzyN7v914jttTWnWxA=
github.com/grafana/grafana-app-sdk v0.48.2/go.mod h1:LDOvQ7OOyHLcXdSa0InATCa5OMoYAd6E1+rGLrMgHuk=
github.com/grafana/grafana-app-sdk/logging v0.38.0/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana-app-sdk/logging v0.39.0 h1:3GgN5+dUZYqq74Q+GT9/ET+yo+V54zWQk/Q2/JsJQB4=
github.com/grafana/grafana-app-sdk/logging v0.39.0/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
@@ -887,9 +888,10 @@ github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5
github.com/grafana/grafana-app-sdk/logging v0.46.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.0 h1:xolkQxBlA2LQF4hprKIAeu+zUem1DigYZ6XC1TOhFJE=
github.com/grafana/grafana-app-sdk/logging v0.48.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.2 h1:tI+a9slUvxKUgweXDzUqkca2LWV3g1UdaSvwt8nQNHg=
github.com/grafana/grafana-app-sdk/logging v0.48.2/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.5 h1:vWiTZrsSscbC5IQq2heWXm0dXhvg40nXIeUTCdE9qsc=
github.com/grafana/grafana-app-sdk/logging v0.48.5/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/plugin v0.41.0 h1:ShUvGpAVzM3UxcsfwS6l/lwW4ytDeTbCQXf8w2P8Yp8=
github.com/grafana/grafana-app-sdk/plugin v0.41.0/go.mod h1:YIhimVfAqtOp3kdhxOanaSZjypVKh/bYxf9wfFfhDm0=
github.com/grafana/grafana-aws-sdk v0.38.2 h1:TzQD0OpWsNjtldi5G5TLDlBRk8OyDf+B5ujcoAu4Dp0=
@@ -956,7 +958,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9K
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
@@ -1375,7 +1376,6 @@ github.com/richardartoul/molecule v1.0.0/go.mod h1:uvX/8buq8uVeiZiFht+0lqSLBHF+u
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
@@ -1412,8 +1412,6 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
@@ -1593,7 +1591,6 @@ go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5queth
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/collector v0.121.0/go.mod h1:M4TlnmkjIgishm2DNCk9K3hMKTmAsY9w8cNFsp9EchM=
go.opentelemetry.io/collector v0.124.0/go.mod h1:QzERYfmHUedawjr8Ph/CBEEkVqWS8IlxRLAZt+KHlCg=
go.opentelemetry.io/collector/client v1.29.0/go.mod h1:LCUoEV2KCTKA1i+/txZaGsSPVWUcqeOV6wCfNsAippE=
@@ -1880,7 +1877,6 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
@@ -2081,7 +2077,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw=
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ=
@@ -2112,7 +2107,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
@@ -2135,7 +2129,6 @@ google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7E
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE=
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI=
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0=
@@ -2250,7 +2243,6 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ih
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
+6
View File
@@ -218,6 +218,8 @@ lineage: schemas: [{
// Optional field, if you want to extract part of a series name or metric node segment.
// Named capture groups can be used to separate the display text and value.
regex?: string
// Determine whether regex applies to variable value or display text
regexApplyTo?: #VariableRegexApplyTo
// Additional static options for query variable
staticOptions?: [...#VariableOption]
// Ordering of static options in relation to options returned from data source for query variable
@@ -245,6 +247,10 @@ lineage: schemas: [{
// Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing), 3 (show under the controls dropdown menu).
#VariableHide: 0 | 1 | 2 | 3 @cuetsy(kind="enum",memberNames="dontHide|hideLabel|hideVariable|inControlsMenu") @grafana(TSVeneer="type")
// Determine whether regex applies to variable value or display text
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
#VariableRegexApplyTo: "value" | "text" @cuetsy(kind="type")
// Sort variable options
// Accepted values are:
// `0`: No sorting
+2 -2
View File
@@ -296,8 +296,8 @@
"@grafana/plugin-ui": "^0.11.1",
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "6.47.1",
"@grafana/scenes-react": "6.47.1",
"@grafana/scenes": "6.49.0",
"@grafana/scenes-react": "6.49.0",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
+1
View File
@@ -521,6 +521,7 @@ export {
VariableRefresh,
VariableSort,
VariableHide,
type VariableRegexApplyTo,
type VariableType,
type VariableModel,
type TypedVariableModel,
+8
View File
@@ -261,6 +261,10 @@ export interface FeatureToggles {
*/
kubernetesCorrelations?: boolean;
/**
* Adds support for Kubernetes unified storage quotas
*/
kubernetesUnifiedStorageQuotas?: boolean;
/**
* Adds support for Kubernetes logs drilldown
*/
kubernetesLogsDrilldown?: boolean;
@@ -1220,4 +1224,8 @@ export interface FeatureToggles {
* @default false
*/
useMTPlugins?: boolean;
/**
* Enables support for variables whose values can have multiple properties
*/
multiPropsVariables?: boolean;
}
@@ -32,6 +32,8 @@ export enum VariableRefresh {
onTimeRangeChanged,
}
export type VariableRegexApplyTo = 'value' | 'text';
export enum VariableSort {
disabled,
alphabeticalAsc,
@@ -117,6 +119,7 @@ export interface QueryVariableModel extends VariableWithMultiSupport {
queryValue?: string;
query: any;
regex: string;
regexApplyTo?: VariableRegexApplyTo;
refresh: VariableRefresh;
staticOptions?: VariableOption[];
staticOptionsOrder?: 'before' | 'after' | 'sorted';
@@ -508,6 +508,9 @@ export const versionedPages = {
queryOptionsRegExInputV2: {
[MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query RegEx field',
},
queryOptionsRegExApplyToSelectV2: {
[MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Query RegExApplyTo select',
},
queryOptionsSortSelect: {
[MIN_GRAFANA_VERSION]: 'Variable editor Form Query Sort select',
},
+1
View File
@@ -12,6 +12,7 @@ export type {
AnnotationTarget,
AnnotationPanelFilter,
VariableOption,
VariableRegexApplyTo,
DashboardLink,
DashboardLinkType,
DashboardLinkPlacement,
@@ -187,6 +187,10 @@ export interface VariableModel {
* Named capture groups can be used to separate the display text and value.
*/
regex?: string;
/**
* Determine whether regex applies to variable value or display text
*/
regexApplyTo?: VariableRegexApplyTo;
/**
* Whether the variable value should be managed by URL query params or not
*/
@@ -259,6 +263,12 @@ export enum VariableHide {
inControlsMenu = 3,
}
/**
* Determine whether regex applies to variable value or display text
* Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
*/
export type VariableRegexApplyTo = ('value' | 'text');
/**
* Sort variable options
* Accepted values are:
@@ -293,6 +293,7 @@ export const handyTestingSchema: Spec = {
},
refresh: 'onDashboardLoad',
regex: 'regex1',
regexApplyTo: 'value',
skipUrlSync: false,
sort: 'disabled',
allowCustomValue: true,
@@ -1105,6 +1105,7 @@ export interface QueryVariableSpec {
datasource?: DataSourceRef;
query: DataQueryKind;
regex: string;
regexApplyTo?: VariableRegexApplyTo;
sort: VariableSort;
definition?: string;
options: VariableOption[];
@@ -1125,6 +1126,7 @@ export const defaultQueryVariableSpec = (): QueryVariableSpec => ({
skipUrlSync: false,
query: defaultDataQueryKind(),
regex: "",
regexApplyTo: "value",
sort: "disabled",
options: [],
multi: false,
@@ -1161,6 +1163,12 @@ export type VariableRefresh = "never" | "onDashboardLoad" | "onTimeRangeChanged"
export const defaultVariableRefresh = (): VariableRefresh => ("never");
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
export type VariableRegexApplyTo = "value" | "text";
export const defaultVariableRegexApplyTo = (): VariableRegexApplyTo => ("value");
// Sort variable options
// Accepted values are:
// `disabled`: No sorting
@@ -1111,6 +1111,7 @@ export interface QueryVariableSpec {
description?: string;
query: DataQueryKind;
regex: string;
regexApplyTo?: VariableRegexApplyTo;
sort: VariableSort;
definition?: string;
options: VariableOption[];
@@ -1131,6 +1132,7 @@ export const defaultQueryVariableSpec = (): QueryVariableSpec => ({
skipUrlSync: false,
query: defaultDataQueryKind(),
regex: "",
regexApplyTo: "value",
sort: "disabled",
options: [],
multi: false,
@@ -1167,6 +1169,12 @@ export type VariableRefresh = "never" | "onDashboardLoad" | "onTimeRangeChanged"
export const defaultVariableRefresh = (): VariableRefresh => ("never");
// Determine whether regex applies to variable value or display text
// Accepted values are `value` (apply to value used in queries) or `text` (apply to display text shown to users)
export type VariableRegexApplyTo = "value" | "text";
export const defaultVariableRegexApplyTo = (): VariableRegexApplyTo => ("value");
// Sort variable options
// Accepted values are:
// `disabled`: No sorting
+2 -1
View File
@@ -56,7 +56,8 @@ import (
_ "github.com/grafana/e2e"
_ "github.com/grafana/gofpdf"
_ "github.com/grafana/gomemcache/memcache"
_ "github.com/grafana/tempo/pkg/traceql"
_ "github.com/grafana/grafana/apps/alerting/alertenrichment/pkg/apis/alertenrichment/v1beta1"
_ "github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
_ "github.com/grafana/tempo/pkg/traceql"
)
+11
View File
@@ -834,6 +834,8 @@ type VariableModel struct {
// Optional field, if you want to extract part of a series name or metric node segment.
// Named capture groups can be used to separate the display text and value.
Regex *string `json:"regex,omitempty"`
// Determine whether regex applies to variable value or display text
RegexApplyTo *VariableRegexApplyTo `json:"regexApplyTo,omitempty"`
// Additional static options for query variable
StaticOptions []VariableOption `json:"staticOptions,omitempty"`
// Ordering of static options in relation to options returned from data source for query variable
@@ -942,6 +944,15 @@ const (
VariableSortNaturalDesc VariableSort = 8
)
// Determine whether regex applies to variable value or display text
// Accepted values are "value" (apply to value used in queries) or "text" (apply to display text shown to users)
type VariableRegexApplyTo string
const (
VariableRegexApplyToValue VariableRegexApplyTo = "value"
VariableRegexApplyToText VariableRegexApplyTo = "text"
)
// Contains the list of annotations that are associated with the dashboard.
// Annotations are used to overlay event markers and overlay event tags on graphs.
// Grafana comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the HTTP API.
@@ -95,3 +95,7 @@ func (d *directResourceClient) BulkProcess(ctx context.Context, opts ...grpc.Cal
func (b *directResourceClient) RebuildIndexes(ctx context.Context, req *resourcepb.RebuildIndexesRequest, opts ...grpc.CallOption) (*resourcepb.RebuildIndexesResponse, error) {
return nil, fmt.Errorf("not implemented")
}
func (b *directResourceClient) GetQuotaUsage(ctx context.Context, req *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
return nil, fmt.Errorf("not implemented")
}
@@ -140,11 +140,30 @@ func NewDashboardSQLAccess(sql legacysql.LegacyDatabaseProvider,
}
func (a *dashboardSqlAccess) executeQuery(ctx context.Context, helper *legacysql.LegacyDatabaseHelper, query string, args ...any) (*sql.Rows, error) {
// Use transaction if available in context.
var tx *sql.Tx
// After this function runs, the `tx` variable will only be set if
// this function was called in the context of a transaction set up by a
// caller upstream. In that case, we reuse the transaction.
_ = helper.DB.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
coreTx, err := sess.Tx()
if err != nil {
return nil
}
tx = coreTx.Tx
return nil
})
// Use transaction from unified storage if available in the context.
// This allows us to run migrations in a transaction which is specifically required for SQLite.
if tx := resource.TransactionFromContext(ctx); tx != nil {
if tx == nil {
tx = resource.TransactionFromContext(ctx)
}
if tx != nil {
return tx.QueryContext(ctx, query, args...)
}
return helper.DB.GetSqlxSession().Query(ctx, query, args...)
}
+10 -1
View File
@@ -132,10 +132,19 @@ func (a *dashboardSqlAccess) WriteEvent(ctx context.Context, event resource.Writ
}
} else {
failOnExisting := event.Type == resourcepb.WatchEvent_ADDED
after, _, err := a.SaveDashboard(ctx, info.OrgID, dash, failOnExisting)
sql, err := a.sql(ctx)
if err != nil {
return 0, err
}
var after *dashboard.Dashboard
if err := sql.DB.InTransaction(ctx, func(ctx context.Context) error {
var err error
after, _, err = a.SaveDashboard(ctx, info.OrgID, dash, failOnExisting)
return err
}); err != nil {
return 0, err
}
if after != nil {
meta, err := utils.MetaAccessor(after)
if err != nil {
@@ -1103,3 +1103,7 @@ func (m *MockClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption) (
func (m *MockClient) UpdateIndex(ctx context.Context, reason string) error {
return nil
}
func (m *MockClient) GetQuotaUsage(ctx context.Context, req *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
return nil, nil
}
+6 -6
View File
@@ -90,11 +90,11 @@ func RegisterAPIService(
builder := &IdentityAccessManagementAPIBuilder{
store: store,
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation),
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation),
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation),
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation),
ssoLegacyStore: sso.NewLegacyStore(ssoService),
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation, tracing),
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation, tracing),
ssoLegacyStore: sso.NewLegacyStore(ssoService, tracing),
coreRolesStorage: coreRolesStorage,
rolesStorage: rolesStorage,
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
@@ -114,7 +114,7 @@ func RegisterAPIService(
dual: dual,
unified: unified,
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(),
unified, user.NewUserLegacySearchClient(userService), features),
unified, user.NewUserLegacySearchClient(userService, tracing), features),
teamSearch: NewTeamSearchHandler(tracing, dual, team.NewLegacyTeamSearchClient(teamService), unified, features),
}
apiregistration.RegisterAPI(builder)
+16 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,14 +36,15 @@ var (
var resource = iamv0alpha1.ServiceAccountResourceInfo
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation}
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
}
type LegacyStore struct {
store legacy.LegacyIdentityStore
ac claims.AccessClient
enableAuthnMutation bool
tracer trace.Tracer
}
// DeleteCollection implements rest.CollectionDeleter.
@@ -52,6 +54,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
// Delete implements rest.GracefulDeleter.
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Delete")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
}
@@ -95,6 +100,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
// Create implements rest.Creater.
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Create")
defer span.End()
if !s.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
}
@@ -165,6 +173,9 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
}
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.List")
defer span.End()
res, err := common.List(
ctx, resource, s.ac, common.PaginationFromListOptions(options),
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.ServiceAccount], error) {
@@ -228,6 +239,9 @@ func extractPluginNameFromTitle(title string) string {
}
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "serviceaccount.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
+16 -2
View File
@@ -7,6 +7,7 @@ import (
commonv1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,12 +33,13 @@ var (
var resource = iamv0.SSOSettingResourceInfo
func NewLegacyStore(service ssosettings.Service) *LegacyStore {
return &LegacyStore{service}
func NewLegacyStore(service ssosettings.Service, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{service, tracer}
}
type LegacyStore struct {
service ssosettings.Service
tracer trace.Tracer
}
// Destroy implements rest.Storage.
@@ -71,6 +73,9 @@ func (s *LegacyStore) NewList() runtime.Object {
// List implements rest.Lister.
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "sso.List")
defer span.End()
ns, _ := request.NamespaceInfoFrom(ctx, false)
settings, err := s.service.List(ctx)
@@ -88,6 +93,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
// Get implements rest.Getter.
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "sso.Get")
defer span.End()
ns, _ := request.NamespaceInfoFrom(ctx, false)
setting, err := s.service.GetForProviderWithRedactedSecrets(ctx, name)
@@ -112,6 +120,9 @@ func (s *LegacyStore) Update(
_ bool,
_ *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "sso.Update")
defer span.End()
const created = false
ident, err := identity.GetRequester(ctx)
if err != nil {
@@ -148,6 +159,9 @@ func (s *LegacyStore) Delete(
_ rest.ValidateObjectFunc,
options *metav1.DeleteOptions,
) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "sso.Delete")
defer span.End()
obj, err := s.Get(ctx, name, nil)
if err != nil {
return obj, false, err
+19 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"strconv"
"go.opentelemetry.io/otel/trace"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -35,14 +36,15 @@ var (
var resource = iamv0alpha1.TeamResourceInfo
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation}
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
}
type LegacyStore struct {
store legacy.LegacyIdentityStore
ac claims.AccessClient
enableAuthnMutation bool
tracer trace.Tracer
}
func (s *LegacyStore) New() runtime.Object {
@@ -74,6 +76,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
// Delete implements rest.GracefulDeleter.
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "team.Delete")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
}
@@ -112,6 +117,9 @@ func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation
// Update implements rest.Updater.
func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := s.tracer.Start(ctx, "team.Update")
defer span.End()
if !s.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
}
@@ -161,6 +169,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
}
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.List")
defer span.End()
res, err := common.List(
ctx, resource, s.ac, common.PaginationFromListOptions(options),
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.Team], error) {
@@ -197,6 +208,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
}
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -219,6 +233,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
}
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := s.tracer.Start(ctx, "team.Create")
defer span.End()
if !s.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
}
@@ -284,3 +284,6 @@ func (m *MockClient) BulkProcess(ctx context.Context, opts ...grpc.CallOption) (
func (m *MockClient) UpdateIndex(ctx context.Context, reason string) error {
return nil
}
func (m *MockClient) GetQuotaUsage(ctx context.Context, in *resourcepb.QuotaUsageRequest, opts ...grpc.CallOption) (*resourcepb.QuotaUsageResponse, error) {
return nil, nil
}
+19 -2
View File
@@ -6,6 +6,7 @@ import (
"strconv"
"time"
"go.opentelemetry.io/otel/trace"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,13 +36,14 @@ var (
_ rest.CollectionDeleter = (*LegacyBindingStore)(nil)
)
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool) *LegacyBindingStore {
return &LegacyBindingStore{store, enableAuthnMutation}
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool, tracer trace.Tracer) *LegacyBindingStore {
return &LegacyBindingStore{store, enableAuthnMutation, tracer}
}
type LegacyBindingStore struct {
store legacy.LegacyIdentityStore
enableAuthnMutation bool
tracer trace.Tracer
}
// Destroy implements rest.Storage.
@@ -73,6 +75,9 @@ func (l *LegacyBindingStore) ConvertToTable(ctx context.Context, object runtime.
}
func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Update")
defer span.End()
if !l.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "update")
}
@@ -125,6 +130,9 @@ func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo re
}
func (l *LegacyBindingStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Delete")
defer span.End()
if !l.enableAuthnMutation {
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "delete")
}
@@ -160,6 +168,9 @@ func (l *LegacyBindingStore) DeleteCollection(ctx context.Context, deleteValidat
}
func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Create")
defer span.End()
if !l.enableAuthnMutation {
return nil, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "create")
}
@@ -230,6 +241,9 @@ func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, cre
// Get implements rest.Getter.
func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.Get")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
@@ -254,6 +268,9 @@ func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *meta
// List implements rest.Lister.
func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
ctx, span := l.tracer.Start(ctx, "teambinding.List")
defer span.End()
ns, err := request.NamespaceInfoFrom(ctx, true)
if err != nil {
return nil, err
+7 -1
View File
@@ -6,6 +6,7 @@ import (
"log/slog"
"math"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -27,13 +28,15 @@ type UserLegacySearchClient struct {
resourcepb.ResourceIndexClient
userService user.Service
log *slog.Logger
tracer trace.Tracer
}
// NewUserLegacySearchClient creates a new UserLegacySearchClient.
func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient {
func NewUserLegacySearchClient(userService user.Service, tracer trace.Tracer) *UserLegacySearchClient {
return &UserLegacySearchClient{
userService: userService,
log: slog.Default().With("logger", "legacy-user-search-client"),
tracer: tracer,
}
}
@@ -41,6 +44,9 @@ func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient
// It only supports exact matching for title, login, or email.
// FIXME: This implementation only supports a single field query and will be extended in the future.
func (c *UserLegacySearchClient) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest, _ ...grpc.CallOption) (*resourcepb.ResourceSearchResponse, error) {
ctx, span := c.tracer.Start(ctx, "user.Search")
defer span.End()
signedInUser, err := identity.GetRequester(ctx)
if err != nil {
return nil, err
@@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest"
res "github.com/grafana/grafana/pkg/storage/unified/resource"
@@ -17,7 +18,7 @@ import (
func TestUserLegacySearchClient_Search(t *testing.T) {
t.Run("should return error if no query fields are provided", func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
@@ -66,7 +67,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Limit: 10,
@@ -125,7 +126,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
t.Run("title should have precedence over login and email", func(t *testing.T) {
mockUserService := usertest.NewMockService(t)
client := NewUserLegacySearchClient(mockUserService)
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{

Some files were not shown because too many files have changed in this diff Show More