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/kindsys/load.go

210 lines
6.8 KiB

package kindsys
import (
"fmt"
"io/fs"
"path/filepath"
"sync"
"cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
"github.com/grafana/grafana/pkg/cuectx"
"github.com/grafana/thema"
)
// CoreDeclParentPath is the path, relative to the repository root, where
// each child directory is expected to contain .cue files declaring one
// Core kind.
var CoreDeclParentPath = "kinds"
// GoCoreKindParentPath is the path, relative to the repository root, to the directory
// containing one directory per kind, full of generated Go kind output: types and bindings.
var GoCoreKindParentPath = filepath.Join("pkg", "kinds")
// TSCoreKindParentPath is the path, relative to the repository root, to the directory that
// contains one directory per kind, full of generated TS kind output: types and default consts.
var TSCoreKindParentPath = filepath.Join("packages", "grafana-schema", "src", "raw")
var defaultFramework cue.Value
var fwOnce sync.Once
func init() {
loadpFrameworkOnce()
}
func loadpFrameworkOnce() {
fwOnce.Do(func() {
var err error
defaultFramework, err = doLoadFrameworkCUE(cuectx.GrafanaCUEContext())
if err != nil {
panic(err)
}
})
}
func doLoadFrameworkCUE(ctx *cue.Context) (cue.Value, error) {
v, err := cuectx.BuildGrafanaInstance(ctx, filepath.Join("pkg", "kindsys"), "kindsys", nil)
if err != nil {
return v, err
}
if err = v.Validate(cue.Concrete(false), cue.All()); err != nil {
return cue.Value{}, fmt.Errorf("kindsys framework loaded cue.Value has err: %w", err)
}
return v, nil
}
// CUEFramework returns a cue.Value representing all the kind framework
// raw CUE files.
//
// For low-level use in constructing other types and APIs, while still letting
// us declare all the frameworky CUE bits in a single package. Other Go types
// make the constructs in the returned cue.Value easy to use.
//
// Calling this with a nil [cue.Context] (the singleton returned from
// [cuectx.GrafanaCUEContext] is used) will memoize certain CUE operations.
// Prefer passing nil unless a different cue.Context is specifically required.
func CUEFramework(ctx *cue.Context) cue.Value {
if ctx == nil || ctx == cuectx.GrafanaCUEContext() {
// Ensure framework is loaded, even if this func is called
// from an init() somewhere.
loadpFrameworkOnce()
return defaultFramework
}
// Error guaranteed to be nil here because erroring would have caused init() to panic
v, _ := doLoadFrameworkCUE(ctx) // nolint:errcheck
return v
}
// ToKindProps takes a cue.Value expected to represent a kind of the category
// specified by the type parameter and populates the Go type from the cue.Value.
func ToKindProps[T KindProperties](v cue.Value) (T, error) {
props := new(T)
if !v.Exists() {
return *props, ErrValueNotExist
}
fw := CUEFramework(v.Context())
var kdef cue.Value
anyprops := any(*props).(SomeKindProperties)
switch anyprops.(type) {
case CoreProperties:
kdef = fw.LookupPath(cue.MakePath(cue.Str("Core")))
case CustomProperties:
kdef = fw.LookupPath(cue.MakePath(cue.Str("Custom")))
case ComposableProperties:
kdef = fw.LookupPath(cue.MakePath(cue.Str("Composable")))
default:
// unreachable so long as all the possibilities in KindProperties have switch branches
panic("unreachable")
}
item := v.Unify(kdef)
if item.Err() != nil {
return *props, errors.Wrap(errors.Promote(ErrValueNotAKind, ""), item.Err())
}
if err := item.Decode(props); err != nil {
// Should only be reachable if CUE and Go framework types have diverged
panic(errors.Details(err, nil))
}
return *props, nil
}
// SomeDecl represents a single kind declaration, having been loaded and
// validated by a func such as [LoadCoreKind].
//
// The underlying type of the Properties field indicates the category of kind.
type SomeDecl struct {
// V is the cue.Value containing the entire Kind declaration.
V cue.Value
// Properties contains the kind's declared properties.
Properties SomeKindProperties
}
// BindKindLineage binds the lineage for the kind declaration.
//
// For kinds with a corresponding Go type, it is left to the caller to associate
// that Go type with the lineage returned from this function by a call to
// [thema.BindType].
func (decl SomeDecl) BindKindLineage(rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
if rt == nil {
rt = cuectx.GrafanaThemaRuntime()
}
return thema.BindLineage(decl.V.LookupPath(cue.MakePath(cue.Str("lineage"))), rt, opts...)
}
// IsCore indicates whether the represented kind is a core kind.
func (decl SomeDecl) IsCore() bool {
_, is := decl.Properties.(CoreProperties)
return is
}
// IsCustom indicates whether the represented kind is a custom kind.
func (decl SomeDecl) IsCustom() bool {
_, is := decl.Properties.(CustomProperties)
return is
}
// IsComposable indicates whether the represented kind is a composable kind.
func (decl SomeDecl) IsComposable() bool {
_, is := decl.Properties.(ComposableProperties)
return is
}
// Decl represents a single kind declaration, having been loaded
// and validated by a func such as [LoadCoreKind].
//
// Its type parameter indicates the category of kind.
type Decl[T KindProperties] struct {
// V is the cue.Value containing the entire Kind declaration.
V cue.Value
// Properties contains the kind's declared properties.
Properties T
}
// Some converts the typed Decl to the equivalent typeless SomeDecl.
func (decl Decl[T]) Some() SomeDecl {
return SomeDecl{
V: decl.V,
Properties: any(decl.Properties).(SomeKindProperties),
}
}
// LoadCoreKind loads and validates a core kind declaration of the kind category
// indicated by the type parameter. On success, it returns a [Decl] which
// contains the entire contents of the kind declaration.
//
// declpath is the path to the directory containing the core kind declaration,
// relative to the grafana/grafana root. For example, dashboards are in
// "kinds/dashboard".
//
// The .cue file bytes containing the core kind declaration will be retrieved
// from the central embedded FS, [grafana.CueSchemaFS]. If desired (e.g. for
// testing), an optional fs.FS may be provided via the overlay parameter, which
// will be merged over [grafana.CueSchemaFS]. But in typical circumstances,
// overlay can and should be nil.
//
// This is a low-level function, primarily intended for use in code generation.
// For representations of core kinds that are useful in Go programs at runtime,
// see ["github.com/grafana/grafana/pkg/registry/corekind"].
func LoadCoreKind(declpath string, ctx *cue.Context, overlay fs.FS) (Decl[CoreProperties], error) {
none := Decl[CoreProperties]{}
vk, err := cuectx.BuildGrafanaInstance(ctx, declpath, "kind", overlay)
if err != nil {
return none, err
}
props, err := ToKindProps[CoreProperties](vk)
if err != nil {
return none, err
}
return Decl[CoreProperties]{
V: vk,
Properties: props,
}, nil
}