mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Scuemata: Add error wrapper for CUE sanity errors (#33934)
* Add error wrapper for CUE sanity errors * Fix error * Fix linting * Update test * Update TestCueErrorWrapper assertions
This commit is contained in:
parent
0f0d94d65b
commit
2b1e6c136f
@ -5,11 +5,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"cuelang.org/go/cue"
|
"cuelang.org/go/cue"
|
||||||
|
errs "cuelang.org/go/cue/errors"
|
||||||
"cuelang.org/go/cue/load"
|
"cuelang.org/go/cue/load"
|
||||||
"github.com/grafana/grafana/pkg/schema"
|
"github.com/grafana/grafana/pkg/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
var panelSubpath cue.Path = cue.MakePath(cue.Def("#Panel"))
|
// cueError wraps errors caused by malformed cue files.
|
||||||
|
type cueError struct {
|
||||||
|
errors []errs.Error
|
||||||
|
filename string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
|
||||||
|
var panelSubpath = cue.MakePath(cue.Def("#Panel"))
|
||||||
|
|
||||||
func defaultOverlay(p BaseLoadPaths) (map[string]load.Source, error) {
|
func defaultOverlay(p BaseLoadPaths) (map[string]load.Source, error) {
|
||||||
overlay := make(map[string]load.Source)
|
overlay := make(map[string]load.Source)
|
||||||
@ -39,7 +47,10 @@ func BaseDashboardFamily(p BaseLoadPaths) (schema.VersionedCueSchema, error) {
|
|||||||
cfg := &load.Config{Overlay: overlay}
|
cfg := &load.Config{Overlay: overlay}
|
||||||
inst, err := rt.Build(load.Instances([]string{"/cue/data/gen.cue"}, cfg)[0])
|
inst, err := rt.Build(load.Instances([]string{"/cue/data/gen.cue"}, cfg)[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
cueErrors := wrapCUEError(err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("errors: %q, in file: %s, on line: %d", cueErrors.errors, cueErrors.filename, cueErrors.line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
famval := inst.Value().LookupPath(cue.MakePath(cue.Str("Family")))
|
famval := inst.Value().LookupPath(cue.MakePath(cue.Str("Family")))
|
||||||
@ -185,3 +196,15 @@ type CompositeDashboardSchema interface {
|
|||||||
schema.VersionedCueSchema
|
schema.VersionedCueSchema
|
||||||
LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error)
|
LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapCUEError(err error) cueError {
|
||||||
|
var cErr errs.Error
|
||||||
|
if ok := errors.As(err, &cErr); ok {
|
||||||
|
return cueError{
|
||||||
|
errors: errs.Errors(err),
|
||||||
|
filename: errs.Errors(err)[0].Position().File().Name(),
|
||||||
|
line: errs.Errors(err)[0].Position().Line(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cueError{}
|
||||||
|
}
|
||||||
|
@ -123,3 +123,24 @@ func TestPanelValidity(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCueErrorWrapper(t *testing.T) {
|
||||||
|
t.Run("Testing scuemata validity with valid cue schemas", func(t *testing.T) {
|
||||||
|
tempDir := os.DirFS(filepath.Join("testdata", "malformed_cue"))
|
||||||
|
|
||||||
|
var baseLoadPaths = BaseLoadPaths{
|
||||||
|
BaseCueFS: tempDir,
|
||||||
|
DistPluginCueFS: GetDefaultLoadPaths().DistPluginCueFS,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := BaseDashboardFamily(baseLoadPaths)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "in file")
|
||||||
|
require.Contains(t, err.Error(), "on line")
|
||||||
|
|
||||||
|
_, err = DistDashboardFamily(baseLoadPaths)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "in file")
|
||||||
|
require.Contains(t, err.Error(), "on line")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
1
pkg/schema/load/testdata/malformed_cue/cue/data/gen.cue
vendored
Normal file
1
pkg/schema/load/testdata/malformed_cue/cue/data/gen.cue
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
;;;;;;;
|
22
pkg/schema/load/testdata/malformed_cue/cue/scuemata/panel-plugin.cue
vendored
Normal file
22
pkg/schema/load/testdata/malformed_cue/cue/scuemata/panel-plugin.cue
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package scuemata
|
||||||
|
|
||||||
|
// Definition of the shape of a panel plugin's schema declarations in its
|
||||||
|
// schema.cue file.
|
||||||
|
//
|
||||||
|
// Note that these keys do not appear directly in any real JSON artifact;
|
||||||
|
// rather, they are composed into panel structures as they are defined within
|
||||||
|
// the larger Dashboard schema.
|
||||||
|
#PanelSchema: {
|
||||||
|
PanelOptions: {...}
|
||||||
|
PanelFieldConfig?: {...}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
// A lineage of panel schema
|
||||||
|
#PanelLineage: [#PanelSchema, ...#PanelSchema]
|
||||||
|
|
||||||
|
// Panel plugin-specific Family
|
||||||
|
#PanelFamily: {
|
||||||
|
lineages: [#PanelLineage, ...#PanelLineage]
|
||||||
|
migrations: [...#Migration]
|
||||||
|
}
|
60
pkg/schema/load/testdata/malformed_cue/cue/scuemata/scuemata.cue
vendored
Normal file
60
pkg/schema/load/testdata/malformed_cue/cue/scuemata/scuemata.cue
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package scuemata
|
||||||
|
|
||||||
|
// A family is a collection of schemas that specify a single kind of object,
|
||||||
|
// allowing evolution of the canonical schema for that kind of object over time.
|
||||||
|
//
|
||||||
|
// The schemas are organized into a list of Lineages, which are themselves ordered
|
||||||
|
// lists of schemas where each schema with its predecessor in the lineage.
|
||||||
|
//
|
||||||
|
// If it is desired to define a schema with a breaking schema relative to its
|
||||||
|
// predecessors, a new Lineage must be created, as well as a Migration that defines
|
||||||
|
// a mapping to the new schema from the latest schema in prior Lineage.
|
||||||
|
//
|
||||||
|
// The version number of a schema is not controlled by the schema itself, but by
|
||||||
|
// its position in the list of lineages - e.g., 0.0 corresponds to the first
|
||||||
|
// schema in the first lineage.
|
||||||
|
#Family: {
|
||||||
|
lineages: [#Lineage, ...#Lineage]
|
||||||
|
migrations: [...#Migration]
|
||||||
|
let lseq = lineages[len(lineages)-1]
|
||||||
|
latest: #LastSchema & {_p: lseq}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Lineage is a non-empty list containing an ordered series of schemas that
|
||||||
|
// all describe a single kind of object, where each schema is backwards
|
||||||
|
// compatible with its predecessor.
|
||||||
|
#Lineage: [{...}, ...{...}]
|
||||||
|
|
||||||
|
#LastSchema: {
|
||||||
|
_p: #Lineage
|
||||||
|
_p[len(_p)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Migration defines a relation between two schemas, "_from" and "_to". The
|
||||||
|
// relation expresses any complex mappings that must be performed to
|
||||||
|
// transform an input artifact valid with respect to the _from schema, into
|
||||||
|
// an artifact valid with respect to the _to schema. This is accomplished
|
||||||
|
// in two stages:
|
||||||
|
// 1. A Migration is initially defined by passing in schemas for _from and _to,
|
||||||
|
// and mappings that translate _from to _to are defined in _rel.
|
||||||
|
// 2. A concrete object may then be unified with _to, resulting in its values
|
||||||
|
// being mapped onto "result" by way of _rel.
|
||||||
|
//
|
||||||
|
// This is the absolute simplest possible definition of a Migration. It's
|
||||||
|
// incumbent on the implementor to manually ensure the correctness and
|
||||||
|
// completeness of the mapping. The primary value in defining such a generic
|
||||||
|
// structure is to allow comparably generic logic for migrating concrete
|
||||||
|
// artifacts through schema changes.
|
||||||
|
//
|
||||||
|
// If _to isn't backwards compatible (accretion-only) with _from, then _rel must
|
||||||
|
// explicitly enumerate every field in _from and map it to a field in _to, even
|
||||||
|
// if they're identical. This is laborious for anything outside trivially tiny
|
||||||
|
// schema. We'll want to eventually add helpers for whitelisting or blacklisting
|
||||||
|
// of paths in _from, so that migrations of larger schema can focus narrowly on
|
||||||
|
// the points of actual change.
|
||||||
|
#Migration: {
|
||||||
|
from: {...}
|
||||||
|
to: {...}
|
||||||
|
rel: {...}
|
||||||
|
result: to & rel
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user