20b08ccaab
* Revert "Revert: Future-proofing query and data source model in Dashboard Sche… (#107985)" This reverts commit13a89d4ae3. * Revert "Revert "Schema V2: Simplify annotations v1<->v2 conversions" (#107984)" This reverts commit2b8c5bea1a. * make gen apps * e2e update * Use v2alpha2 by default (#108177) * Use v2alpha2 by default * Apply only DS changes to alpha2 * Use v2alpha2 by default except to query * Create a v2 index in @grafana/schema * Update path and apply lint * Update tests * Update imports to v2 status * Fix failing openapi test * Schemav2 breaking changes: conversion implementation (#108224) * provision v2alpha1 dashboard * Run conversions for DS refactor * Run snapshot testing on conversions * Normalize output name * Update snapshots to include all panel and variable cases * fix lint * fix lint * fix test and go lint * more go lint --------- Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com> Co-authored-by: Haris Rozajac <haris.rozajac12@gmail.com> * Schema v2: Introduce group/datasource convention to GroupBy and AdHoc variable (#108237) * Schema v2: Introduce group/datasource convention to GroupBy and AdHoc variables * add conversion * App Installer: Authorizer support (#108419) * Chore: use `satisfies` and remove a load of `any`s (#108397) use satisfies and remove a load of anys * improve logging and fail unified-storage migration with more than 0 errors (#108471) improve logging and fail unified-storage migration with more than 0 errors * fix conversion test * Secrets: Create more granular fixed roles for SecureValues (#108382) * Provisioning: Fix bug in job progress recording (#108440) Fix bug in job progress recording * Provisioning: Fix ImportAllPanelsFromLocalRepository test (#108441) * Provisioning: Skip flaky test * Fix flaky provisioning test * Fix lint --------- Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com> * BulkDeleteProvisionedResource: Move progress bar into a second step (#108417) * Move progress bar into a second step --------- Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> * [Dashboard Schema Codegen] Move dashboard CUE codegen block back up into kind body (#108476) [Dashboard Schema Codegen] Move dashboard CUE codegen block back up into kind body to make sure new versions have the same settings. --------- Co-authored-by: Haris Rozajac <haris.rozajac12@gmail.com> Co-authored-by: Todd Treece <360020+toddtreece@users.noreply.github.com> Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com> Co-authored-by: Will Assis <35489495+gassiss@users.noreply.github.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> Co-authored-by: Roberto Jiménez Sánchez <jszroberto@gmail.com> Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com> Co-authored-by: Yunwen Zheng <yunwen.zheng@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> Co-authored-by: Austin Pond <IfSentient@users.noreply.github.com> Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com> * Dashboard Schema V2: Refactor VizConfigKind to follow DataQueryKind convention (#108148) * Dashboards API: Register v2alpha2 API * Prepare conversion functions * Fix test * Refactor VizConfigKind to follow DataQueryKind convention * fix tests * use new dataquerykind convention alpha 2 * add conversion * fix tests * fix tests * fix another test * Fix merge --------- Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com> * fix k8s codegen * Update e2e-playwright/dashboards/TestV2Dashboard.json * Update e2e/dashboards/TestV2Dashboard.json * revert app generation for non-related apps * try again * another try * also revert folder and secret app generation * v2alpha1 provisioned dashboard * Fix kind * Fix conversion snapshots * Update API discovery registry * Rename to v2beta1 * Rename migrations * Update apps/dashboard/pkg/apis/dashboard/v2beta1/doc.go Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com> * Ensure conditional rendering and other non changed properties --------- Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com> Co-authored-by: Haris Rozajac <haris.rozajac12@gmail.com> Co-authored-by: Todd Treece <360020+toddtreece@users.noreply.github.com> Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com> Co-authored-by: Will Assis <35489495+gassiss@users.noreply.github.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com> Co-authored-by: Roberto Jiménez Sánchez <jszroberto@gmail.com> Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com> Co-authored-by: Yunwen Zheng <yunwen.zheng@grafana.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> Co-authored-by: Austin Pond <IfSentient@users.noreply.github.com> Co-authored-by: Haris Rozajac <58232930+harisrozajac@users.noreply.github.com> Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
223 lines
7.4 KiB
Go
223 lines
7.4 KiB
Go
package conversion
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"github.com/grafana/grafana/apps/dashboard/pkg/apis"
|
|
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/testutil"
|
|
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
)
|
|
|
|
func TestConversionMatrixExist(t *testing.T) {
|
|
// Initialize the migrator with a test data source provider
|
|
migration.Initialize(testutil.GetTestProvider())
|
|
|
|
versions := []v1.Object{
|
|
&dashv0.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV0"}}},
|
|
&dashv1.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV1"}}},
|
|
&dashv2alpha1.Dashboard{Spec: dashv2alpha1.DashboardSpec{Title: "dashboardV2alpha1"}},
|
|
&dashv2beta1.Dashboard{Spec: dashv2beta1.DashboardSpec{Title: "dashboardV2beta1"}},
|
|
}
|
|
|
|
scheme := runtime.NewScheme()
|
|
err := RegisterConversions(scheme)
|
|
require.NoError(t, err)
|
|
|
|
for idx, in := range versions {
|
|
kind := fmt.Sprintf("%T", in)[1:]
|
|
t.Run(kind, func(t *testing.T) {
|
|
for i, out := range versions {
|
|
if i == idx {
|
|
continue // skip the same version
|
|
}
|
|
err = scheme.Convert(in, out, nil)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Make sure we get the right title for each value
|
|
meta, err := utils.MetaAccessor(in)
|
|
require.NoError(t, err)
|
|
require.True(t, strings.HasPrefix(meta.FindTitle(""), "dashboard"))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeepCopyValid(t *testing.T) {
|
|
dash1 := &dashv0.Dashboard{}
|
|
meta1, err := utils.MetaAccessor(dash1)
|
|
require.NoError(t, err)
|
|
meta1.SetFolder("f1")
|
|
require.Equal(t, "f1", dash1.Annotations[utils.AnnoKeyFolder])
|
|
|
|
dash1Copy := dash1.DeepCopyObject()
|
|
metaCopy, err := utils.MetaAccessor(dash1Copy)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "f1", metaCopy.GetFolder())
|
|
|
|
// Changing a property on the copy should not effect the original
|
|
metaCopy.SetFolder("XYZ")
|
|
require.Equal(t, "f1", meta1.GetFolder()) // 💣💣💣
|
|
}
|
|
|
|
func TestDashboardConversionToAllVersions(t *testing.T) {
|
|
// Initialize the migrator with a test data source provider
|
|
migration.Initialize(testutil.GetTestProvider())
|
|
|
|
// Set up conversion scheme
|
|
scheme := runtime.NewScheme()
|
|
err := RegisterConversions(scheme)
|
|
require.NoError(t, err)
|
|
|
|
// Read all files from input directory
|
|
files, err := os.ReadDir(filepath.Join("testdata", "input"))
|
|
require.NoError(t, err, "Failed to read input directory")
|
|
|
|
for _, file := range files {
|
|
if file.IsDir() {
|
|
continue
|
|
}
|
|
|
|
t.Run(fmt.Sprintf("Convert_%s", file.Name()), func(t *testing.T) {
|
|
// Read input dashboard file
|
|
inputFile := filepath.Join("testdata", "input", file.Name())
|
|
// ignore gosec G304 as this function is only used in the test process
|
|
//nolint:gosec
|
|
inputData, err := os.ReadFile(inputFile)
|
|
require.NoError(t, err, "Failed to read input file")
|
|
|
|
// Parse the input dashboard to get its version
|
|
var rawDash map[string]interface{}
|
|
err = json.Unmarshal(inputData, &rawDash)
|
|
require.NoError(t, err, "Failed to unmarshal dashboard JSON")
|
|
|
|
// Extract apiVersion
|
|
apiVersion, ok := rawDash["apiVersion"].(string)
|
|
require.True(t, ok, "apiVersion not found or not a string")
|
|
|
|
// Parse group and version from apiVersion (format: "group/version")
|
|
parts := strings.Split(apiVersion, "/")
|
|
require.Equal(t, 2, len(parts), "apiVersion should be in format 'group/version'")
|
|
sourceVersion := parts[1]
|
|
|
|
// Create source object based on version
|
|
var sourceDash v1.Object
|
|
switch sourceVersion {
|
|
case "v0alpha1":
|
|
var dash dashv0.Dashboard
|
|
err = json.Unmarshal(inputData, &dash)
|
|
sourceDash = &dash
|
|
case "v1beta1":
|
|
var dash dashv1.Dashboard
|
|
err = json.Unmarshal(inputData, &dash)
|
|
sourceDash = &dash
|
|
case "v2alpha1":
|
|
var dash dashv2alpha1.Dashboard
|
|
err = json.Unmarshal(inputData, &dash)
|
|
sourceDash = &dash
|
|
case "v2beta1":
|
|
var dash dashv2beta1.Dashboard
|
|
err = json.Unmarshal(inputData, &dash)
|
|
sourceDash = &dash
|
|
default:
|
|
t.Fatalf("Unsupported source version: %s", sourceVersion)
|
|
}
|
|
require.NoError(t, err, "Failed to unmarshal dashboard into typed object")
|
|
|
|
// Ensure output directory exists
|
|
outDir := filepath.Join("testdata", "output")
|
|
// ignore gosec G301 as this function is only used in the test process
|
|
//nolint:gosec
|
|
err = os.MkdirAll(outDir, 0755)
|
|
require.NoError(t, err, "Failed to create output directory")
|
|
|
|
// Get target versions from the dashboard manifest
|
|
manifest := apis.LocalManifest()
|
|
targetVersions := make(map[string]runtime.Object)
|
|
|
|
// Get original filename without extension
|
|
originalName := strings.TrimSuffix(file.Name(), ".json")
|
|
|
|
// Get all Dashboard versions from the manifest
|
|
for _, kind := range manifest.ManifestData.Kinds() {
|
|
if kind.Kind == "Dashboard" {
|
|
for _, version := range kind.Versions {
|
|
// Skip converting to the same version
|
|
if version.VersionName == sourceVersion {
|
|
continue
|
|
}
|
|
|
|
filename := fmt.Sprintf("%s.%s.json", originalName, version.VersionName)
|
|
|
|
// Create target object based on version
|
|
switch version.VersionName {
|
|
case "v0alpha1":
|
|
targetVersions[filename] = &dashv0.Dashboard{}
|
|
case "v1beta1":
|
|
targetVersions[filename] = &dashv1.Dashboard{}
|
|
case "v2alpha1":
|
|
targetVersions[filename] = &dashv2alpha1.Dashboard{}
|
|
case "v2beta1":
|
|
targetVersions[filename] = &dashv2beta1.Dashboard{}
|
|
default:
|
|
t.Logf("Unknown version %s, skipping", version.VersionName)
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
// Convert to each target version
|
|
for filename, target := range targetVersions {
|
|
t.Run(fmt.Sprintf("Convert_to_%s", filename), func(t *testing.T) {
|
|
// Create a copy of the input dashboard for conversion
|
|
inputCopy := sourceDash.(runtime.Object).DeepCopyObject()
|
|
|
|
// Convert to target version
|
|
err = scheme.Convert(inputCopy, target, nil)
|
|
require.NoError(t, err, "Conversion failed for %s", filename)
|
|
|
|
// Test the changes in the conversion result
|
|
testConversion(t, target.(v1.Object), filename, outDir)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testConversion(t *testing.T, convertedDash v1.Object, filename, outputDir string) {
|
|
t.Helper()
|
|
|
|
outPath := filepath.Join(outputDir, filename)
|
|
outBytes, err := json.MarshalIndent(convertedDash, "", " ")
|
|
require.NoError(t, err, "failed to marshal converted dashboard")
|
|
|
|
if _, err := os.Stat(outPath); os.IsNotExist(err) {
|
|
err = os.WriteFile(outPath, outBytes, 0644)
|
|
require.NoError(t, err, "failed to write new output file %s", outPath)
|
|
t.Logf("✓ Created new output file: %s", filename)
|
|
return
|
|
}
|
|
|
|
// ignore gosec G304 as this function is only used in the test process
|
|
//nolint:gosec
|
|
existingBytes, err := os.ReadFile(outPath)
|
|
require.NoError(t, err, "failed to read existing output file")
|
|
require.JSONEq(t, string(existingBytes), string(outBytes), "%s did not match", outPath)
|
|
t.Logf("✓ Conversion to %s matches existing file", filename)
|
|
}
|