mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
a7ea0ca849
* refactory to move trim/apply default into schema.go * fix comments * move schema test outside of load folder
188 lines
5.4 KiB
Go
188 lines
5.4 KiB
Go
package load
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"cuelang.org/go/cue"
|
|
"cuelang.org/go/cue/load"
|
|
"github.com/grafana/grafana/pkg/schema"
|
|
)
|
|
|
|
var panelSubpath cue.Path = cue.MakePath(cue.Def("#Panel"))
|
|
|
|
func defaultOverlay(p BaseLoadPaths) (map[string]load.Source, error) {
|
|
overlay := make(map[string]load.Source)
|
|
if err := toOverlay("/", p.BaseCueFS, overlay); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := toOverlay("/", p.DistPluginCueFS, overlay); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return overlay, nil
|
|
}
|
|
|
|
// BaseDashboardFamily loads the family of schema representing the "Base" variant of
|
|
// a Grafana dashboard: the core-defined dashboard schema that applies universally to
|
|
// all dashboards, independent of any plugins.
|
|
//
|
|
// The returned VersionedCueSchema will always be the oldest schema in the
|
|
// family: the 0.0 schema. schema.Find() provides easy traversal to newer schema
|
|
// versions.
|
|
func BaseDashboardFamily(p BaseLoadPaths) (schema.VersionedCueSchema, error) {
|
|
overlay, err := defaultOverlay(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg := &load.Config{Overlay: overlay}
|
|
inst, err := rt.Build(load.Instances([]string{"/cue/data/gen.cue"}, cfg)[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
famval := inst.Value().LookupPath(cue.MakePath(cue.Str("Family")))
|
|
if !famval.Exists() {
|
|
return nil, errors.New("dashboard schema family did not exist at expected path in expected file")
|
|
}
|
|
|
|
return buildGenericScuemata(famval)
|
|
}
|
|
|
|
// DistDashboardFamily loads the family of schema representing the "Dist"
|
|
// variant of a Grafana dashboard: the "Base" variant (see
|
|
// BaseDashboardFamily()), but constrained such that all substructures (e.g.
|
|
// panels) must be valid with respect to the schemas provided by the core
|
|
// plugins that ship with Grafana.
|
|
//
|
|
// The returned VersionedCueSchema will always be the oldest schema in the
|
|
// family: the 0.0 schema. schema.Find() provides easy traversal to newer schema
|
|
// versions.
|
|
func DistDashboardFamily(p BaseLoadPaths) (schema.VersionedCueSchema, error) {
|
|
head, err := BaseDashboardFamily(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
scuemap, err := readPanelModels(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
dj, err := disjunctPanelScuemata(scuemap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Stick this into a dummy struct so that we can unify it into place, as
|
|
// Value.Fill() can't target definitions. Need new method based on cue.Path;
|
|
// a CL has been merged that creates FillPath and will be in the next
|
|
// release of CUE.
|
|
dummy, _ := rt.Compile("mergeStruct", `
|
|
obj: {}
|
|
dummy: {
|
|
#Panel: obj
|
|
}
|
|
`)
|
|
|
|
filled := dummy.Value().FillPath(cue.MakePath(cue.Str("obj")), dj)
|
|
ddj := filled.LookupPath(cue.MakePath(cue.Str("dummy")))
|
|
|
|
var first, prev *compositeDashboardSchema
|
|
for head != nil {
|
|
cds := &compositeDashboardSchema{
|
|
base: head,
|
|
actual: head.CUE().Unify(ddj),
|
|
panelFams: scuemap,
|
|
// TODO migrations
|
|
migration: terminalMigrationFunc,
|
|
}
|
|
|
|
if prev == nil {
|
|
first = cds
|
|
} else {
|
|
prev.next = cds
|
|
}
|
|
|
|
prev = cds
|
|
head = head.Successor()
|
|
}
|
|
|
|
return first, nil
|
|
}
|
|
|
|
type compositeDashboardSchema struct {
|
|
// The base/root dashboard schema
|
|
base schema.VersionedCueSchema
|
|
actual cue.Value
|
|
next *compositeDashboardSchema
|
|
migration migrationFunc
|
|
panelFams map[string]schema.VersionedCueSchema
|
|
}
|
|
|
|
// Validate checks that the resource is correct with respect to the schema.
|
|
func (cds *compositeDashboardSchema) Validate(r schema.Resource) error {
|
|
rv, err := rt.Compile("resource", r.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return cds.actual.Unify(rv.Value()).Validate(cue.Concrete(true))
|
|
}
|
|
|
|
// CUE returns the cue.Value representing the actual schema.
|
|
func (cds *compositeDashboardSchema) CUE() cue.Value {
|
|
return cds.actual
|
|
}
|
|
|
|
// Version reports the major and minor versions of the schema.
|
|
func (cds *compositeDashboardSchema) Version() (major int, minor int) {
|
|
return cds.base.Version()
|
|
}
|
|
|
|
// Returns the next VersionedCueSchema
|
|
func (cds *compositeDashboardSchema) Successor() schema.VersionedCueSchema {
|
|
if cds.next == nil {
|
|
// Untyped nil, allows `<sch> == nil` checks to work as people expect
|
|
return nil
|
|
}
|
|
return cds.next
|
|
}
|
|
|
|
func (cds *compositeDashboardSchema) Migrate(x schema.Resource) (schema.Resource, schema.VersionedCueSchema, error) { // TODO restrict input/return type to concrete
|
|
r, sch, err := cds.migration(x.Value)
|
|
if err != nil || sch == nil {
|
|
// TODO fix sloppy types
|
|
r = x.Value.(cue.Value)
|
|
}
|
|
|
|
return schema.Resource{Value: r}, sch, nil
|
|
}
|
|
|
|
func (cds *compositeDashboardSchema) LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error) {
|
|
// So much slop rn, but it's OK because i FINALLY know where this is going!
|
|
psch, has := cds.panelFams[id]
|
|
if !has {
|
|
// TODO typed errors
|
|
return nil, fmt.Errorf("unknown panel plugin type %q", id)
|
|
}
|
|
|
|
latest := schema.Find(psch, schema.Latest())
|
|
sch := &genericVersionedSchema{
|
|
actual: cds.base.CUE().LookupPath(panelSubpath).Unify(mapPanelModel(id, latest)),
|
|
}
|
|
sch.major, sch.minor = latest.Version()
|
|
|
|
return sch, nil
|
|
}
|
|
|
|
// One-off special interface for dashboard composite schema, until the composite
|
|
// dashboard schema pattern is fully generalized.
|
|
//
|
|
// NOTE: THIS IS A TEMPORARY TYPE. IT WILL BE REPLACED WITH A GENERIC INTERFACE
|
|
// TO REPRESENT COMPOSITIONAL SCHEMA FAMILY PRIOR TO GRAFANA 8. UPDATING WILL
|
|
// SHOULD BE TRIVIAL, BUT IT WILL CAUSE BREAKAGES.
|
|
type CompositeDashboardSchema interface {
|
|
schema.VersionedCueSchema
|
|
LatestPanelSchemaFor(id string) (schema.VersionedCueSchema, error)
|
|
}
|