Dashboards: Add Dashboard Schema validation (2) (#103844)
* Activate schema validation and align underlying systems * update to save as v0 if not the right schema version * Resolve merge conflicts * Move RequireApiErrorStatus to tests package * Add mutation tests * Fix lint * Only do min version check if dashboard is v1 * Fix lint and disable provisioning test * Revert provisioning changes * Revert more tests and add schema test * Reran gen * SQL Dashboard save * Adjust APIVERSION * Fixed mutation test * Add logging on downgrade --------- Co-authored-by: Marco de Abreu <18629099+marcoabreu@users.noreply.github.com> Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
This commit is contained in:
co-authored by
Marco de Abreu
Stephanie Hingtgen
parent
07a225649d
commit
c47ab101d1
@@ -14,6 +14,7 @@ import (
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -30,6 +31,7 @@ func TestScanRow(t *testing.T) {
|
||||
store := &dashboardSqlAccess{
|
||||
namespacer: func(_ int64) string { return "default" },
|
||||
provisioning: provisioner,
|
||||
log: log.New("test"),
|
||||
}
|
||||
|
||||
columns := []string{"orgId", "dashboard_id", "name", "folder_uid", "deleted", "plugin_id", "origin_name", "origin_path", "origin_hash", "origin_ts", "created", "createdBy", "createdByID", "updated", "updatedBy", "updatedByID", "version", "message", "data", "api_version"}
|
||||
@@ -126,60 +128,95 @@ func TestScanRow(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildSaveDashboardCommand(t *testing.T) {
|
||||
mockStore := &dashboards.FakeDashboardStore{}
|
||||
access := &dashboardSqlAccess{
|
||||
dashStore: mockStore,
|
||||
testCases := []struct {
|
||||
name string
|
||||
schemaVersion int
|
||||
expectedAPI string
|
||||
}{
|
||||
{
|
||||
name: "with schema version 36 should save as v0alpha1",
|
||||
schemaVersion: 36,
|
||||
expectedAPI: "v0alpha1",
|
||||
},
|
||||
{
|
||||
name: "with schema version 41 should save as v1alpha1",
|
||||
schemaVersion: 41,
|
||||
expectedAPI: "v1alpha1",
|
||||
},
|
||||
{
|
||||
name: "with empty schema version should save as v0alpha1",
|
||||
schemaVersion: 0,
|
||||
expectedAPI: "v0alpha1",
|
||||
},
|
||||
}
|
||||
dash := &dashboard.Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: dashboard.APIVERSION,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dash",
|
||||
},
|
||||
Spec: common.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockStore := &dashboards.FakeDashboardStore{}
|
||||
access := &dashboardSqlAccess{
|
||||
dashStore: mockStore,
|
||||
log: log.New("test"),
|
||||
}
|
||||
|
||||
dashSpec := map[string]interface{}{
|
||||
"title": "Test Dashboard",
|
||||
"id": 123,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if tc.schemaVersion > 0 {
|
||||
dashSpec["schemaVersion"] = tc.schemaVersion
|
||||
}
|
||||
|
||||
dash := &dashboard.Dashboard{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: dashboard.APIVERSION,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-dash",
|
||||
},
|
||||
Spec: common.Unstructured{
|
||||
Object: dashSpec,
|
||||
},
|
||||
}
|
||||
|
||||
// fail if no user in context
|
||||
_, _, err := access.buildSaveDashboardCommand(context.Background(), 1, dash)
|
||||
require.Error(t, err)
|
||||
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
OrgRole: "Admin",
|
||||
})
|
||||
// create new dashboard
|
||||
mockStore.On("GetDashboard", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
cmd, created, err := access.buildSaveDashboardCommand(ctx, 1, dash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, created)
|
||||
require.NotNil(t, cmd)
|
||||
require.Equal(t, "test-dash", cmd.Dashboard.Get("uid").MustString())
|
||||
_, exists := cmd.Dashboard.CheckGet("id")
|
||||
require.False(t, exists) // id should be removed
|
||||
require.Equal(t, cmd.OrgID, int64(1))
|
||||
require.True(t, cmd.Overwrite)
|
||||
require.Equal(t, tc.expectedAPI, cmd.APIVersion) // verify expected API version
|
||||
|
||||
// now update existing dashboard
|
||||
mockStore.On("GetDashboard", mock.Anything, mock.Anything).Return(
|
||||
&dashboards.Dashboard{
|
||||
ID: 1234,
|
||||
Version: 2,
|
||||
APIVersion: dashboard.APIVERSION,
|
||||
}, nil).Once()
|
||||
cmd, created, err = access.buildSaveDashboardCommand(ctx, 1, dash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, created)
|
||||
require.NotNil(t, cmd)
|
||||
require.Equal(t, "test-dash", cmd.Dashboard.Get("uid").MustString())
|
||||
require.Equal(t, cmd.Dashboard.Get("id").MustInt64(), int64(1234)) // should set to existing ID
|
||||
require.Equal(t, cmd.Dashboard.Get("version").MustFloat64(), float64(2)) // version must be set - otherwise seen as a new dashboard in NewDashboardFromJson
|
||||
require.Equal(t, tc.expectedAPI, cmd.APIVersion) // verify expected API version
|
||||
require.Equal(t, cmd.OrgID, int64(1))
|
||||
require.True(t, cmd.Overwrite)
|
||||
})
|
||||
}
|
||||
|
||||
// fail if no user in context
|
||||
_, _, err := access.buildSaveDashboardCommand(context.Background(), 1, dash)
|
||||
require.Error(t, err)
|
||||
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
OrgRole: "Admin",
|
||||
})
|
||||
// create new dashboard
|
||||
mockStore.On("GetDashboard", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
cmd, created, err := access.buildSaveDashboardCommand(ctx, 1, dash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, created)
|
||||
require.NotNil(t, cmd)
|
||||
require.Equal(t, "test-dash", cmd.Dashboard.Get("uid").MustString())
|
||||
_, exists := cmd.Dashboard.CheckGet("id")
|
||||
require.False(t, exists) // id should be removed
|
||||
require.Equal(t, cmd.OrgID, int64(1))
|
||||
require.True(t, cmd.Overwrite)
|
||||
|
||||
// now update existing dashboard
|
||||
mockStore.On("GetDashboard", mock.Anything, mock.Anything).Return(
|
||||
&dashboards.Dashboard{
|
||||
ID: 1234,
|
||||
Version: 2,
|
||||
APIVersion: dashboard.APIVERSION,
|
||||
}, nil).Once()
|
||||
cmd, created, err = access.buildSaveDashboardCommand(ctx, 1, dash)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, created)
|
||||
require.NotNil(t, cmd)
|
||||
require.Equal(t, "test-dash", cmd.Dashboard.Get("uid").MustString())
|
||||
require.Equal(t, cmd.Dashboard.Get("id").MustInt64(), int64(1234)) // should set to existing ID
|
||||
require.Equal(t, cmd.Dashboard.Get("version").MustFloat64(), float64(2)) // version must be set - otherwise seen as a new dashboard in NewDashboardFromJson
|
||||
require.Equal(t, cmd.APIVersion, "v1alpha1") // should trim prefix
|
||||
require.Equal(t, cmd.OrgID, int64(1))
|
||||
require.True(t, cmd.Overwrite)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user