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/panel.go

161 lines
3.9 KiB

package load
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"io/ioutil"
"path/filepath"
"cuelang.org/go/cue"
"cuelang.org/go/cue/load"
"github.com/grafana/grafana/pkg/schema"
)
// Returns a disjunction of structs representing each panel schema version
// (post-mapping from on-disk #PanelModel form) from each scuemata in the map.
func disjunctPanelScuemata(scuemap map[string]schema.VersionedCueSchema) (cue.Value, error) {
partsi, err := rt.Compile("panelDisjunction", `
allPanels: [Name=_]: {}
parts: or([for v in allPanels { v }])
`)
if err != nil {
return cue.Value{}, err
}
parts := partsi.Value()
for id, sch := range scuemap {
for sch != nil {
cv := mapPanelModel(id, sch)
mjv, miv := sch.Version()
parts = parts.Fill(cv, "allPanels", fmt.Sprintf("%s@%v.%v", id, mjv, miv))
sch = sch.Successor()
}
}
return parts.LookupPath(cue.MakePath(cue.Str("parts"))), nil
}
// mapPanelModel maps a schema from the #PanelModel form in which it's declared
// in a plugin's model.cue to the structure in which it actually appears in the
// dashboard schema.
func mapPanelModel(id string, vcs schema.VersionedCueSchema) cue.Value {
maj, min := vcs.Version()
// Ignore err return, this can't fail to compile
inter, _ := rt.Compile("typedPanel", fmt.Sprintf(`
in: {
type: %q
v: {
maj: %d
min: %d
}
model: {...}
}
result: {
type: in.type,
panelSchema: maj: in.v.maj
panelSchema: min: in.v.min
options: in.model.PanelOptions
fieldConfig: defaults: custom: in.model.PanelFieldConfig
}
`, id, maj, min))
// TODO validate, especially with #PanelModel
return inter.Value().Fill(vcs.CUE(), "in", "model").LookupPath(cue.MakePath(cue.Str(("result"))))
}
func readPanelModels(p BaseLoadPaths) (map[string]schema.VersionedCueSchema, 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
}
base, err := getBaseScuemata(p)
if err != nil {
return nil, err
}
pmf := base.Value().LookupPath(cue.MakePath(cue.Def("#PanelFamily")))
if !pmf.Exists() {
return nil, errors.New("could not locate #PanelFamily definition")
}
all := make(map[string]schema.VersionedCueSchema)
err = fs.WalkDir(p.DistPluginCueFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || d.Name() != "plugin.json" {
return nil
}
dpath := filepath.Dir(path)
// For now, skip plugins without a models.cue
_, err = p.DistPluginCueFS.Open(filepath.Join(dpath, "models.cue"))
if err != nil {
return nil
}
fi, err := p.DistPluginCueFS.Open(path)
if err != nil {
return err
}
b, err := ioutil.ReadAll(fi)
if err != nil {
return err
}
jmap := make(map[string]interface{})
err = json.Unmarshal(b, &jmap)
if err != nil {
return err
}
iid, has := jmap["id"]
if !has || jmap["type"] != "panel" {
return errors.New("no type field in plugin.json or not a panel type plugin")
}
id := iid.(string)
cfg := &load.Config{
Package: "grafanaschema",
Overlay: overlay,
}
li := load.Instances([]string{filepath.Join("/", dpath, "models.cue")}, cfg)
imod, err := rt.Build(li[0])
if err != nil {
return err
}
// Get the Family declaration in the models.cue file...
pmod := imod.Value().LookupPath(cue.MakePath(cue.Str("Family")))
if !pmod.Exists() {
return fmt.Errorf("%s does not contain a declaration of its models at path 'Family'", path)
}
// Ensure the declared value is subsumed by/correct wrt #PanelFamily
if err := pmf.Subsume(pmod); err != nil {
return err
}
// Create a generic schema family to represent the whole of the
fam, err := buildGenericScuemata(pmod)
if err != nil {
return err
}
all[id] = fam
return nil
})
if err != nil {
return nil, err
}
return all, nil
}