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
@@ -9,6 +9,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gh "github.com/google/go-github/v70/github"
|
||||
ghmock "github.com/migueleliasweb/go-github-mock/src/mock"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_CreatingAndGetting(t *testing.T) {
|
||||
@@ -119,6 +121,88 @@ func TestIntegrationProvisioning_CreatingAndGetting(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationProvisioning_FailInvalidSchema(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
t.Skip("Reenable this test once we enforce schema validation for provisioning")
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
const repo = "invalid-schema-tmp"
|
||||
// Set up the repository and the file to import.
|
||||
helper.CopyToProvisioningPath(t, "testdata/invalid-dashboard-schema.json", "invalid-dashboard-schema.json")
|
||||
|
||||
localTmp := helper.RenderObject(t, "testdata/local-write.json.tmpl", map[string]any{
|
||||
"Name": repo,
|
||||
"SyncEnabled": true,
|
||||
})
|
||||
_, err := helper.Repositories.Resource.Create(ctx, localTmp, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure the repo can read and validate the file
|
||||
_, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "invalid-dashboard-schema.json")
|
||||
status := helper.RequireApiErrorStatus(err, metav1.StatusReasonBadRequest, http.StatusBadRequest)
|
||||
require.Equal(t, status.Message, "Dry run failed: Dashboard.dashboard.grafana.app \"invalid-schema-uid\" is invalid: [spec.panels.0.repeatDirection: Invalid value: conflicting values \"h\" and \"this is not an allowed value\", spec.panels.0.repeatDirection: Invalid value: conflicting values \"v\" and \"this is not an allowed value\"]")
|
||||
|
||||
const invalidSchemaUid = "invalid-schema-uid"
|
||||
_, err = helper.Dashboards.Resource.Get(ctx, invalidSchemaUid, metav1.GetOptions{})
|
||||
require.Error(t, err, "invalid dashboard shouldn't exist")
|
||||
require.True(t, apierrors.IsNotFound(err))
|
||||
|
||||
var jobObj *unstructured.Unstructured
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
result := helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("jobs").
|
||||
Body(asJSON(&provisioning.JobSpec{
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{},
|
||||
})).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(t.Context())
|
||||
require.NoError(collect, result.Error())
|
||||
job, err := result.Get()
|
||||
require.NoError(collect, err)
|
||||
var ok bool
|
||||
jobObj, ok = job.(*unstructured.Unstructured)
|
||||
require.True(collect, ok, "expecting unstructured object, but got %T", job)
|
||||
}, time.Second*10, time.Millisecond*10, "Expected to be able to start a sync job")
|
||||
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
//helper.TriggerJobProcessing(t)
|
||||
result, err := helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{},
|
||||
"jobs", string(jobObj.GetUID()))
|
||||
|
||||
if apierrors.IsNotFound(err) {
|
||||
assert.Fail(collect, "job '%s' not found yet yet", jobObj.GetName())
|
||||
return // continue trying
|
||||
}
|
||||
|
||||
// Can fail fast here -- the jobs are immutable
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
job := &provisioning.Job{}
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(result.Object, job)
|
||||
require.NoError(t, err, "should convert to Job object")
|
||||
|
||||
require.Equal(t, provisioning.JobStateError, job.Status.State)
|
||||
require.Equal(t, job.Status.Message, "completed with errors")
|
||||
require.Equal(t, job.Status.Errors[0], "Dashboard.dashboard.grafana.app \"invalid-schema-uid\" is invalid: [spec.panels.0.repeatDirection: Invalid value: conflicting values \"h\" and \"this is not an allowed value\", spec.panels.0.repeatDirection: Invalid value: conflicting values \"v\" and \"this is not an allowed value\"]")
|
||||
}, time.Second*10, time.Millisecond*10, "Expected provisioning job to conclude with the status failed")
|
||||
|
||||
_, err = helper.Dashboards.Resource.Get(ctx, invalidSchemaUid, metav1.GetOptions{})
|
||||
require.Error(t, err, "invalid dashboard shouldn't have been created")
|
||||
require.True(t, apierrors.IsNotFound(err))
|
||||
|
||||
err = helper.Repositories.Resource.Delete(ctx, repo, metav1.DeleteOptions{}, "files", "invalid-dashboard-schema.json")
|
||||
require.NoError(t, err, "should delete the resource file")
|
||||
}
|
||||
|
||||
func TestIntegrationProvisioning_CreatingGitHubRepository(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"title": "Provisioning test - invalid schema",
|
||||
"uid": "invalid-schema-uid",
|
||||
"schemaVersion": 41,
|
||||
"tags": [
|
||||
"tag"
|
||||
],
|
||||
"revision": "this-is-not-a-number",
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 34,
|
||||
"options": {
|
||||
"content": "# All panels\n\nThis dashboard was created to quickly check accessiblity issues on a lot of panels at the same time ",
|
||||
"mode": "markdown"
|
||||
},
|
||||
"pluginVersion": "8.1.0-pre",
|
||||
"transparent": true,
|
||||
"type": "text",
|
||||
"repeat": "something",
|
||||
"repeatDirection": "this is not an allowed value"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user