Scuemata: Add test to validate devenv resources (#35810)

* Add test for devenv resources

* Refactor validation tests for grokkability

* Devenv dashboards error-tracking script

* Refactor to use cueerrors.Details()

* Further test refinement

* Close major elements of dashboard schema

* Centralize dashboard validation tests

General dashboard validation testing belongs in the load package.

* Better names for error context on glue CUE code

* Fixup validate-resource

Do only one of base or dist, and fix copied docs.

* Skip the devenv test

* Remove test for validateResources

* Fix shellcheck

* Backend linter

Co-authored-by: sam boyer <sdboyer@grafana.com>
This commit is contained in:
Dimitris Sotirakis
2021-07-15 20:08:03 -04:00
committed by GitHub
co-authored by sam boyer
parent 8de218d5f1
commit 2e0dc835cf
10 changed files with 115 additions and 109 deletions
+6 -1
View File
@@ -142,13 +142,18 @@ var cueCommands = []*cli.Command{
},
{
Name: "validate-resource",
Usage: "validate *.cue files in the project",
Usage: "validate resource files (e.g. dashboard JSON) against schema",
Action: runPluginCommand(cmd.validateResources),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dashboard",
Usage: "dashboard JSON file to validate",
},
&cli.BoolFlag{
Name: "base-only",
Usage: "validate using only base schema, not dist (includes plugin schema)",
Value: false,
},
},
},
}
@@ -1,10 +1,12 @@
package commands
import (
gerrors "errors"
"fmt"
"os"
"path/filepath"
"cuelang.org/go/cue/errors"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/grafana/grafana/pkg/schema"
"github.com/grafana/grafana/pkg/schema/load"
@@ -25,37 +27,31 @@ func (cmd Command) validateScuemataBasics(c utils.CommandLine) error {
}
func (cmd Command) validateResources(c utils.CommandLine) error {
resource := c.String("dashboard")
b, err := os.Open(filepath.Clean(resource))
filename := c.String("dashboard")
baseonly := c.Bool("base-only")
if filename == "" {
return gerrors.New("must specify dashboard to validate with --dashboard")
}
b, err := os.Open(filepath.Clean(filename))
res := schema.Resource{Value: b, Name: filename}
if err != nil {
return err
}
if err := validateResources(b, paths, load.BaseDashboardFamily); err != nil {
return err
var sch schema.VersionedCueSchema
if baseonly {
sch, err = load.BaseDashboardFamily(paths)
} else {
sch, err = load.DistDashboardFamily(paths)
}
if err := validateResources(b, paths, load.DistDashboardFamily); err != nil {
return err
}
return nil
}
func validateResources(resource interface{}, p load.BaseLoadPaths, loader func(p load.BaseLoadPaths) (schema.VersionedCueSchema, error)) error {
dash, err := loader(p)
if err != nil {
return fmt.Errorf("error while loading dashboard scuemata, err: %w", err)
}
// Validate checks that the resource is correct with respect to the schema.
if resource != nil {
err = dash.Validate(schema.Resource{Value: resource})
if err != nil {
return fmt.Errorf("failed validation: %w", err)
}
err = sch.Validate(res)
if err != nil {
return gerrors.New(errors.Details(err, nil))
}
return nil
}
@@ -1,9 +1,7 @@
package commands
import (
"io/fs"
"os"
"path/filepath"
"testing"
"testing/fstest"
@@ -67,55 +65,4 @@ func TestValidateScuemataBasics(t *testing.T) {
err = validateScuemata(baseLoadPaths, load.DistDashboardFamily)
assert.EqualError(t, err, "all schema should be valid with respect to basic CUE rules, Family.lineages.0.0: field #Panel not allowed")
})
t.Run("Testing validateResources against scuemata and resource inputs", func(t *testing.T) {
validPanel, err := os.ReadFile("testdata/panels/valid_resource_panel.json")
require.NoError(t, err)
invalidPanel, err := os.ReadFile("testdata/panels/invalid_resource_panel.json")
require.NoError(t, err)
filesystem := fstest.MapFS{
"valid.json": &fstest.MapFile{Data: validPanel},
"invalid.json": &fstest.MapFile{Data: invalidPanel},
}
mergedFS := mergefs.Merge(filesystem, defaultBaseLoadPaths.BaseCueFS)
var baseLoadPaths = load.BaseLoadPaths{
BaseCueFS: mergedFS,
DistPluginCueFS: defaultBaseLoadPaths.DistPluginCueFS,
}
require.NoError(t, fs.WalkDir(mergedFS, ".", func(path string, d fs.DirEntry, err error) error {
require.NoError(t, err)
if d.IsDir() || filepath.Ext(d.Name()) != ".json" {
return nil
}
if d.Name() == "valid.json" {
t.Run(path, func(t *testing.T) {
b, err := mergedFS.Open(path)
require.NoError(t, err, "failed to open dashboard file")
err = validateResources(b, baseLoadPaths, load.BaseDashboardFamily)
require.NoError(t, err, "error while loading base dashboard scuemata")
err = validateResources(b, baseLoadPaths, load.DistDashboardFamily)
require.NoError(t, err, "error while loading base dashboard scuemata")
})
}
if d.Name() == "invalid.json" {
t.Run(path, func(t *testing.T) {
b, err := mergedFS.Open(path)
require.NoError(t, err, "failed to open dashboard file")
err = validateResources(b, baseLoadPaths, load.BaseDashboardFamily)
assert.EqualError(t, err, "failed validation: Family.lineages.0.0.panels.0.type: incomplete value !=\"\"")
})
}
return nil
}))
})
}