The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/schema/load/dashboard.go

200 lines
6.0 KiB

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().Fill(dj, "obj")
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))
}
// ApplyDefaults returns a new, concrete copy of the Resource with all paths
// that are 1) missing in the Resource AND 2) specified by the schema,
// filled with default values specified by the schema.
func (cds *compositeDashboardSchema) ApplyDefaults(_ schema.Resource) (schema.Resource, error) {
panic("not implemented") // TODO: Implement
}
// TrimDefaults returns a new, concrete copy of the Resource where all paths
// in the where the values at those paths are the same as the default value
// given in the schema.
func (cds *compositeDashboardSchema) TrimDefaults(_ schema.Resource) (schema.Resource, error) {
panic("not implemented") // TODO: Implement
}
// 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)
}