diff --git a/pkg/apis/dashboard/migration/schemaversion/migrations.go b/pkg/apis/dashboard/migration/schemaversion/migrations.go index 5875261aaeb..6d3955c329f 100644 --- a/pkg/apis/dashboard/migration/schemaversion/migrations.go +++ b/pkg/apis/dashboard/migration/schemaversion/migrations.go @@ -5,11 +5,12 @@ import "strconv" type SchemaVersionMigrationFunc func(map[string]interface{}) error const ( - MINIUM_VERSION = 37 + MINIUM_VERSION = 36 LATEST_VERSION = 40 ) var Migrations = map[int]SchemaVersionMigrationFunc{ + 37: V37, 38: V38, 39: V39, 40: V40, diff --git a/pkg/apis/dashboard/migration/schemaversion/v37.go b/pkg/apis/dashboard/migration/schemaversion/v37.go new file mode 100644 index 00000000000..c040daf842c --- /dev/null +++ b/pkg/apis/dashboard/migration/schemaversion/v37.go @@ -0,0 +1,62 @@ +package schemaversion + +// V37 normalizes legend configuration in panels to use a consistent format: +// - Converts boolean legend values to object format +// - Standardizes hidden legends to use showLegend: false with displayMode: list +// - Ensures visible legends have showLegend: true +func V37(dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = int(37) + + panels, ok := dashboard["panels"].([]interface{}) + if !ok { + return nil + } + + for _, panel := range panels { + p, ok := panel.(map[string]interface{}) + if !ok { + continue + } + + options, ok := p["options"].(map[string]interface{}) + if !ok { + continue + } + + // Skip if no legend config exists + legendValue := options["legend"] + if legendValue == nil { + continue + } + + // Convert boolean legend to object format + if legendBool, ok := legendValue.(bool); ok { + options["legend"] = map[string]interface{}{ + "displayMode": "list", + "showLegend": legendBool, + } + continue + } + + // Handle object format legend + legend, ok := legendValue.(map[string]interface{}) + if !ok { + continue + } + + displayMode, hasDisplayMode := legend["displayMode"].(string) + showLegend, hasShowLegend := legend["showLegend"].(bool) + + // Normalize hidden legends + if (hasDisplayMode && displayMode == "hidden") || (hasShowLegend && !showLegend) { + legend["displayMode"] = "list" + legend["showLegend"] = false + continue + } + + // Ensure visible legends have showLegend true + legend["showLegend"] = true + } + + return nil +} diff --git a/pkg/apis/dashboard/migration/schemaversion/v37_test.go b/pkg/apis/dashboard/migration/schemaversion/v37_test.go new file mode 100644 index 00000000000..e0b2f4c27cb --- /dev/null +++ b/pkg/apis/dashboard/migration/schemaversion/v37_test.go @@ -0,0 +1,170 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/apis/dashboard/migration/schemaversion" +) + +func TestV37(t *testing.T) { + tests := []migrationTestCase{ + { + name: "no legend config", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "options": map[string]interface{}{}, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "options": map[string]interface{}{}, + }, + }, + }, + }, + { + name: "boolean legend true", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "list", + "showLegend": true, + }, + }, + }, + }, + }, + }, + { + name: "boolean legend false", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": false, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "list", + "showLegend": false, + }, + }, + }, + }, + }, + }, + { + name: "hidden displayMode", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "hidden", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "list", + "showLegend": false, + }, + }, + }, + }, + }, + }, + { + name: "showLegend false", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "showLegend": false, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "list", + "showLegend": false, + }, + }, + }, + }, + }, + }, + { + name: "visible legend", + input: map[string]interface{}{ + "schemaVersion": 36, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "table", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 37, + "panels": []interface{}{ + map[string]interface{}{ + "options": map[string]interface{}{ + "legend": map[string]interface{}{ + "displayMode": "table", + "showLegend": true, + }, + }, + }, + }, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V37) +} diff --git a/pkg/apis/dashboard/migration/testdata/input/36.legend_normalization.json b/pkg/apis/dashboard/migration/testdata/input/36.legend_normalization.json new file mode 100644 index 00000000000..93ddfecb078 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/input/36.legend_normalization.json @@ -0,0 +1,123 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "type": "graph", + "options": {}, + "title": "No Legend Config", + "id": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + } + }, + { + "options": { + "legend": true + }, + "title": "Boolean Legend True", + "id": 2, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + } + }, + { + "options": { + "legend": false + }, + "title": "Boolean Legend False", + "id": 3, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + } + }, + { + "options": { + "legend": { + "displayMode": "hidden" + } + }, + "title": "Hidden DisplayMode", + "id": 4, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + } + }, + { + "options": { + "legend": { + "showLegend": false + } + }, + "title": "ShowLegend False", + "id": 5, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + } + }, + { + "options": { + "legend": { + "displayMode": "table" + } + }, + "title": "Visible Legend", + "id": 6, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + } + } + ], + "preload": false, + "refresh": true, + "schemaVersion": 36, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file diff --git a/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.37.json b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.37.json new file mode 100644 index 00000000000..1e6e0484aad --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.37.json @@ -0,0 +1,132 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": {}, + "title": "No Legend Config", + "type": "graph" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "displayMode": "list", + "showLegend": true + } + }, + "title": "Boolean Legend True" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Boolean Legend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Hidden DisplayMode" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "ShowLegend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "displayMode": "table", + "showLegend": true + } + }, + "title": "Visible Legend" + } + ], + "preload": false, + "refresh": true, + "schemaVersion": 37, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file diff --git a/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.38.json b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.38.json new file mode 100644 index 00000000000..14c5c32071f --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.38.json @@ -0,0 +1,132 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": {}, + "title": "No Legend Config", + "type": "graph" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "displayMode": "list", + "showLegend": true + } + }, + "title": "Boolean Legend True" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Boolean Legend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Hidden DisplayMode" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "ShowLegend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "displayMode": "table", + "showLegend": true + } + }, + "title": "Visible Legend" + } + ], + "preload": false, + "refresh": true, + "schemaVersion": 38, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file diff --git a/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.39.json b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.39.json new file mode 100644 index 00000000000..5e3239e89fa --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.39.json @@ -0,0 +1,132 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": {}, + "title": "No Legend Config", + "type": "graph" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "displayMode": "list", + "showLegend": true + } + }, + "title": "Boolean Legend True" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Boolean Legend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Hidden DisplayMode" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "ShowLegend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "displayMode": "table", + "showLegend": true + } + }, + "title": "Visible Legend" + } + ], + "preload": false, + "refresh": true, + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file diff --git a/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.40.json b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.40.json new file mode 100644 index 00000000000..91d625264b0 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/36.legend_normalization.40.json @@ -0,0 +1,132 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": {}, + "title": "No Legend Config", + "type": "graph" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "displayMode": "list", + "showLegend": true + } + }, + "title": "Boolean Legend True" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Boolean Legend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "Hidden DisplayMode" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "displayMode": "list", + "showLegend": false + } + }, + "title": "ShowLegend False" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "displayMode": "table", + "showLegend": true + } + }, + "title": "Visible Legend" + } + ], + "preload": false, + "refresh": "", + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file diff --git a/pkg/apis/dashboard/migration/testdata/output/37.timeseries_table_display_mode.37.json b/pkg/apis/dashboard/migration/testdata/output/37.timeseries_table_display_mode.37.json new file mode 100644 index 00000000000..4b7c2fa4572 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/37.timeseries_table_display_mode.37.json @@ -0,0 +1,358 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "basic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Basic Display Mode", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "gradient-gauge" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Gradient Gauge Display Mode", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "lcd-gauge" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "LCD Gauge Display Mode", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "color-background" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Color Background Display Mode", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "color-background-solid" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 5, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Color Background Solid Display Mode", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "displayMode": "some-other-mode" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 6, + "options": { + "showHeader": true + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "refId": "A" + } + ], + "title": "Other Display Mode", + "type": "table" + } + ], + "preload": false, + "refresh": true, + "schemaVersion": 37, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "utc", + "title": "New dashboard", + "version": 0, + "weekStart": "" +} \ No newline at end of file