diff --git a/pkg/apis/dashboard/migration/schemaversion/migrations.go b/pkg/apis/dashboard/migration/schemaversion/migrations.go index 1b8925e2885..f689894cf34 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 = 39 + MINIUM_VERSION = 38 LATEST_VERSION = 40 ) var Migrations = map[int]SchemaVersionMigrationFunc{ + 39: V39, 40: V40, } diff --git a/pkg/apis/dashboard/migration/schemaversion/migrations_test.go b/pkg/apis/dashboard/migration/schemaversion/migrations_test.go index 79be968d23b..b20f7e4f561 100644 --- a/pkg/apis/dashboard/migration/schemaversion/migrations_test.go +++ b/pkg/apis/dashboard/migration/schemaversion/migrations_test.go @@ -55,3 +55,21 @@ func TestGetSchemaVersion(t *testing.T) { }) } } + +type migrationTestCase struct { + name string + input map[string]interface{} + expected map[string]interface{} +} + +func runMigrationTests(t *testing.T, testCases []migrationTestCase, migrationFunc schemaversion.SchemaVersionMigrationFunc) { + t.Helper() + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + err := migrationFunc(tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, tt.input) + }) + } +} diff --git a/pkg/apis/dashboard/migration/schemaversion/v39.go b/pkg/apis/dashboard/migration/schemaversion/v39.go new file mode 100644 index 00000000000..b2881c2ee44 --- /dev/null +++ b/pkg/apis/dashboard/migration/schemaversion/v39.go @@ -0,0 +1,60 @@ +package schemaversion + +// V39 updates the configuration of the Timeseries to table transformation +// to support multiple options per query +func V39(dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = int(39) + + panels, ok := dashboard["panels"].([]interface{}) + if !ok { + return nil + } + + for _, panel := range panels { + p, ok := panel.(map[string]interface{}) + if !ok { + continue + } + + transformations, ok := p["transformations"].([]interface{}) + if !ok { + continue + } + + for _, transformation := range transformations { + t, ok := transformation.(map[string]interface{}) + if !ok { + continue + } + + // If we run into a timeSeriesTable transformation + // and it doesn't have undefined options then we migrate + if t["id"] != "timeSeriesTable" { + continue + } + + options, ok := t["options"].(map[string]interface{}) + if !ok { + continue + } + + refIdStats, ok := options["refIdToStat"].(map[string]interface{}) + if !ok { + continue + } + + // For each {refIdtoStat} record which maps refId to a statistic + // we add that to the stat property of the new + // RefIdTransformerOptions interface which includes multiple settings + transformationOptions := make(map[string]interface{}) + for refId, stat := range refIdStats { + transformationOptions[refId] = map[string]interface{}{"stat": stat} + } + + // Update the options + t["options"] = transformationOptions + } + } + + return nil +} diff --git a/pkg/apis/dashboard/migration/schemaversion/v39_test.go b/pkg/apis/dashboard/migration/schemaversion/v39_test.go new file mode 100644 index 00000000000..d204d31f0cf --- /dev/null +++ b/pkg/apis/dashboard/migration/schemaversion/v39_test.go @@ -0,0 +1,111 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/apis/dashboard/migration/schemaversion" +) + +func TestV39(t *testing.T) { + tests := []migrationTestCase{ + { + name: "no transformations", + input: map[string]interface{}{ + "schemaVersion": 38, + "title": "Test Dashboard", + "panels": []interface{}{ + map[string]interface{}{ + "title": "Panel 1", + }, + }, + }, + expected: map[string]interface{}{ + "title": "Test Dashboard", + "schemaVersion": 39, + "panels": []interface{}{ + map[string]interface{}{ + "title": "Panel 1", + }, + }, + }, + }, + { + name: "timeSeriesTable transformation with refIdToStat", + input: map[string]interface{}{ + "schemaVersion": 38, + "panels": []interface{}{ + map[string]interface{}{ + "transformations": []interface{}{ + map[string]interface{}{ + "id": "timeSeriesTable", + "options": map[string]interface{}{ + "refIdToStat": map[string]interface{}{ + "A": "mean", + "B": "max", + }, + }, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 39, + "panels": []interface{}{ + map[string]interface{}{ + "transformations": []interface{}{ + map[string]interface{}{ + "id": "timeSeriesTable", + "options": map[string]interface{}{ + "A": map[string]interface{}{ + "stat": "mean", + }, + "B": map[string]interface{}{ + "stat": "max", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "non-timeSeriesTable transformation is not modified", + input: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "transformations": []interface{}{ + map[string]interface{}{ + "id": "otherTransform", + "options": map[string]interface{}{ + "refIdToStat": map[string]interface{}{ + "A": "mean", + }, + }, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 39, + "panels": []interface{}{ + map[string]interface{}{ + "transformations": []interface{}{ + map[string]interface{}{ + "id": "otherTransform", + "options": map[string]interface{}{ + "refIdToStat": map[string]interface{}{ + "A": "mean", + }, + }, + }, + }, + }, + }, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V39) +} diff --git a/pkg/apis/dashboard/migration/schemaversion/v40_test.go b/pkg/apis/dashboard/migration/schemaversion/v40_test.go index 19e6c90aa79..179a2fbf655 100644 --- a/pkg/apis/dashboard/migration/schemaversion/v40_test.go +++ b/pkg/apis/dashboard/migration/schemaversion/v40_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/grafana/grafana/pkg/apis/dashboard/migration/schemaversion" - "github.com/stretchr/testify/require" ) func TestV40(t *testing.T) { @@ -50,21 +49,3 @@ func TestV40(t *testing.T) { runMigrationTests(t, tests, schemaversion.V40) } - -type migrationTestCase struct { - name string - input map[string]interface{} - expected map[string]interface{} -} - -func runMigrationTests(t *testing.T, testCases []migrationTestCase, migrationFunc schemaversion.SchemaVersionMigrationFunc) { - t.Helper() - - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - err := migrationFunc(tt.input) - require.NoError(t, err) - require.Equal(t, tt.expected, tt.input) - }) - } -} diff --git a/pkg/apis/dashboard/migration/testdata/input/38.transform_timeseries_table.json b/pkg/apis/dashboard/migration/testdata/input/38.transform_timeseries_table.json new file mode 100644 index 00000000000..e5f08f17c69 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/input/38.transform_timeseries_table.json @@ -0,0 +1,153 @@ +{ + "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": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "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, + "transformations": [ + { + "id": "timeSeriesTable", + "options": { + "refIdToStat": { + "A": "mean", + "B": "max" + } + } + } + ], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "B" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "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/38.transform_timeseries_table.39.json b/pkg/apis/dashboard/migration/testdata/output/38.transform_timeseries_table.39.json new file mode 100644 index 00000000000..136a2fb9d40 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/38.transform_timeseries_table.39.json @@ -0,0 +1,155 @@ +{ + "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": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "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": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "B" + } + ], + "title": "Panel Title", + "transformations": [ + { + "id": "timeSeriesTable", + "options": { + "A": { + "stat": "mean" + }, + "B": { + "stat": "max" + } + } + } + ], + "type": "timeseries" + } + ], + "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/38.transform_timeseries_table.40.json b/pkg/apis/dashboard/migration/testdata/output/38.transform_timeseries_table.40.json new file mode 100644 index 00000000000..4217f847e8c --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/38.transform_timeseries_table.40.json @@ -0,0 +1,155 @@ +{ + "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": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "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": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "B" + } + ], + "title": "Panel Title", + "transformations": [ + { + "id": "timeSeriesTable", + "options": { + "A": { + "stat": "mean" + }, + "B": { + "stat": "max" + } + } + } + ], + "type": "timeseries" + } + ], + "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/39.refresh_true.39.json b/pkg/apis/dashboard/migration/testdata/output/39.refresh_true.39.json new file mode 100644 index 00000000000..6c0cb4d1974 --- /dev/null +++ b/pkg/apis/dashboard/migration/testdata/output/39.refresh_true.39.json @@ -0,0 +1,134 @@ +{ + "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": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "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": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.5.0-81438", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "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