Compare commits

...

53 Commits

Author SHA1 Message Date
Steve Simpson a6e9e2e49b Alerting: Improve errors from failed requests in historian app.
Passing an empty body to the query handler would result in the rather unhelpful
response of "EOF", so add some prefixes to errors in useful places.
2025-12-11 20:16:32 +00: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
656 changed files with 11526 additions and 4562 deletions
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/grafana/grafana-app-sdk/app"
@@ -49,7 +50,7 @@ func (n *Notification) QueryHandler(ctx context.Context, writer app.CustomRouteR
ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusBadRequest,
Message: err.Error(),
Message: fmt.Sprintf("parse request: %s", err.Error()),
}}
}
@@ -67,7 +68,7 @@ func (n *Notification) QueryHandler(ctx context.Context, writer app.CustomRouteR
ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusInternalServerError,
Message: err.Error(),
Message: fmt.Sprintf("notification query: %s", err.Error()),
}}
}
@@ -8,9 +8,8 @@ import (
func (stars *StarsSpec) Add(group, kind, name string) {
for i, r := range stars.Resource {
if r.Group == group && r.Kind == kind {
r.Names = append(r.Names, name)
slices.Sort(r.Names)
stars.Resource[i].Names = slices.Compact(r.Names)
stars.Resource[i].Names = append(r.Names, name)
stars.Normalize()
return
}
}
@@ -46,8 +45,15 @@ func (stars *StarsSpec) Normalize() {
resources := make([]StarsResource, 0, len(stars.Resource))
for _, r := range stars.Resource {
if len(r.Names) > 0 {
slices.Sort(r.Names)
r.Names = slices.Compact(r.Names) // removes any duplicates
unique := make([]string, 0, len(r.Names))
found := make(map[string]bool, len(r.Names))
for _, name := range r.Names {
if !found[name] {
unique = append(unique, name)
found[name] = true
}
}
r.Names = unique
resources = append(resources, r)
}
}
@@ -39,7 +39,7 @@ func TestStarsWrite(t *testing.T) {
Resource: []StarsResource{{
Group: "g",
Kind: "k",
Names: []string{"a", "b", "c", "x"}, // added "b" (and sorted)
Names: []string{"a", "b", "x", "c"}, // added c to the end
}},
},
}, {
+1
View File
@@ -57,6 +57,7 @@ require (
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/jaegertracing/jaeger-idl v0.5.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
+2
View File
@@ -112,6 +112,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/jaegertracing/jaeger-idl v0.5.0 h1:zFXR5NL3Utu7MhPg8ZorxtCBjHrL3ReM1VoB65FOFGE=
@@ -0,0 +1,454 @@
package conversion
import (
"context"
"sync/atomic"
"testing"
"time"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
dashv2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1"
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// countingDataSourceProvider tracks how many times Index() is called
type countingDataSourceProvider struct {
datasources []schemaversion.DataSourceInfo
callCount atomic.Int64
}
func newCountingDataSourceProvider(datasources []schemaversion.DataSourceInfo) *countingDataSourceProvider {
return &countingDataSourceProvider{
datasources: datasources,
}
}
func (p *countingDataSourceProvider) Index(_ context.Context) *schemaversion.DatasourceIndex {
p.callCount.Add(1)
return schemaversion.NewDatasourceIndex(p.datasources)
}
func (p *countingDataSourceProvider) getCallCount() int64 {
return p.callCount.Load()
}
// countingLibraryElementProvider tracks how many times GetLibraryElementInfo() is called
type countingLibraryElementProvider struct {
elements []schemaversion.LibraryElementInfo
callCount atomic.Int64
}
func newCountingLibraryElementProvider(elements []schemaversion.LibraryElementInfo) *countingLibraryElementProvider {
return &countingLibraryElementProvider{
elements: elements,
}
}
func (p *countingLibraryElementProvider) GetLibraryElementInfo(_ context.Context) []schemaversion.LibraryElementInfo {
p.callCount.Add(1)
return p.elements
}
func (p *countingLibraryElementProvider) getCallCount() int64 {
return p.callCount.Load()
}
// createTestV0Dashboard creates a minimal v0 dashboard for testing
// The dashboard has a datasource with UID only (no type) to force provider lookup
// and includes library panels to test library element provider caching
func createTestV0Dashboard(namespace, title string) *dashv0.Dashboard {
return &dashv0.Dashboard{
ObjectMeta: metav1.ObjectMeta{
Name: "test-dashboard",
Namespace: namespace,
},
Spec: common.Unstructured{
Object: map[string]interface{}{
"title": title,
"schemaVersion": schemaversion.LATEST_VERSION,
// Variables with datasource reference that requires lookup
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "query_var",
"type": "query",
"query": "label_values(up, job)",
// Datasource with UID only - type needs to be looked up
"datasource": map[string]interface{}{
"uid": "ds1",
// type is intentionally omitted to trigger provider lookup
},
},
},
},
"panels": []interface{}{
map[string]interface{}{
"id": 1,
"title": "Test Panel",
"type": "timeseries",
"targets": []interface{}{
map[string]interface{}{
// Datasource with UID only - type needs to be looked up
"datasource": map[string]interface{}{
"uid": "ds1",
},
},
},
},
// Library panel reference - triggers library element provider lookup
map[string]interface{}{
"id": 2,
"title": "Library Panel with Horizontal Repeat",
"type": "library-panel-ref",
"gridPos": map[string]interface{}{
"h": 8,
"w": 12,
"x": 0,
"y": 8,
},
"libraryPanel": map[string]interface{}{
"uid": "lib-panel-repeat-h",
"name": "Library Panel with Horizontal Repeat",
},
},
// Another library panel reference
map[string]interface{}{
"id": 3,
"title": "Library Panel without Repeat",
"type": "library-panel-ref",
"gridPos": map[string]interface{}{
"h": 3,
"w": 6,
"x": 0,
"y": 16,
},
"libraryPanel": map[string]interface{}{
"uid": "lib-panel-no-repeat",
"name": "Library Panel without Repeat",
},
},
},
},
},
}
}
// createTestV1Dashboard creates a minimal v1beta1 dashboard for testing
// The dashboard has a datasource with UID only (no type) to force provider lookup
// and includes library panels to test library element provider caching
func createTestV1Dashboard(namespace, title string) *dashv1.Dashboard {
return &dashv1.Dashboard{
ObjectMeta: metav1.ObjectMeta{
Name: "test-dashboard",
Namespace: namespace,
},
Spec: common.Unstructured{
Object: map[string]interface{}{
"title": title,
"schemaVersion": schemaversion.LATEST_VERSION,
// Variables with datasource reference that requires lookup
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{
"name": "query_var",
"type": "query",
"query": "label_values(up, job)",
// Datasource with UID only - type needs to be looked up
"datasource": map[string]interface{}{
"uid": "ds1",
// type is intentionally omitted to trigger provider lookup
},
},
},
},
"panels": []interface{}{
map[string]interface{}{
"id": 1,
"title": "Test Panel",
"type": "timeseries",
"targets": []interface{}{
map[string]interface{}{
// Datasource with UID only - type needs to be looked up
"datasource": map[string]interface{}{
"uid": "ds1",
},
},
},
},
// Library panel reference - triggers library element provider lookup
map[string]interface{}{
"id": 2,
"title": "Library Panel with Vertical Repeat",
"type": "library-panel-ref",
"gridPos": map[string]interface{}{
"h": 4,
"w": 6,
"x": 0,
"y": 8,
},
"libraryPanel": map[string]interface{}{
"uid": "lib-panel-repeat-v",
"name": "Library Panel with Vertical Repeat",
},
},
// Another library panel reference
map[string]interface{}{
"id": 3,
"title": "Library Panel without Repeat",
"type": "library-panel-ref",
"gridPos": map[string]interface{}{
"h": 3,
"w": 6,
"x": 6,
"y": 8,
},
"libraryPanel": map[string]interface{}{
"uid": "lib-panel-no-repeat",
"name": "Library Panel without Repeat",
},
},
},
},
},
}
}
// TestConversionCaching_V0_to_V2alpha1 verifies caching works when converting V0 to V2alpha1
func TestConversionCaching_V0_to_V2alpha1(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-h", Name: "Library Panel with Horizontal Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, time.Minute)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, time.Minute)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
// Convert multiple dashboards in the same namespace
numDashboards := 5
namespace := "default"
for i := 0; i < numDashboards; i++ {
source := createTestV0Dashboard(namespace, "Dashboard "+string(rune('A'+i)))
target := &dashv2alpha1.Dashboard{}
err := Convert_V0_to_V2alpha1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion %d should succeed", i)
require.NotNil(t, target.Spec)
}
// With caching, the underlying datasource provider should only be called once per namespace
// The test dashboard has datasources without type that require lookup
assert.Equal(t, int64(1), underlyingDS.getCallCount(),
"datasource provider should be called only once for %d conversions in same namespace", numDashboards)
// Library element provider should also be called only once per namespace due to caching
assert.Equal(t, int64(1), underlyingLE.getCallCount(),
"library element provider should be called only once for %d conversions in same namespace", numDashboards)
}
// TestConversionCaching_V0_to_V2beta1 verifies caching works when converting V0 to V2beta1
func TestConversionCaching_V0_to_V2beta1(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-h", Name: "Library Panel with Horizontal Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, time.Minute)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, time.Minute)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
numDashboards := 5
namespace := "default"
for i := 0; i < numDashboards; i++ {
source := createTestV0Dashboard(namespace, "Dashboard "+string(rune('A'+i)))
target := &dashv2beta1.Dashboard{}
err := Convert_V0_to_V2beta1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion %d should succeed", i)
require.NotNil(t, target.Spec)
}
assert.Equal(t, int64(1), underlyingDS.getCallCount(),
"datasource provider should be called only once for %d conversions in same namespace", numDashboards)
assert.Equal(t, int64(1), underlyingLE.getCallCount(),
"library element provider should be called only once for %d conversions in same namespace", numDashboards)
}
// TestConversionCaching_V1beta1_to_V2alpha1 verifies caching works when converting V1beta1 to V2alpha1
func TestConversionCaching_V1beta1_to_V2alpha1(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-v", Name: "Library Panel with Vertical Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, time.Minute)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, time.Minute)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
numDashboards := 5
namespace := "default"
for i := 0; i < numDashboards; i++ {
source := createTestV1Dashboard(namespace, "Dashboard "+string(rune('A'+i)))
target := &dashv2alpha1.Dashboard{}
err := Convert_V1beta1_to_V2alpha1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion %d should succeed", i)
require.NotNil(t, target.Spec)
}
assert.Equal(t, int64(1), underlyingDS.getCallCount(),
"datasource provider should be called only once for %d conversions in same namespace", numDashboards)
assert.Equal(t, int64(1), underlyingLE.getCallCount(),
"library element provider should be called only once for %d conversions in same namespace", numDashboards)
}
// TestConversionCaching_V1beta1_to_V2beta1 verifies caching works when converting V1beta1 to V2beta1
func TestConversionCaching_V1beta1_to_V2beta1(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-v", Name: "Library Panel with Vertical Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, time.Minute)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, time.Minute)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
numDashboards := 5
namespace := "default"
for i := 0; i < numDashboards; i++ {
source := createTestV1Dashboard(namespace, "Dashboard "+string(rune('A'+i)))
target := &dashv2beta1.Dashboard{}
err := Convert_V1beta1_to_V2beta1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion %d should succeed", i)
require.NotNil(t, target.Spec)
}
assert.Equal(t, int64(1), underlyingDS.getCallCount(),
"datasource provider should be called only once for %d conversions in same namespace", numDashboards)
assert.Equal(t, int64(1), underlyingLE.getCallCount(),
"library element provider should be called only once for %d conversions in same namespace", numDashboards)
}
// TestConversionCaching_MultipleNamespaces verifies that different namespaces get separate cache entries
func TestConversionCaching_MultipleNamespaces(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-h", Name: "Library Panel with Horizontal Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, time.Minute)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, time.Minute)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
namespaces := []string{"default", "org-2", "org-3"}
numDashboardsPerNs := 3
for _, ns := range namespaces {
for i := 0; i < numDashboardsPerNs; i++ {
source := createTestV0Dashboard(ns, "Dashboard "+string(rune('A'+i)))
target := &dashv2alpha1.Dashboard{}
err := Convert_V0_to_V2alpha1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion for namespace %s should succeed", ns)
}
}
// With caching, each namespace should result in one call to the underlying provider
expectedCalls := int64(len(namespaces))
assert.Equal(t, expectedCalls, underlyingDS.getCallCount(),
"datasource provider should be called once per namespace (%d namespaces)", len(namespaces))
assert.Equal(t, expectedCalls, underlyingLE.getCallCount(),
"library element provider should be called once per namespace (%d namespaces)", len(namespaces))
}
// TestConversionCaching_CacheDisabled verifies that TTL=0 disables caching
func TestConversionCaching_CacheDisabled(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Prometheus", Default: true},
}
elements := []schemaversion.LibraryElementInfo{
{UID: "lib-panel-repeat-h", Name: "Library Panel with Horizontal Repeat", Type: "timeseries"},
{UID: "lib-panel-no-repeat", Name: "Library Panel without Repeat", Type: "graph"},
}
underlyingDS := newCountingDataSourceProvider(datasources)
underlyingLE := newCountingLibraryElementProvider(elements)
// TTL of 0 should disable caching - the wrapper returns the underlying provider directly
cachedDS := schemaversion.WrapIndexProviderWithCache(underlyingDS, 0)
cachedLE := schemaversion.WrapLibraryElementProviderWithCache(underlyingLE, 0)
migration.ResetForTesting()
migration.Initialize(cachedDS, cachedLE, migration.DefaultCacheTTL)
numDashboards := 3
namespace := "default"
for i := 0; i < numDashboards; i++ {
source := createTestV0Dashboard(namespace, "Dashboard "+string(rune('A'+i)))
target := &dashv2alpha1.Dashboard{}
err := Convert_V0_to_V2alpha1(source, target, nil, cachedDS, cachedLE)
require.NoError(t, err, "conversion %d should succeed", i)
}
// Without caching, each conversion calls the underlying provider multiple times
// (once for each datasource lookup needed - variables and panels)
// The key check is that the count is GREATER than 1 per conversion (no caching benefit)
assert.Greater(t, underlyingDS.getCallCount(), int64(numDashboards),
"with cache disabled, conversions should call datasource provider multiple times")
// Library element provider is also called for each conversion without caching
assert.GreaterOrEqual(t, underlyingLE.getCallCount(), int64(numDashboards),
"with cache disabled, conversions should call library element provider multiple times")
}
@@ -4,8 +4,6 @@ import (
"errors"
"fmt"
"k8s.io/apimachinery/pkg/conversion"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
@@ -121,6 +119,14 @@ func countPanelsV0V1(spec map[string]interface{}) int {
return count
}
// countTargetsFromPanel counts the number of targets/queries in a panel.
func countTargetsFromPanel(panelMap map[string]interface{}) int {
if targets, ok := panelMap["targets"].([]interface{}); ok {
return len(targets)
}
return 0
}
// countQueriesV0V1 counts data queries in v0alpha1 or v1beta1 dashboard spec
// Note: Row panels are layout containers and should not have queries.
// We ignore any queries on row panels themselves, but count queries in their collapsed panels.
@@ -145,9 +151,7 @@ func countQueriesV0V1(spec map[string]interface{}) int {
// Count queries in regular panels (NOT row panels)
if panelType != "row" {
if targets, ok := panelMap["targets"].([]interface{}); ok {
count += len(targets)
}
count += countTargetsFromPanel(panelMap)
}
// Count queries in collapsed panels inside row panels
@@ -155,9 +159,7 @@ func countQueriesV0V1(spec map[string]interface{}) int {
if collapsedPanels, ok := panelMap["panels"].([]interface{}); ok {
for _, cp := range collapsedPanels {
if cpMap, ok := cp.(map[string]interface{}); ok {
if targets, ok := cpMap["targets"].([]interface{}); ok {
count += len(targets)
}
count += countTargetsFromPanel(cpMap)
}
}
}
@@ -442,77 +444,3 @@ func collectDashboardStats(dashboard interface{}) dashboardStats {
}
return dashboardStats{}
}
// withConversionDataLossDetection wraps a conversion function to detect data loss
func withConversionDataLossDetection(sourceFuncName, targetFuncName string, conversionFunc func(a, b interface{}, scope conversion.Scope) error) func(a, b interface{}, scope conversion.Scope) error {
return func(a, b interface{}, scope conversion.Scope) error {
// Collect source statistics
var sourceStats dashboardStats
switch source := a.(type) {
case *dashv0.Dashboard:
if source.Spec.Object != nil {
sourceStats = collectStatsV0V1(source.Spec.Object)
}
case *dashv1.Dashboard:
if source.Spec.Object != nil {
sourceStats = collectStatsV0V1(source.Spec.Object)
}
case *dashv2alpha1.Dashboard:
sourceStats = collectStatsV2alpha1(source.Spec)
case *dashv2beta1.Dashboard:
sourceStats = collectStatsV2beta1(source.Spec)
}
// Execute the conversion
err := conversionFunc(a, b, scope)
if err != nil {
return err
}
// Collect target statistics
var targetStats dashboardStats
switch target := b.(type) {
case *dashv0.Dashboard:
if target.Spec.Object != nil {
targetStats = collectStatsV0V1(target.Spec.Object)
}
case *dashv1.Dashboard:
if target.Spec.Object != nil {
targetStats = collectStatsV0V1(target.Spec.Object)
}
case *dashv2alpha1.Dashboard:
targetStats = collectStatsV2alpha1(target.Spec)
case *dashv2beta1.Dashboard:
targetStats = collectStatsV2beta1(target.Spec)
}
// Detect if data was lost
if dataLossErr := detectConversionDataLoss(sourceStats, targetStats, sourceFuncName, targetFuncName); dataLossErr != nil {
logger.Error("Dashboard conversion data loss detected",
"sourceFunc", sourceFuncName,
"targetFunc", targetFuncName,
"sourcePanels", sourceStats.panelCount,
"targetPanels", targetStats.panelCount,
"sourceQueries", sourceStats.queryCount,
"targetQueries", targetStats.queryCount,
"sourceAnnotations", sourceStats.annotationCount,
"targetAnnotations", targetStats.annotationCount,
"sourceLinks", sourceStats.linkCount,
"targetLinks", targetStats.linkCount,
"error", dataLossErr,
)
return dataLossErr
}
logger.Debug("Dashboard conversion completed without data loss",
"sourceFunc", sourceFuncName,
"targetFunc", targetFuncName,
"panels", targetStats.panelCount,
"queries", targetStats.queryCount,
"annotations", targetStats.annotationCount,
"links", targetStats.linkCount,
)
return nil
}
}
File diff suppressed because it is too large Load Diff
@@ -35,7 +35,7 @@ func TestConversionMatrixExist(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
versions := []metav1.Object{
&dashv0.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV0"}}},
@@ -89,7 +89,7 @@ func TestDashboardConversionToAllVersions(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Set up conversion scheme
scheme := runtime.NewScheme()
@@ -309,7 +309,7 @@ func TestMigratedDashboardsConversion(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Set up conversion scheme
scheme := runtime.NewScheme()
@@ -428,7 +428,7 @@ func setupTestConversionScheme(t *testing.T) *runtime.Scheme {
t.Helper()
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
leProvider := migrationtestutil.NewLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
scheme := runtime.NewScheme()
err := RegisterConversions(scheme, dsProvider, leProvider)
@@ -527,7 +527,7 @@ func TestConversionMetrics(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Create a test registry for metrics
registry := prometheus.NewRegistry()
@@ -694,7 +694,7 @@ func TestConversionMetricsWrapper(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Create a test registry for metrics
registry := prometheus.NewRegistry()
@@ -864,7 +864,7 @@ func TestSchemaVersionExtraction(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Create a test registry for metrics
registry := prometheus.NewRegistry()
@@ -910,7 +910,7 @@ func TestConversionLogging(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
// Create a test registry for metrics
registry := prometheus.NewRegistry()
@@ -1003,7 +1003,7 @@ func TestConversionLogLevels(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
t.Run("log levels and structured fields verification", func(t *testing.T) {
// Create test wrapper to verify logging behavior
@@ -1076,7 +1076,7 @@ func TestConversionLoggingFields(t *testing.T) {
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
// Use TestLibraryElementProvider for tests that need library panel models with repeat options
leProvider := migrationtestutil.NewTestLibraryElementProvider()
migration.Initialize(dsProvider, leProvider)
migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL)
t.Run("verify all log fields are present", func(t *testing.T) {
// Test that the conversion wrapper includes all expected structured fields
@@ -17,7 +17,9 @@ import (
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
var logger = logging.DefaultLogger.With("logger", "dashboard.conversion")
func getLogger() logging.Logger {
return logging.DefaultLogger.With("logger", "dashboard.conversion")
}
// getErroredSchemaVersionFunc determines the schema version function that errored
func getErroredSchemaVersionFunc(err error) string {
@@ -197,9 +199,9 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion
)
if errorType == "schema_minimum_version_error" {
logger.Warn("Dashboard conversion failed", logFields...)
getLogger().Warn("Dashboard conversion failed", logFields...)
} else {
logger.Error("Dashboard conversion failed", logFields...)
getLogger().Error("Dashboard conversion failed", logFields...)
}
} else {
// Record success metrics
@@ -235,7 +237,7 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion
)
}
logger.Debug("Dashboard conversion succeeded", successLogFields...)
getLogger().Debug("Dashboard conversion succeeded", successLogFields...)
}
return nil
@@ -76,9 +76,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": true,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -155,9 +155,9 @@
"barGlow": false,
"centerGlow": true,
"rounded": true,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -234,9 +234,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -313,9 +313,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -392,9 +392,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -471,9 +471,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": false,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -550,9 +550,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": false,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -642,9 +642,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -721,9 +721,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -800,9 +800,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -879,9 +879,9 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -975,9 +975,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1054,9 +1054,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1133,9 +1133,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": true
},
"gradient": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1212,9 +1212,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1291,9 +1291,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1387,9 +1387,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": true
},
"gradient": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1470,9 +1470,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": true
},
"gradient": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1553,9 +1553,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": true
},
"gradient": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1645,10 +1645,10 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1731,10 +1731,10 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "scheme",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1831,10 +1831,10 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "scheme",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1919,10 +1919,10 @@
"centerGlow": true,
"rounded": true,
"sparkline": false,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "scheme",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -2005,10 +2005,10 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "hue",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -2091,10 +2091,10 @@
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true
"spotlight": true,
"gradient": true
},
"glow": "both",
"gradient": "hue",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -2147,4 +2147,4 @@
"title": "Panel tests - Gauge (new)",
"uid": "panel-tests-gauge-new",
"weekStart": ""
}
}
@@ -956,9 +956,9 @@
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false
"spotlight": false,
"gradient": false
},
"gradient": "none",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1162,4 +1162,4 @@
"title": "Panel tests - Old gauge to new",
"uid": "panel-tests-old-gauge-to-new",
"weekStart": ""
}
}
@@ -237,5 +237,10 @@
"title": "V10 Table Thresholds Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -244,5 +244,10 @@
"title": "V10 Table Thresholds Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -206,5 +206,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -213,5 +213,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -203,5 +203,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -216,5 +216,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -351,5 +351,10 @@
"title": "V13 Graph Thresholds Migration Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -362,5 +362,10 @@
"title": "V13 Graph Thresholds Migration Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -129,5 +129,10 @@
"title": "Dashboard with minimal graph panel settings",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -132,5 +132,10 @@
"title": "Dashboard with minimal graph panel settings",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -210,5 +210,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -217,5 +217,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1004,5 +1004,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1023,5 +1023,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -223,5 +223,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -231,5 +231,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1455,5 +1455,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1481,5 +1481,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -719,5 +719,10 @@
"title": "V16 Grid Layout Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -739,5 +739,10 @@
"title": "V16 Grid Layout Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1655,5 +1655,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1707,5 +1707,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -636,5 +636,10 @@
"title": "V17 MinSpan to MaxPerRow Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -657,5 +657,10 @@
"title": "V17 MinSpan to MaxPerRow Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -401,5 +401,10 @@
"title": "V18 Gauge Options Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -412,5 +412,10 @@
"title": "V18 Gauge Options Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -434,5 +434,10 @@
"title": "V19 Panel Links Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -447,5 +447,10 @@
"title": "V19 Panel Links Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -354,5 +354,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -365,5 +365,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -426,5 +426,10 @@
"title": "V20 Variable Syntax Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -437,5 +437,10 @@
"title": "V20 Variable Syntax Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -401,5 +401,10 @@
"title": "V21 Data Links Series to Field Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -412,5 +412,10 @@
"title": "V21 Data Links Series to Field Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -123,5 +123,10 @@
"title": "V22 Table Panel Styles Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -126,5 +126,10 @@
"title": "V22 Table Panel Styles Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -374,5 +374,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -391,5 +391,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1065,5 +1065,10 @@
"title": "No Title",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1101,5 +1101,10 @@
"title": "No Title",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -217,5 +217,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -226,5 +226,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -240,5 +240,10 @@
"title": "No Title",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -247,5 +247,10 @@
"title": "No Title",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -207,5 +207,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -211,5 +211,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -131,5 +131,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -134,5 +134,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -393,5 +393,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -406,5 +406,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -622,5 +622,10 @@
"title": "V28 Singlestat and Variable Properties Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -642,5 +642,10 @@
"title": "V28 Singlestat and Variable Properties Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -435,5 +435,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -458,5 +458,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -294,5 +294,10 @@
"title": "V3 No-Op Migration - but tests ensuring panel IDs are unique",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -303,5 +303,10 @@
"title": "V3 No-Op Migration - but tests ensuring panel IDs are unique",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -689,5 +689,10 @@
"title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -705,5 +705,10 @@
"title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -669,5 +669,10 @@
"title": "V31 LabelsToFields Merge Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -684,5 +684,10 @@
"title": "V31 LabelsToFields Merge Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -310,5 +310,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -320,5 +320,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -720,5 +720,10 @@
"title": "V33 Panel Datasource Name to Ref Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -746,5 +746,10 @@
"title": "V33 Panel Datasource Name to Ref Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1900,5 +1900,10 @@
"title": "CloudWatch Multiple Statistics Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1964,5 +1964,10 @@
"title": "CloudWatch Multiple Statistics Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -595,5 +595,10 @@
"title": "X-Axis Visibility Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -612,5 +612,10 @@
"title": "X-Axis Visibility Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1029,5 +1029,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1065,5 +1065,10 @@
}
]
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -629,5 +629,10 @@
"title": "V37 Legend Normalization Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -642,5 +642,10 @@
"title": "V37 Legend Normalization Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -659,5 +659,10 @@
"title": "V38 Table Migration Comprehensive Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -671,5 +671,10 @@
"title": "V38 Table Migration Comprehensive Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -659,5 +659,10 @@
"title": "V38 Table Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -671,5 +671,10 @@
"title": "V38 Table Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -586,5 +586,10 @@
"title": "V39 TimeSeriesTable Transformation Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -596,5 +596,10 @@
"title": "V39 TimeSeriesTable Transformation Migration Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -237,5 +237,10 @@
"title": "V4 No-Op Migration Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -244,5 +244,10 @@
"title": "V4 No-Op Migration Test",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -64,5 +64,10 @@
"title": "Empty String Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -65,5 +65,10 @@
"title": "Empty String Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -64,5 +64,10 @@
"title": "Boolean False Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -65,5 +65,10 @@
"title": "Boolean False Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -64,5 +64,10 @@
"title": "Refresh Not Set Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -65,5 +65,10 @@
"title": "Refresh Not Set Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -64,5 +64,10 @@
"title": "Numeric Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -65,5 +65,10 @@
"title": "Numeric Refresh Test Dashboard",
"variables": []
},
"status": {}
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}

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