mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Kindsys: Remove defs, Slot->SchemaInterface (#61069)
* kindsys: Remove defs, Slot->SchemaInterface * Remove excess file * Fix up tests * Regenerate kinds report * Final bits of cleanup * Stop complaining, linter * Update pkg/kindsys/kindcat_composable.cue Co-authored-by: Tania <yalyna.ts@gmail.com> Co-authored-by: Tania <yalyna.ts@gmail.com>
This commit is contained in:
@@ -3,5 +3,5 @@ package kind
|
|||||||
import "github.com/grafana/grafana/pkg/kindsys"
|
import "github.com/grafana/grafana/pkg/kindsys"
|
||||||
|
|
||||||
// In each child directory, the set of .cue files with 'package kind'
|
// In each child directory, the set of .cue files with 'package kind'
|
||||||
// must be an instance of kindsys.#Core - a declaration of a core kind.
|
// must be an instance of kindsys.Core - a declaration of a core kind.
|
||||||
kindsys.#Core
|
kindsys.Core
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type ManyToMany codejen.ManyToMany[*DeclForGen]
|
|||||||
|
|
||||||
// ForGen is a codejen input transformer that converts a pure kindsys.SomeDecl into
|
// ForGen is a codejen input transformer that converts a pure kindsys.SomeDecl into
|
||||||
// a DeclForGen by binding its contained lineage.
|
// a DeclForGen by binding its contained lineage.
|
||||||
func ForGen(rt *thema.Runtime, decl *kindsys.SomeDecl) (*DeclForGen, error) {
|
func ForGen(rt *thema.Runtime, decl kindsys.SomeDecl) (*DeclForGen, error) {
|
||||||
lin, err := decl.BindKindLineage(rt)
|
lin, err := decl.BindKindLineage(rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -32,7 +32,7 @@ func ForGen(rt *thema.Runtime, decl *kindsys.SomeDecl) (*DeclForGen, error) {
|
|||||||
// DeclForGen wraps [kindsys.SomeDecl] to provide trivial caching of
|
// DeclForGen wraps [kindsys.SomeDecl] to provide trivial caching of
|
||||||
// the lineage declared by the kind (nil for raw kinds).
|
// the lineage declared by the kind (nil for raw kinds).
|
||||||
type DeclForGen struct {
|
type DeclForGen struct {
|
||||||
*kindsys.SomeDecl
|
kindsys.SomeDecl
|
||||||
lin thema.Lineage
|
lin thema.Lineage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ func (gen *genBaseRegistry) JennyName() string {
|
|||||||
func (gen *genBaseRegistry) Generate(decls ...*DeclForGen) (*codejen.File, error) {
|
func (gen *genBaseRegistry) Generate(decls ...*DeclForGen) (*codejen.File, error) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
if err := tmpls.Lookup("kind_registry.tmpl").Execute(buf, tvars_kind_registry{
|
if err := tmpls.Lookup("kind_registry.tmpl").Execute(buf, tvars_kind_registry{
|
||||||
NumStructured: len(decls),
|
|
||||||
PackageName: filepath.Base(gen.path),
|
PackageName: filepath.Base(gen.path),
|
||||||
KindPackagePrefix: filepath.ToSlash(filepath.Join("github.com/grafana/grafana", gen.kindrelroot)),
|
KindPackagePrefix: filepath.ToSlash(filepath.Join("github.com/grafana/grafana", gen.kindrelroot)),
|
||||||
Kinds: decls,
|
Kinds: decls,
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
// all generated kinds.
|
// all generated kinds.
|
||||||
//
|
//
|
||||||
// This generator only has output for core structured kinds.
|
// This generator only has output for core structured kinds.
|
||||||
func CoreKindJenny(gokindsdir string, cfg *CoreStructuredKindGeneratorConfig) OneToOne {
|
func CoreKindJenny(gokindsdir string, cfg *CoreKindJennyConfig) OneToOne {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
cfg = new(CoreStructuredKindGeneratorConfig)
|
cfg = new(CoreKindJennyConfig)
|
||||||
}
|
}
|
||||||
if cfg.GenDirName == nil {
|
if cfg.GenDirName == nil {
|
||||||
cfg.GenDirName = func(decl *DeclForGen) string {
|
cfg.GenDirName = func(decl *DeclForGen) string {
|
||||||
@@ -31,8 +31,8 @@ func CoreKindJenny(gokindsdir string, cfg *CoreStructuredKindGeneratorConfig) On
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoreStructuredKindGeneratorConfig holds configuration options for [CoreKindJenny].
|
// CoreKindJennyConfig holds configuration options for [CoreKindJenny].
|
||||||
type CoreStructuredKindGeneratorConfig struct {
|
type CoreKindJennyConfig struct {
|
||||||
// GenDirName returns the name of the directory in which the file should be
|
// GenDirName returns the name of the directory in which the file should be
|
||||||
// generated. Defaults to DeclForGen.Lineage().Name() if nil.
|
// generated. Defaults to DeclForGen.Lineage().Name() if nil.
|
||||||
GenDirName func(*DeclForGen) string
|
GenDirName func(*DeclForGen) string
|
||||||
@@ -40,7 +40,7 @@ type CoreStructuredKindGeneratorConfig struct {
|
|||||||
|
|
||||||
type coreKindJenny struct {
|
type coreKindJenny struct {
|
||||||
gokindsdir string
|
gokindsdir string
|
||||||
cfg *CoreStructuredKindGeneratorConfig
|
cfg *CoreKindJennyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ OneToOne = &coreKindJenny{}
|
var _ OneToOne = &coreKindJenny{}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ type (
|
|||||||
}
|
}
|
||||||
tvars_kind_registry struct {
|
tvars_kind_registry struct {
|
||||||
// Header tvars_autogen_header
|
// Header tvars_autogen_header
|
||||||
NumStructured int
|
|
||||||
PackageName string
|
PackageName string
|
||||||
KindPackagePrefix string
|
KindPackagePrefix string
|
||||||
Kinds []*DeclForGen
|
Kinds []*DeclForGen
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k := &Kind{
|
k := &Kind{
|
||||||
decl: *decl,
|
decl: decl,
|
||||||
}
|
}
|
||||||
|
|
||||||
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
||||||
@@ -91,9 +91,8 @@ func (k *Kind) Maturity() kindsys.Maturity {
|
|||||||
|
|
||||||
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
||||||
// {{ .Properties.MachineName }} declaration in .cue files.
|
// {{ .Properties.MachineName }} declaration in .cue files.
|
||||||
func (k *Kind) Decl() *kindsys.Decl[kindsys.CoreProperties] {
|
func (k *Kind) Decl() kindsys.Decl[kindsys.CoreProperties] {
|
||||||
d := k.decl
|
return k.decl
|
||||||
return &d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k := &Kind{
|
k := &Kind{
|
||||||
decl: *decl,
|
decl: decl,
|
||||||
}
|
}
|
||||||
|
|
||||||
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
||||||
@@ -100,9 +100,8 @@ func (k *Kind) Maturity() kindsys.Maturity {
|
|||||||
|
|
||||||
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
||||||
// dashboard declaration in .cue files.
|
// dashboard declaration in .cue files.
|
||||||
func (k *Kind) Decl() *kindsys.Decl[kindsys.CoreProperties] {
|
func (k *Kind) Decl() kindsys.Decl[kindsys.CoreProperties] {
|
||||||
d := k.decl
|
return k.decl
|
||||||
return &d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k := &Kind{
|
k := &Kind{
|
||||||
decl: *decl,
|
decl: decl,
|
||||||
}
|
}
|
||||||
|
|
||||||
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
||||||
@@ -100,9 +100,8 @@ func (k *Kind) Maturity() kindsys.Maturity {
|
|||||||
|
|
||||||
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
||||||
// playlist declaration in .cue files.
|
// playlist declaration in .cue files.
|
||||||
func (k *Kind) Decl() *kindsys.Decl[kindsys.CoreProperties] {
|
func (k *Kind) Decl() kindsys.Decl[kindsys.CoreProperties] {
|
||||||
d := k.decl
|
return k.decl
|
||||||
return &d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k := &Kind{
|
k := &Kind{
|
||||||
decl: *decl,
|
decl: decl,
|
||||||
}
|
}
|
||||||
|
|
||||||
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
lin, err := decl.Some().BindKindLineage(rt, opts...)
|
||||||
@@ -100,9 +100,8 @@ func (k *Kind) Maturity() kindsys.Maturity {
|
|||||||
|
|
||||||
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
// Decl returns the [kindsys.Decl] containing both CUE and Go representations of the
|
||||||
// team declaration in .cue files.
|
// team declaration in .cue files.
|
||||||
func (k *Kind) Decl() *kindsys.Decl[kindsys.CoreProperties] {
|
func (k *Kind) Decl() kindsys.Decl[kindsys.CoreProperties] {
|
||||||
d := k.decl
|
return k.decl
|
||||||
return &d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
// Props returns a [kindsys.SomeKindProps], with underlying type [kindsys.CoreProperties],
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ type Interface interface {
|
|||||||
// a Decl() method through which these same properties are accessible.
|
// a Decl() method through which these same properties are accessible.
|
||||||
Props() SomeKindProperties
|
Props() SomeKindProperties
|
||||||
|
|
||||||
|
// TODO docs
|
||||||
|
Lineage() thema.Lineage
|
||||||
|
|
||||||
// TODO remove, unnecessary with Props()
|
// TODO remove, unnecessary with Props()
|
||||||
Name() string
|
Name() string
|
||||||
|
|
||||||
@@ -71,18 +74,19 @@ type Core interface {
|
|||||||
Interface
|
Interface
|
||||||
|
|
||||||
// TODO docs
|
// TODO docs
|
||||||
Lineage() thema.Lineage
|
Decl() Decl[CoreProperties]
|
||||||
|
|
||||||
// TODO docs
|
|
||||||
Decl() *Decl[CoreProperties] // TODO figure out how to reconcile this interface with CustomProperties
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// type Composable interface {
|
type Custom interface {
|
||||||
// Interface
|
Interface
|
||||||
//
|
|
||||||
// // TODO docs
|
// TODO docs
|
||||||
// Lineage() thema.Lineage
|
Decl() Decl[CustomProperties]
|
||||||
//
|
}
|
||||||
// // TODO docs
|
|
||||||
// Properties() CoreProperties // TODO figure out how to reconcile this interface with CustomProperties
|
type Composable interface {
|
||||||
// }
|
Interface
|
||||||
|
|
||||||
|
// TODO docs
|
||||||
|
Decl() Decl[ComposableProperties]
|
||||||
|
}
|
||||||
|
|||||||
29
pkg/kindsys/kindcat_composable.cue
Normal file
29
pkg/kindsys/kindcat_composable.cue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package kindsys
|
||||||
|
|
||||||
|
// Composable is a category of kind that provides schema elements for
|
||||||
|
// composition into Core and Custom kinds. Grafana plugins
|
||||||
|
// provide composable kinds; for example, a datasource plugin provides one to
|
||||||
|
// describe the structure of its queries, which is then composed into dashboards
|
||||||
|
// and alerting rules.
|
||||||
|
//
|
||||||
|
// Each Composable is an implementation of exactly one Slot, a shared meta-schema
|
||||||
|
// defined by Grafana itself that constrains the shape of schemas declared in
|
||||||
|
// that ComposableKind.
|
||||||
|
Composable: S={
|
||||||
|
_sharedKind
|
||||||
|
|
||||||
|
// schemaInterface is the name of the Grafana schema interface implemented by
|
||||||
|
// this Composable kind. The set is open to ensure forward compatibility of
|
||||||
|
// Grafana and tooling with any additional schema interfaces that may be added.
|
||||||
|
// schemaInterface: or([ for k, _ in schemaInterfaces {k}, string])
|
||||||
|
schemaInterface: string
|
||||||
|
|
||||||
|
let schif = schemaInterfaces[S.schemaInterface]
|
||||||
|
|
||||||
|
// lineage is the Thema lineage containing all the schemas that have existed for this kind.
|
||||||
|
// The name of the lineage is constrained to the name of the schema interface being implemented.
|
||||||
|
// lineage: thema.#Lineage & {name: S.schemaInterface, joinSchema: schif.interface}
|
||||||
|
lineage: { joinSchema: (schif.interface | *{}) }
|
||||||
|
|
||||||
|
lineageIsGroup: schif.group | *false
|
||||||
|
}
|
||||||
@@ -7,10 +7,9 @@ package kindsys
|
|||||||
//
|
//
|
||||||
// Grafana provides Kubernetes apiserver-shaped HTTP APIs for interacting with custom
|
// Grafana provides Kubernetes apiserver-shaped HTTP APIs for interacting with custom
|
||||||
// kinds - the same API patterns (and clients) used to interact with k8s CustomResources.
|
// kinds - the same API patterns (and clients) used to interact with k8s CustomResources.
|
||||||
#Custom: S={
|
Custom: S={
|
||||||
_sharedKind
|
_sharedKind
|
||||||
|
|
||||||
lineage: { name: S.machineName }
|
lineage: { name: S.machineName }
|
||||||
lineageIsGroup: false
|
lineageIsGroup: false
|
||||||
...
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
// Grafana provides a standard mechanism for representing its kinds as CRDs.
|
// Grafana provides a standard mechanism for representing its kinds as CRDs.
|
||||||
//
|
//
|
||||||
// There are three categories of kinds: Core, Custom, and Composable.
|
// There are three categories of kinds: Core, Custom, and Composable.
|
||||||
#Kind: #Composable | #Core | #Custom
|
Kind: Composable | Core | Custom
|
||||||
|
|
||||||
// properties shared between all kind categories.
|
// properties shared between all kind categories.
|
||||||
_sharedKind: {
|
_sharedKind: {
|
||||||
@@ -86,37 +86,9 @@ _sharedKind: {
|
|||||||
// Core specifies the kind category for core-defined arbitrary types.
|
// Core specifies the kind category for core-defined arbitrary types.
|
||||||
// Familiar types and functional resources in Grafana, such as dashboards and
|
// Familiar types and functional resources in Grafana, such as dashboards and
|
||||||
// and datasources, are represented as core kinds.
|
// and datasources, are represented as core kinds.
|
||||||
#Core: S=close({
|
Core: S=close({
|
||||||
_sharedKind
|
_sharedKind
|
||||||
|
|
||||||
lineage: { name: S.machineName }
|
lineage: { name: S.machineName }
|
||||||
lineageIsGroup: false
|
lineageIsGroup: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// Composable is a category of kind that provides schema elements for
|
|
||||||
// composition into Core and Custom kinds. Grafana plugins
|
|
||||||
// provide composable kinds; for example, a datasource plugin provides one to
|
|
||||||
// describe the structure of its queries, which is then composed into dashboards
|
|
||||||
// and alerting rules.
|
|
||||||
//
|
|
||||||
// Each Composable is an implementation of exactly one Slot, a shared meta-schema
|
|
||||||
// defined by Grafana itself that constrains the shape of schemas declared in
|
|
||||||
// that ComposableKind.
|
|
||||||
#Composable: S={
|
|
||||||
_sharedKind
|
|
||||||
|
|
||||||
// TODO docs
|
|
||||||
// TODO unify this with the existing slots decls in pkg/framework/coremodel
|
|
||||||
slot: "Panel" | "Query" | "DSConfig"
|
|
||||||
|
|
||||||
// TODO unify this with the existing slots decls in pkg/framework/coremodel
|
|
||||||
lineageIsGroup: bool & [
|
|
||||||
if slot == "Panel" { true },
|
|
||||||
if slot == "DSConfig" { true },
|
|
||||||
if slot == "Query" { false },
|
|
||||||
][0]
|
|
||||||
|
|
||||||
// lineage is the Thema lineage containing all the schemas that have existed for this kind.
|
|
||||||
// It is required that lineage.name is the same as the [machineName].
|
|
||||||
lineage: thema.#Lineage & { name: S.machineName }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ type CommonProperties struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CoreProperties represents the static properties in the declaration of a
|
// CoreProperties represents the static properties in the declaration of a
|
||||||
// #Core kind that are representable with basic Go types. This
|
// Core kind that are representable with basic Go types. This
|
||||||
// excludes Thema schemas.
|
// excludes Thema schemas.
|
||||||
//
|
//
|
||||||
// When a .cue #Core declaration is loaded through the standard [LoadCoreKind],
|
// When a .cue Core declaration is loaded through the standard [LoadCoreKind],
|
||||||
// func, it is fully validated and populated according to all rules specified
|
// func, it is fully validated and populated according to all rules specified
|
||||||
// in CUE for #Core kinds.
|
// in CUE for Core kinds.
|
||||||
type CoreProperties struct {
|
type CoreProperties struct {
|
||||||
CommonProperties
|
CommonProperties
|
||||||
CurrentVersion thema.SyntacticVersion `json:"currentVersion"`
|
CurrentVersion thema.SyntacticVersion `json:"currentVersion"`
|
||||||
@@ -30,7 +30,7 @@ func (m CoreProperties) Common() CommonProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CustomProperties represents the static properties in the declaration of a
|
// CustomProperties represents the static properties in the declaration of a
|
||||||
// #Custom kind that are representable with basic Go types. This
|
// Custom kind that are representable with basic Go types. This
|
||||||
// excludes Thema schemas.
|
// excludes Thema schemas.
|
||||||
type CustomProperties struct {
|
type CustomProperties struct {
|
||||||
CommonProperties
|
CommonProperties
|
||||||
@@ -43,7 +43,7 @@ func (m CustomProperties) Common() CommonProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ComposableProperties represents the static properties in the declaration of a
|
// ComposableProperties represents the static properties in the declaration of a
|
||||||
// #Composable kind that are representable with basic Go types. This
|
// Composable kind that are representable with basic Go types. This
|
||||||
// excludes Thema schemas.
|
// excludes Thema schemas.
|
||||||
type ComposableProperties struct {
|
type ComposableProperties struct {
|
||||||
CommonProperties
|
CommonProperties
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ func doLoadFrameworkCUE(ctx *cue.Context) (cue.Value, error) {
|
|||||||
//
|
//
|
||||||
// For low-level use in constructing other types and APIs, while still letting
|
// 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
|
// us declare all the frameworky CUE bits in a single package. Other Go types
|
||||||
// make the constructs in this value easy to use.
|
// make the constructs in the returned cue.Value easy to use.
|
||||||
//
|
//
|
||||||
// All calling code within grafana/grafana is expected to use Grafana's
|
// Calling this with a nil [cue.Context] (the singleton returned from
|
||||||
// singleton [cue.Context], returned from [cuectx.GrafanaCUEContext]. If nil
|
// [cuectx.GrafanaCUEContext] is used) will memoize certain CUE operations.
|
||||||
// is passed, the singleton will be used.
|
// Prefer passing nil unless a different cue.Context is specifically required.
|
||||||
func CUEFramework(ctx *cue.Context) cue.Value {
|
func CUEFramework(ctx *cue.Context) cue.Value {
|
||||||
if ctx == nil || ctx == cuectx.GrafanaCUEContext() {
|
if ctx == nil || ctx == cuectx.GrafanaCUEContext() {
|
||||||
// Ensure framework is loaded, even if this func is called
|
// Ensure framework is loaded, even if this func is called
|
||||||
@@ -77,9 +77,9 @@ func CUEFramework(ctx *cue.Context) cue.Value {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToKindMeta takes a cue.Value expected to represent a kind of the category
|
// 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.
|
// specified by the type parameter and populates the Go type from the cue.Value.
|
||||||
func ToKindMeta[T KindProperties](v cue.Value) (T, error) {
|
func ToKindProps[T KindProperties](v cue.Value) (T, error) {
|
||||||
props := new(T)
|
props := new(T)
|
||||||
if !v.Exists() {
|
if !v.Exists() {
|
||||||
return *props, ErrValueNotExist
|
return *props, ErrValueNotExist
|
||||||
@@ -91,11 +91,11 @@ func ToKindMeta[T KindProperties](v cue.Value) (T, error) {
|
|||||||
anyprops := any(*props).(SomeKindProperties)
|
anyprops := any(*props).(SomeKindProperties)
|
||||||
switch anyprops.(type) {
|
switch anyprops.(type) {
|
||||||
case CoreProperties:
|
case CoreProperties:
|
||||||
kdef = fw.LookupPath(cue.MakePath(cue.Def("Core")))
|
kdef = fw.LookupPath(cue.MakePath(cue.Str("Core")))
|
||||||
case CustomProperties:
|
case CustomProperties:
|
||||||
kdef = fw.LookupPath(cue.MakePath(cue.Def("Custom")))
|
kdef = fw.LookupPath(cue.MakePath(cue.Str("Custom")))
|
||||||
case ComposableProperties:
|
case ComposableProperties:
|
||||||
kdef = fw.LookupPath(cue.MakePath(cue.Def("Composable")))
|
kdef = fw.LookupPath(cue.MakePath(cue.Str("Composable")))
|
||||||
default:
|
default:
|
||||||
// unreachable so long as all the possibilities in KindProperties have switch branches
|
// unreachable so long as all the possibilities in KindProperties have switch branches
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@@ -128,8 +128,9 @@ type SomeDecl struct {
|
|||||||
// BindKindLineage binds the lineage for the kind declaration.
|
// BindKindLineage binds the lineage for the kind declaration.
|
||||||
//
|
//
|
||||||
// For kinds with a corresponding Go type, it is left to the caller to associate
|
// 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].
|
// that Go type with the lineage returned from this function by a call to
|
||||||
func (decl *SomeDecl) BindKindLineage(rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
// [thema.BindType].
|
||||||
|
func (decl SomeDecl) BindKindLineage(rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
||||||
if rt == nil {
|
if rt == nil {
|
||||||
rt = cuectx.GrafanaThemaRuntime()
|
rt = cuectx.GrafanaThemaRuntime()
|
||||||
}
|
}
|
||||||
@@ -142,19 +143,19 @@ func (decl *SomeDecl) BindKindLineage(rt *thema.Runtime, opts ...thema.BindOptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsCore indicates whether the represented kind is a core kind.
|
// IsCore indicates whether the represented kind is a core kind.
|
||||||
func (decl *SomeDecl) IsCore() bool {
|
func (decl SomeDecl) IsCore() bool {
|
||||||
_, is := decl.Properties.(CoreProperties)
|
_, is := decl.Properties.(CoreProperties)
|
||||||
return is
|
return is
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCustom indicates whether the represented kind is a custom kind.
|
// IsCustom indicates whether the represented kind is a custom kind.
|
||||||
func (decl *SomeDecl) IsCustom() bool {
|
func (decl SomeDecl) IsCustom() bool {
|
||||||
_, is := decl.Properties.(CustomProperties)
|
_, is := decl.Properties.(CustomProperties)
|
||||||
return is
|
return is
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsComposable indicates whether the represented kind is a composable kind.
|
// IsComposable indicates whether the represented kind is a composable kind.
|
||||||
func (decl *SomeDecl) IsComposable() bool {
|
func (decl SomeDecl) IsComposable() bool {
|
||||||
_, is := decl.Properties.(ComposableProperties)
|
_, is := decl.Properties.(ComposableProperties)
|
||||||
return is
|
return is
|
||||||
}
|
}
|
||||||
@@ -171,8 +172,8 @@ type Decl[T KindProperties] struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Some converts the typed Decl to the equivalent typeless SomeDecl.
|
// Some converts the typed Decl to the equivalent typeless SomeDecl.
|
||||||
func (decl *Decl[T]) Some() *SomeDecl {
|
func (decl Decl[T]) Some() SomeDecl {
|
||||||
return &SomeDecl{
|
return SomeDecl{
|
||||||
V: decl.V,
|
V: decl.V,
|
||||||
Properties: any(decl.Properties).(SomeKindProperties),
|
Properties: any(decl.Properties).(SomeKindProperties),
|
||||||
}
|
}
|
||||||
@@ -195,17 +196,20 @@ func (decl *Decl[T]) Some() *SomeDecl {
|
|||||||
// This is a low-level function, primarily intended for use in code generation.
|
// 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,
|
// For representations of core kinds that are useful in Go programs at runtime,
|
||||||
// see ["github.com/grafana/grafana/pkg/registry/corekind"].
|
// see ["github.com/grafana/grafana/pkg/registry/corekind"].
|
||||||
func LoadCoreKind(declpath string, ctx *cue.Context, overlay fs.FS) (*Decl[CoreProperties], error) {
|
func LoadCoreKind(declpath string, ctx *cue.Context, overlay fs.FS) (Decl[CoreProperties], error) {
|
||||||
|
none := Decl[CoreProperties]{}
|
||||||
vk, err := cuectx.BuildGrafanaInstance(ctx, declpath, "kind", overlay)
|
vk, err := cuectx.BuildGrafanaInstance(ctx, declpath, "kind", overlay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return none, err
|
||||||
}
|
}
|
||||||
decl := &Decl[CoreProperties]{
|
|
||||||
V: vk,
|
props, err := ToKindProps[CoreProperties](vk)
|
||||||
}
|
|
||||||
decl.Properties, err = ToKindMeta[CoreProperties](vk)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return none, err
|
||||||
}
|
}
|
||||||
return decl, nil
|
|
||||||
|
return Decl[CoreProperties]{
|
||||||
|
V: vk,
|
||||||
|
Properties: props,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,24 +139,24 @@ func buildKindStateReport() *KindStateReport {
|
|||||||
}, "core")
|
}, "core")
|
||||||
}
|
}
|
||||||
|
|
||||||
all := kindsys.AllSlots(nil)
|
all := kindsys.SchemaInterfaces(nil)
|
||||||
// TODO this is all hacks until #59001, which will unite plugins with kindsys
|
// TODO this is all hacks until #59001, which will unite plugins with kindsys
|
||||||
for _, tree := range corelist.New(nil) {
|
for _, tree := range corelist.New(nil) {
|
||||||
rp := tree.RootPlugin()
|
rp := tree.RootPlugin()
|
||||||
for _, slot := range all {
|
for _, si := range all {
|
||||||
if may, _ := slot.ForPluginType(string(rp.Meta().Type)); may {
|
if si.Should(string(rp.Meta().Type)) {
|
||||||
n := fmt.Sprintf("%s-%s", strings.Title(rp.Meta().Id), slot.Name())
|
n := fmt.Sprintf("%s-%s", strings.Title(rp.Meta().Id), si.Name())
|
||||||
props := kindsys.ComposableProperties{
|
props := kindsys.ComposableProperties{
|
||||||
CommonProperties: kindsys.CommonProperties{
|
CommonProperties: kindsys.CommonProperties{
|
||||||
Name: n,
|
Name: n,
|
||||||
PluralName: n + "s",
|
PluralName: n + "s",
|
||||||
MachineName: machinize(n),
|
MachineName: machinize(n),
|
||||||
PluralMachineName: machinize(n) + "s",
|
PluralMachineName: machinize(n) + "s",
|
||||||
LineageIsGroup: slot.IsGroup(),
|
LineageIsGroup: si.IsGroup(),
|
||||||
Maturity: "planned",
|
Maturity: "planned",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if ck, has := rp.SlotImplementations()[slot.Name()]; has {
|
if ck, has := rp.SlotImplementations()[si.Name()]; has {
|
||||||
props.CommonProperties.Maturity = "merged"
|
props.CommonProperties.Maturity = "merged"
|
||||||
props.CurrentVersion = ck.Latest().Version()
|
props.CurrentVersion = ck.Latest().Version()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"pluralName": "Alertmanager-Querys",
|
"pluralName": "Alertmanager-Querys",
|
||||||
"machineName": "alertmanager_query",
|
"machineName": "alertmanager_query",
|
||||||
"pluralMachineName": "alertmanager_querys",
|
"pluralMachineName": "alertmanager_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"pluralName": "Cloudwatch-Querys",
|
"pluralName": "Cloudwatch-Querys",
|
||||||
"machineName": "cloudwatch_query",
|
"machineName": "cloudwatch_query",
|
||||||
"pluralMachineName": "cloudwatch_querys",
|
"pluralMachineName": "cloudwatch_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
"pluralName": "Dashboard-Querys",
|
"pluralName": "Dashboard-Querys",
|
||||||
"machineName": "dashboard_query",
|
"machineName": "dashboard_query",
|
||||||
"pluralMachineName": "dashboard_querys",
|
"pluralMachineName": "dashboard_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
"pluralName": "Elasticsearch-Querys",
|
"pluralName": "Elasticsearch-Querys",
|
||||||
"machineName": "elasticsearch_query",
|
"machineName": "elasticsearch_query",
|
||||||
"pluralMachineName": "elasticsearch_querys",
|
"pluralMachineName": "elasticsearch_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
"pluralName": "Grafana-Azure-Monitor-Datasource-Querys",
|
"pluralName": "Grafana-Azure-Monitor-Datasource-Querys",
|
||||||
"machineName": "grafana_azure_monitor_datasource_query",
|
"machineName": "grafana_azure_monitor_datasource_query",
|
||||||
"pluralMachineName": "grafana_azure_monitor_datasource_querys",
|
"pluralMachineName": "grafana_azure_monitor_datasource_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -317,7 +317,7 @@
|
|||||||
"pluralName": "Grafana-Querys",
|
"pluralName": "Grafana-Querys",
|
||||||
"machineName": "grafana_query",
|
"machineName": "grafana_query",
|
||||||
"pluralMachineName": "grafana_querys",
|
"pluralMachineName": "grafana_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
"pluralName": "Graphite-Querys",
|
"pluralName": "Graphite-Querys",
|
||||||
"machineName": "graphite_query",
|
"machineName": "graphite_query",
|
||||||
"pluralMachineName": "graphite_querys",
|
"pluralMachineName": "graphite_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -401,7 +401,7 @@
|
|||||||
"pluralName": "Jaeger-Querys",
|
"pluralName": "Jaeger-Querys",
|
||||||
"machineName": "jaeger_query",
|
"machineName": "jaeger_query",
|
||||||
"pluralMachineName": "jaeger_querys",
|
"pluralMachineName": "jaeger_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -449,7 +449,7 @@
|
|||||||
"pluralName": "Loki-Querys",
|
"pluralName": "Loki-Querys",
|
||||||
"machineName": "loki_query",
|
"machineName": "loki_query",
|
||||||
"pluralMachineName": "loki_querys",
|
"pluralMachineName": "loki_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -473,7 +473,7 @@
|
|||||||
"pluralName": "Mssql-Querys",
|
"pluralName": "Mssql-Querys",
|
||||||
"machineName": "mssql_query",
|
"machineName": "mssql_query",
|
||||||
"pluralMachineName": "mssql_querys",
|
"pluralMachineName": "mssql_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -497,7 +497,7 @@
|
|||||||
"pluralName": "Mysql-Querys",
|
"pluralName": "Mysql-Querys",
|
||||||
"machineName": "mysql_query",
|
"machineName": "mysql_query",
|
||||||
"pluralMachineName": "mysql_querys",
|
"pluralMachineName": "mysql_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -545,7 +545,7 @@
|
|||||||
"pluralName": "Parca-Querys",
|
"pluralName": "Parca-Querys",
|
||||||
"machineName": "parca_query",
|
"machineName": "parca_query",
|
||||||
"pluralMachineName": "parca_querys",
|
"pluralMachineName": "parca_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"pluralName": "Phlare-Querys",
|
"pluralName": "Phlare-Querys",
|
||||||
"machineName": "phlare_query",
|
"machineName": "phlare_query",
|
||||||
"pluralMachineName": "phlare_querys",
|
"pluralMachineName": "phlare_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -617,7 +617,7 @@
|
|||||||
"pluralName": "Postgres-Querys",
|
"pluralName": "Postgres-Querys",
|
||||||
"machineName": "postgres_query",
|
"machineName": "postgres_query",
|
||||||
"pluralMachineName": "postgres_querys",
|
"pluralMachineName": "postgres_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -641,7 +641,7 @@
|
|||||||
"pluralName": "Prometheus-Querys",
|
"pluralName": "Prometheus-Querys",
|
||||||
"machineName": "prometheus_query",
|
"machineName": "prometheus_query",
|
||||||
"pluralMachineName": "prometheus_querys",
|
"pluralMachineName": "prometheus_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -701,7 +701,7 @@
|
|||||||
"pluralName": "Stackdriver-Querys",
|
"pluralName": "Stackdriver-Querys",
|
||||||
"machineName": "stackdriver_query",
|
"machineName": "stackdriver_query",
|
||||||
"pluralMachineName": "stackdriver_querys",
|
"pluralMachineName": "stackdriver_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -761,7 +761,7 @@
|
|||||||
"pluralName": "Tempo-Querys",
|
"pluralName": "Tempo-Querys",
|
||||||
"machineName": "tempo_query",
|
"machineName": "tempo_query",
|
||||||
"pluralMachineName": "tempo_querys",
|
"pluralMachineName": "tempo_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -785,7 +785,7 @@
|
|||||||
"pluralName": "Testdata-Querys",
|
"pluralName": "Testdata-Querys",
|
||||||
"machineName": "testdata_query",
|
"machineName": "testdata_query",
|
||||||
"pluralMachineName": "testdata_querys",
|
"pluralMachineName": "testdata_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
@@ -881,7 +881,7 @@
|
|||||||
"pluralName": "Zipkin-Querys",
|
"pluralName": "Zipkin-Querys",
|
||||||
"machineName": "zipkin_query",
|
"machineName": "zipkin_query",
|
||||||
"pluralMachineName": "zipkin_querys",
|
"pluralMachineName": "zipkin_querys",
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": true,
|
||||||
"maturity": "planned",
|
"maturity": "planned",
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
|
|||||||
140
pkg/kindsys/schema_interface.cue
Normal file
140
pkg/kindsys/schema_interface.cue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package kindsys
|
||||||
|
|
||||||
|
// The schema interfaces defined in this file are meta-schemas. They are shared
|
||||||
|
// contracts between the producers (composable kinds, defined in Grafana
|
||||||
|
// plugins) and consumers (core and custom Grafana kinds) of composable schemas.
|
||||||
|
//
|
||||||
|
// This contract is similar to an interface in most programming languages:
|
||||||
|
// producer and consumer implementations depend only on the schema interface
|
||||||
|
// definition, rather than the details of any particular implementation. This
|
||||||
|
// allows producers and consumers to be loosely coupled, while keeping an
|
||||||
|
// explicit contract for composition of sub-schemas from producers into the
|
||||||
|
// consumer schemas that want to use them.
|
||||||
|
//
|
||||||
|
// Schema interfaces allow schema composition to be broken down into a series of
|
||||||
|
// simple "what," "which," and "how" questions:
|
||||||
|
//
|
||||||
|
// - "What" is the subschema to be composed?
|
||||||
|
// - "How" should subschema(s) be composed into another schema to produce a unified result schema?
|
||||||
|
// - "Which" subset of known composable subschemas ("whats") should be provided in composition ("how")?
|
||||||
|
//
|
||||||
|
// On the producer side, Grafana plugin authors may provide Thema lineages
|
||||||
|
// within Composable kinds declared in .cue files adjacent to their
|
||||||
|
// plugin.json, following a pattern (see
|
||||||
|
// github.com/grafana/grafana/pkg/plugins/pfs.#GrafanaPlugin.composableKinds)
|
||||||
|
// corresponding to the name of the schema interface. Each such definition is
|
||||||
|
// an answer to "what."
|
||||||
|
//
|
||||||
|
// On the consumer side, any core or custom kind author can choose to define a
|
||||||
|
// standard Thema composition slot in its contained lineage that uses one of
|
||||||
|
// these schema interfaces as its meta-schema. The slot specification in Thema
|
||||||
|
// answers "how", for that kind.
|
||||||
|
//
|
||||||
|
// Composable kinds declared by a plugin are parsed and validated by Grafana's
|
||||||
|
// plugin system when a plugin is installed. This gives each Grafana instance a
|
||||||
|
// set of all known Composable kinds ("whats"), which can be narrowed into the
|
||||||
|
// subsets ("which") that each known Core or Custom can consume. These subsets
|
||||||
|
// are injected dynamically into the consumers, resulting in the final schema.
|
||||||
|
//
|
||||||
|
// For example, in the Thema lineage for the dashboard core kind:
|
||||||
|
// - There is a slot named `panelcfg`
|
||||||
|
// - It is constrained to accept only Thema lineages following the `panelcfg` schema interface
|
||||||
|
// - The composition logic specifies that the `panelcfg.PanelOptions` from each lineage provided
|
||||||
|
// to the dashboard lineage be one possibility for `panels[].options`
|
||||||
|
//
|
||||||
|
// (TODO actual implementation is pending https://github.com/grafana/thema/issue/8)
|
||||||
|
//
|
||||||
|
// Thus, the dashboard schema used for validation by any particular Grafana instance
|
||||||
|
// can tell the user if a particular dashboard with a `timeseries` panel has invalid
|
||||||
|
// values for `panels[].options`, even though neither the dashboard core kind, nor the
|
||||||
|
// the timeseries composable kind, are directly aware of (import) each other.
|
||||||
|
|
||||||
|
// A SchemaInterface defines a single Grafana schema interface.
|
||||||
|
SchemaInterface: {
|
||||||
|
// name is the unique identifier of the schema interface.
|
||||||
|
//
|
||||||
|
// Often used to provide namespacing of schema interface implementations
|
||||||
|
// in places where implementations must be enumerated, such as:
|
||||||
|
// - In-memory indexes in the Grafana backend
|
||||||
|
// - Documentation URLs
|
||||||
|
// - Parent directory paths or names in generated code
|
||||||
|
name: string & =~"^[A-Z][A-Za-z]{1,19}$"
|
||||||
|
|
||||||
|
// interface is the body of the SchemaInterface - the actual meta-schema that
|
||||||
|
// forms the shared contract between consumers (core & custom kind lineages)
|
||||||
|
// and producers (composable kind lineages).
|
||||||
|
interface: {}
|
||||||
|
|
||||||
|
// pluginTypes is a list of plugin types that are expected to produce composable
|
||||||
|
// kinds following this interface.
|
||||||
|
//
|
||||||
|
// Note that Grafana's plugin architecture intentionally does not enforce this.
|
||||||
|
// The worst that a violation (impl expected and absent, or impl present and not expected)
|
||||||
|
// will currently produce is a warning.
|
||||||
|
//
|
||||||
|
// TODO this relies on information in pkg/plugins/plugindef, awkward having it here
|
||||||
|
pluginTypes: [...string]
|
||||||
|
|
||||||
|
// Whether lineages implementing this are considered "grouped" or not. Generally
|
||||||
|
// this refers to whether an e.g. JSON object is ever expected to exist that
|
||||||
|
// corresponds to the whole schema, or to top-level fields within the schema.
|
||||||
|
//
|
||||||
|
// TODO see https://github.com/grafana/thema/issues/62
|
||||||
|
//
|
||||||
|
// The main effect is whether code generation should produce one type that represents
|
||||||
|
// the root schema for lineages, or only produce types for each of the top-level fields
|
||||||
|
// within the schema.
|
||||||
|
group: bool | *true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The canonical list of all Grafana schema interfaces.
|
||||||
|
schemaInterfaces: [N=string]: SchemaInterface & { name: N }
|
||||||
|
schemaInterfaces: {
|
||||||
|
Panel: {
|
||||||
|
interface: {
|
||||||
|
// Defines plugin-specific options for a panel that should be persisted. Required,
|
||||||
|
// though a panel without any options may specify an empty struct.
|
||||||
|
//
|
||||||
|
// Currently mapped to #Panel.options within the dashboard schema.
|
||||||
|
PanelOptions: {}
|
||||||
|
|
||||||
|
// Plugin-specific custom field properties. Optional.
|
||||||
|
//
|
||||||
|
// Currently mapped to #Panel.fieldConfig.defaults.custom within the dashboard schema.
|
||||||
|
PanelFieldConfig?: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginTypes: ["panel"]
|
||||||
|
|
||||||
|
// grouped b/c separate non-cross-referring elements always occur together in larger structure (panel)
|
||||||
|
group: true
|
||||||
|
}
|
||||||
|
Query: {
|
||||||
|
// The contract for the queries schema interface is itself a pattern:
|
||||||
|
// Each of its top-level fields must be represent a distinct query type for
|
||||||
|
// the datasource plugin. The queryType field acts as a discriminator, and
|
||||||
|
// is constrained to be the same as the name of the top-level field declaring it.
|
||||||
|
interface: [QT=string]: {
|
||||||
|
queryType?: QT
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginTypes: ["datasource"]
|
||||||
|
|
||||||
|
// grouped b/c separate, non-cross-referring elements are actually themselves each impls of the concept
|
||||||
|
// and it avoids us having to put more levels in the slot system (uggghhh)
|
||||||
|
group: true
|
||||||
|
}
|
||||||
|
DSOptions: {
|
||||||
|
interface: {
|
||||||
|
// Normal datasource configuration options.
|
||||||
|
Options: {}
|
||||||
|
// Sensitive datasource configuration options that require encryption.
|
||||||
|
SecureOptions: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginTypes: ["datasource"]
|
||||||
|
|
||||||
|
// group b/c separate, non-cross-referring elements have diff runtime representation due to encryption
|
||||||
|
group: true
|
||||||
|
}
|
||||||
|
}
|
||||||
121
pkg/kindsys/schema_interface.go
Normal file
121
pkg/kindsys/schema_interface.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package kindsys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"cuelang.org/go/cue"
|
||||||
|
"github.com/grafana/grafana/pkg/cuectx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchemaInterface represents one of Grafana's named schema interfaces.
|
||||||
|
//
|
||||||
|
// Canonical definition of schema interfaces is done in CUE. Instances of
|
||||||
|
// this type simply represent that information in Go.
|
||||||
|
// TODO link to framework docs
|
||||||
|
type SchemaInterface struct {
|
||||||
|
name string
|
||||||
|
group bool
|
||||||
|
raw cue.Value
|
||||||
|
plugins []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the SchemaInterface.
|
||||||
|
//
|
||||||
|
// The name is also used as the path at which a SchemaInterface lineage is defined in a
|
||||||
|
// plugin models.cue file.
|
||||||
|
func (s SchemaInterface) Name() string {
|
||||||
|
return s.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface returns the cue.Value representing the meta-schema that is the
|
||||||
|
// contract between core or custom kinds that compose the meta-schema, and the
|
||||||
|
// plugin-declared composable kinds that implement the meta-schema.
|
||||||
|
func (s SchemaInterface) Interface() cue.Value {
|
||||||
|
return s.raw.LookupPath(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip = cue.ParsePath("interface")
|
||||||
|
|
||||||
|
// Should indicates whether the given plugin type is expected (but not required)
|
||||||
|
// to produce a composable kind that implements this SchemaInterface.
|
||||||
|
func (s SchemaInterface) Should(plugintype string) bool {
|
||||||
|
pt := plugintype
|
||||||
|
for _, t := range s.plugins {
|
||||||
|
if pt == t {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGroup indicates whether the slot specifies a group lineage - one in which
|
||||||
|
// each top-level key represents a distinct schema for objects that are expected
|
||||||
|
// to exist in the wild, but objects corresponding to the root of the schema are not
|
||||||
|
// expected to exist.
|
||||||
|
func (s SchemaInterface) IsGroup() bool {
|
||||||
|
return s.group
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindSchemaInterface(name string) (SchemaInterface, error) {
|
||||||
|
sl, has := SchemaInterfaces(nil)[name]
|
||||||
|
if !has {
|
||||||
|
return SchemaInterface{}, fmt.Errorf("unsupported slot: %s", name)
|
||||||
|
}
|
||||||
|
return sl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultIfaces map[string]SchemaInterface
|
||||||
|
var onceIfaces sync.Once
|
||||||
|
|
||||||
|
// SchemaInterfaces returns a map of all [SchemaInterface]s defined by
|
||||||
|
// Grafana's kindsys framework.
|
||||||
|
//
|
||||||
|
// All calling code within grafana/grafana is expected to use Grafana's
|
||||||
|
// singleton [cue.Context], returned from [cuectx.GrafanaCUEContext]. If nil is
|
||||||
|
// passed, the singleton will be used. This is a reasonable default for external
|
||||||
|
// code, as well.
|
||||||
|
//
|
||||||
|
// TODO link to framework docs
|
||||||
|
func SchemaInterfaces(ctx *cue.Context) map[string]SchemaInterface {
|
||||||
|
if ctx == nil || ctx == cuectx.GrafanaCUEContext() {
|
||||||
|
// Ensure framework is loaded, even if this func is called
|
||||||
|
// from an init() somewhere.
|
||||||
|
onceIfaces.Do(func() {
|
||||||
|
defaultIfaces = doSchemaInterfaces(nil)
|
||||||
|
})
|
||||||
|
return defaultIfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
return doSchemaInterfaces(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doSchemaInterfaces(ctx *cue.Context) map[string]SchemaInterface {
|
||||||
|
fw := CUEFramework(ctx)
|
||||||
|
|
||||||
|
defs := fw.LookupPath(cue.ParsePath("schemaInterfaces"))
|
||||||
|
if !defs.Exists() {
|
||||||
|
panic("schemaInterfaces key does not exist in kindsys framework")
|
||||||
|
}
|
||||||
|
type typ struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PluginTypes []string `json:"pluginTypes"`
|
||||||
|
Group bool `json:"group"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ifaces := make(map[string]SchemaInterface)
|
||||||
|
iter, _ := defs.Fields() //nolint:errcheck
|
||||||
|
for iter.Next() {
|
||||||
|
k := iter.Selector().String()
|
||||||
|
v := &typ{}
|
||||||
|
_ = iter.Value().Decode(&v) //nolint:errcheck,gosec
|
||||||
|
ifaces[k] = SchemaInterface{
|
||||||
|
name: v.Name,
|
||||||
|
plugins: v.PluginTypes,
|
||||||
|
group: v.Group,
|
||||||
|
raw: iter.Value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifaces
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package kindsys
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"cuelang.org/go/cue"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Slot represents one of Grafana's named slot definitions.
|
|
||||||
// TODO link to framework docs
|
|
||||||
type Slot struct {
|
|
||||||
name string
|
|
||||||
raw cue.Value
|
|
||||||
plugins map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the Slot.
|
|
||||||
//
|
|
||||||
// The name is also used as the path at which a Slot lineage is defined in a
|
|
||||||
// plugin models.cue file.
|
|
||||||
func (s Slot) Name() string {
|
|
||||||
return s.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// MetaSchema returns the meta-schema that is the contract between core or
|
|
||||||
// custom kinds that compose the meta-schema, and the plugin-declared composable
|
|
||||||
// kinds that implement the meta-schema.
|
|
||||||
func (s Slot) MetaSchema() cue.Value {
|
|
||||||
return s.raw
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForPluginType indicates whether for this Slot, plugins of the given type may
|
|
||||||
// provide a slot implementation (first return value), and for those types that
|
|
||||||
// may, whether they must produce one (second return value).
|
|
||||||
//
|
|
||||||
// Expected values here are those in the set of
|
|
||||||
// ["github.com/grafana/grafana/pkg/plugins/plugindef".Type], though passing
|
|
||||||
// a string not in that set will harmlessly return {false, false}. That type is
|
|
||||||
// not used here to avoid import cycles.
|
|
||||||
//
|
|
||||||
// Note that, at least for now, plugins are not required to provide any slot
|
|
||||||
// implementations, and do so by simply not containing any .cue files in the
|
|
||||||
// "grafanaplugin" package. Consequently, the "must" return value is best
|
|
||||||
// understood as, "IF a plugin provides a *.cue files, it MUST contain an
|
|
||||||
// implementation of this slot."
|
|
||||||
func (s Slot) ForPluginType(plugintype string) (may, must bool) {
|
|
||||||
must, may = s.plugins[plugintype]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsGroup indicates whether the slot specifies a group lineage - one in which
|
|
||||||
// each top-level key represents a distinct schema for objects that are expected
|
|
||||||
// to exist in the wild, but objects corresponding to the root of the schema are not
|
|
||||||
// expected to exist.
|
|
||||||
func (s Slot) IsGroup() bool {
|
|
||||||
// TODO rely on first-class Thema properties for this, one they exist - https://github.com/grafana/thema/issues/62
|
|
||||||
switch s.name {
|
|
||||||
case "Panel", "DSOptions":
|
|
||||||
return true
|
|
||||||
case "Query":
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
panic("unreachable - unknown slot name " + s.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindSlot(name string) (*Slot, error) {
|
|
||||||
sl, has := AllSlots(nil)[name]
|
|
||||||
if !has {
|
|
||||||
return nil, fmt.Errorf("unsupported slot: %s", name)
|
|
||||||
}
|
|
||||||
return sl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllSlots returns a map of all [Slot]s defined in the Grafana kindsys
|
|
||||||
// framework.
|
|
||||||
//
|
|
||||||
// TODO cache this for core context
|
|
||||||
func AllSlots(ctx *cue.Context) map[string]*Slot {
|
|
||||||
fw := CUEFramework(ctx)
|
|
||||||
slots := make(map[string]*Slot)
|
|
||||||
|
|
||||||
// Ignore err, can only happen if we change structure of fw files, and all we'd
|
|
||||||
// do is panic and that's what the next line will do anyway. Same for similar ignored
|
|
||||||
// errors later in this func
|
|
||||||
iter, _ := fw.LookupPath(cue.ParsePath("pluginTypeMetaSchema")).Fields(cue.Optional(true))
|
|
||||||
type nameopt struct {
|
|
||||||
name string
|
|
||||||
req bool
|
|
||||||
}
|
|
||||||
plugslots := make(map[string][]nameopt)
|
|
||||||
for iter.Next() {
|
|
||||||
plugin := iter.Selector().String()
|
|
||||||
iiter, _ := iter.Value().Fields(cue.Optional(true))
|
|
||||||
for iiter.Next() {
|
|
||||||
slotname := iiter.Selector().String()
|
|
||||||
plugslots[slotname] = append(plugslots[slotname], nameopt{
|
|
||||||
name: plugin,
|
|
||||||
req: !iiter.IsOptional(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iter, _ = fw.LookupPath(cue.ParsePath("slots")).Fields(cue.Optional(true))
|
|
||||||
for iter.Next() {
|
|
||||||
n := iter.Selector().String()
|
|
||||||
sl := Slot{
|
|
||||||
name: n,
|
|
||||||
raw: iter.Value(),
|
|
||||||
plugins: make(map[string]bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, no := range plugslots[n] {
|
|
||||||
sl.plugins[no.name] = no.req
|
|
||||||
}
|
|
||||||
|
|
||||||
slots[n] = &sl
|
|
||||||
}
|
|
||||||
|
|
||||||
return slots
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package kindsys
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"cuelang.org/go/cue/cuecontext"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a brick-dumb test that just ensures slots are being loaded correctly
|
|
||||||
// from their declarations in .cue files.
|
|
||||||
//
|
|
||||||
// If this test fails, it's either because:
|
|
||||||
// - They're not being loaded correctly - there's a bug in kindsys somewhere, fix it
|
|
||||||
// - The set of slots names has been modified - update the static list here
|
|
||||||
func TestSlotsAreLoaded(t *testing.T) {
|
|
||||||
slots := []string{"Panel", "Query", "DSOptions"}
|
|
||||||
all := AllSlots(cuecontext.New())
|
|
||||||
var loadedSlots []string
|
|
||||||
for k := range all {
|
|
||||||
loadedSlots = append(loadedSlots, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(slots)
|
|
||||||
sort.Strings(loadedSlots)
|
|
||||||
|
|
||||||
require.Equal(t, slots, loadedSlots, "slots loaded from cue differs from fixture set - either a bug or fixture needs updating")
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package kindsys
|
|
||||||
|
|
||||||
// The slots named and specified in this file are meta-schemas that act as a
|
|
||||||
// shared contract between Grafana plugins (producers) and coremodel types
|
|
||||||
// (consumers).
|
|
||||||
//
|
|
||||||
// On the consumer side, any coremodel Thema lineage can choose to define a
|
|
||||||
// standard Thema composition slot that specifies one of these named slots as
|
|
||||||
// its meta-schema. Such a specification entails that all schemas in any lineage
|
|
||||||
// placed into that composition slot must adhere to the meta-schema.
|
|
||||||
//
|
|
||||||
// On the producer side, Grafana's plugin system enforces that certain plugin
|
|
||||||
// types are expected to provide Thema lineages for these named slots which
|
|
||||||
// adhere to the slot meta-schema.
|
|
||||||
//
|
|
||||||
// For example, the Panel slot is consumed by the dashboard coremodel, and is
|
|
||||||
// expected to be produced by panel plugins.
|
|
||||||
//
|
|
||||||
// The name given to each slot in this file must be used as the name of the
|
|
||||||
// slot in the coremodel, and the name of the field under which the lineage
|
|
||||||
// is provided in a plugin's models.cue file.
|
|
||||||
//
|
|
||||||
// Conformance to meta-schema is achieved by Thema's native lineage joinSchema,
|
|
||||||
// which Thema internals automatically enforce across all schemas in a lineage.
|
|
||||||
|
|
||||||
// Meta-schema for the Panel slot, as implemented in Grafana panel plugins.
|
|
||||||
//
|
|
||||||
// This is a grouped meta-schema, intended solely for use in composition. Object
|
|
||||||
// literals conforming to it are not expected to exist.
|
|
||||||
slots: Panel: {
|
|
||||||
// Defines plugin-specific options for a panel that should be persisted. Required,
|
|
||||||
// though a panel without any options may specify an empty struct.
|
|
||||||
//
|
|
||||||
// Currently mapped to #Panel.options within the dashboard schema.
|
|
||||||
PanelOptions: {...}
|
|
||||||
// Plugin-specific custom field properties. Optional.
|
|
||||||
//
|
|
||||||
// Currently mapped to #Panel.fieldConfig.defaults.custom within the dashboard schema.
|
|
||||||
PanelFieldConfig?: {...}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta-schema for the Query slot, as implemented in Grafana datasource plugins.
|
|
||||||
slots: Query: {...}
|
|
||||||
|
|
||||||
// Meta-schema for the DSOptions slot, as implemented in Grafana datasource plugins.
|
|
||||||
//
|
|
||||||
// This is a grouped meta-schema, intended solely for use in composition. Object
|
|
||||||
// literals conforming to it are not expected to exist.
|
|
||||||
slots: DSOptions: {
|
|
||||||
// Normal datasource configuration options.
|
|
||||||
Options: {...}
|
|
||||||
// Sensitive datasource configuration options that require encryption.
|
|
||||||
SecureOptions: {...}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pluginTypeMetaSchema defines which plugin types should use which metaschemas
|
|
||||||
// as joinSchema for the lineages declared at which paths.
|
|
||||||
pluginTypeMetaSchema: [string]: {...}
|
|
||||||
pluginTypeMetaSchema: {
|
|
||||||
// Panel plugins are expected to provide a lineage at path Panel conforming to
|
|
||||||
// the Panel joinSchema.
|
|
||||||
panel: {
|
|
||||||
Panel: slots.Panel
|
|
||||||
}
|
|
||||||
// Datasource plugins are expected to provide lineages at paths Query and
|
|
||||||
// DSOptions, conforming to those joinSchemas respectively.
|
|
||||||
datasource: {
|
|
||||||
Query: slots.Query
|
|
||||||
DSOptions: slots.DSOptions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ func (j *pgoJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pluginfolder := filepath.Base(decl.PluginPath)
|
pluginfolder := filepath.Base(decl.PluginPath)
|
||||||
slotname := strings.ToLower(decl.Slot.Name())
|
slotname := strings.ToLower(decl.SchemaInterface.Name())
|
||||||
filename := fmt.Sprintf("types_%s_gen.go", slotname)
|
filename := fmt.Sprintf("types_%s_gen.go", slotname)
|
||||||
f.RelativePath = filepath.Join(j.root, pluginfolder, filename)
|
f.RelativePath = filepath.Join(j.root, pluginfolder, filename)
|
||||||
f.From = append(f.From, j)
|
f.From = append(f.From, j)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (j *ptsJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slotname := decl.Slot.Name()
|
slotname := decl.SchemaInterface.Name()
|
||||||
v := decl.Lineage.Latest().Version()
|
v := decl.Lineage.Latest().Version()
|
||||||
|
|
||||||
tsf.Nodes = append(tsf.Nodes, tsast.Raw{
|
tsf.Nodes = append(tsf.Nodes, tsast.Raw{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package grafanaplugin
|
|||||||
import "github.com/grafana/thema"
|
import "github.com/grafana/thema"
|
||||||
|
|
||||||
Query: thema.#Lineage & {
|
Query: thema.#Lineage & {
|
||||||
name: "missing_slot_impl"
|
name: "missing_kind_datasource"
|
||||||
seqs: [
|
seqs: [
|
||||||
{
|
{
|
||||||
schemas: [
|
schemas: [
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"type": "datasource",
|
"type": "datasource",
|
||||||
"name": "Missing slot impl",
|
"name": "Missing kind impl",
|
||||||
"id": "missing-slot-datasource",
|
"id": "missing-kind-datasource",
|
||||||
"backend": true,
|
"backend": true,
|
||||||
"state": "alpha",
|
"state": "alpha",
|
||||||
"info": {
|
"info": {
|
||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PluginDecl struct {
|
type PluginDecl struct {
|
||||||
Slot *kindsys.Slot
|
SchemaInterface *kindsys.SchemaInterface
|
||||||
Lineage thema.Lineage
|
Lineage thema.Lineage
|
||||||
Imports []*ast.ImportSpec
|
Imports []*ast.ImportSpec
|
||||||
PluginPath string
|
PluginPath string
|
||||||
PluginMeta plugindef.PluginDef
|
PluginMeta plugindef.PluginDef
|
||||||
}
|
}
|
||||||
|
|
||||||
func EmptyPluginDecl(path string, meta plugindef.PluginDef) *PluginDecl {
|
func EmptyPluginDecl(path string, meta plugindef.PluginDef) *PluginDecl {
|
||||||
@@ -24,5 +24,5 @@ func EmptyPluginDecl(path string, meta plugindef.PluginDef) *PluginDecl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (decl *PluginDecl) HasSchema() bool {
|
func (decl *PluginDecl) HasSchema() bool {
|
||||||
return decl.Lineage != nil && decl.Slot != nil
|
return decl.Lineage != nil && decl.SchemaInterface != nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,17 +54,17 @@ func (psr *declParser) Parse(root fs.FS) ([]*PluginDecl, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for slotName, lin := range slots {
|
for slotName, lin := range slots {
|
||||||
slot, err := kindsys.FindSlot(slotName)
|
slot, err := kindsys.FindSchemaInterface(slotName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(fmt.Errorf("parsing plugin failed for %s: %s", dir, err))
|
log.Println(fmt.Errorf("parsing plugin failed for %s: %s", dir, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
decls = append(decls, &PluginDecl{
|
decls = append(decls, &PluginDecl{
|
||||||
Slot: slot,
|
SchemaInterface: &slot,
|
||||||
Lineage: lin,
|
Lineage: lin,
|
||||||
Imports: p.CUEImports(),
|
Imports: p.CUEImports(),
|
||||||
PluginMeta: p.Meta(),
|
PluginMeta: p.Meta(),
|
||||||
PluginPath: path,
|
PluginPath: path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ var allowedImportsStr string
|
|||||||
|
|
||||||
type slotandname struct {
|
type slotandname struct {
|
||||||
name string
|
name string
|
||||||
slot *kindsys.Slot
|
slot kindsys.SchemaInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
var allslots []slotandname
|
var allslots []slotandname
|
||||||
@@ -55,7 +55,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
allowedImportsStr = strings.Join(all, "\n")
|
allowedImportsStr = strings.Join(all, "\n")
|
||||||
|
|
||||||
for n, s := range kindsys.AllSlots(nil) {
|
for n, s := range kindsys.SchemaInterfaces(nil) {
|
||||||
allslots = append(allslots, slotandname{
|
allslots = append(allslots, slotandname{
|
||||||
name: n,
|
name: n,
|
||||||
slot: s,
|
slot: s,
|
||||||
@@ -93,7 +93,7 @@ func (t *Tree) SubPlugins() map[string]PluginInfo {
|
|||||||
type TreeList []*Tree
|
type TreeList []*Tree
|
||||||
|
|
||||||
// LineagesForSlot returns the set of plugin-defined lineages that implement a
|
// LineagesForSlot returns the set of plugin-defined lineages that implement a
|
||||||
// particular named Grafana slot (See ["github.com/grafana/grafana/pkg/framework/coremodel".Slot]).
|
// particular named Grafana slot (See ["github.com/grafana/grafana/pkg/framework/coremodel".SchemaInterface]).
|
||||||
func (tl TreeList) LineagesForSlot(slotname string) map[string]thema.Lineage {
|
func (tl TreeList) LineagesForSlot(slotname string) map[string]thema.Lineage {
|
||||||
m := make(map[string]thema.Lineage)
|
m := make(map[string]thema.Lineage)
|
||||||
for _, tree := range tl {
|
for _, tree := range tl {
|
||||||
@@ -218,12 +218,14 @@ func ParsePluginFS(f fs.FS, rt *thema.Runtime) (*Tree, error) {
|
|||||||
}
|
}
|
||||||
for _, s := range allslots {
|
for _, s := range allslots {
|
||||||
iv := val.LookupPath(cue.ParsePath(s.slot.Name()))
|
iv := val.LookupPath(cue.ParsePath(s.slot.Name()))
|
||||||
lin, err := bindSlotLineage(iv, s.slot, r.meta, rt)
|
if iv.Exists() {
|
||||||
if lin != nil {
|
lin, err := bindSlotLineage(iv, s.slot, r.meta, rt)
|
||||||
r.slotimpls[s.slot.Name()] = lin
|
if lin != nil {
|
||||||
}
|
r.slotimpls[s.slot.Name()] = lin
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,8 +233,10 @@ func ParsePluginFS(f fs.FS, rt *thema.Runtime) (*Tree, error) {
|
|||||||
return tree, nil
|
return tree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindSlotLineage(v cue.Value, s *kindsys.Slot, meta plugindef.PluginDef, rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
func bindSlotLineage(v cue.Value, s kindsys.SchemaInterface, meta plugindef.PluginDef, rt *thema.Runtime, opts ...thema.BindOption) (thema.Lineage, error) {
|
||||||
accept, required := s.ForPluginType(string(meta.Type))
|
// temporarily keep this around, there are IMMEDIATE plans to refactor
|
||||||
|
var required bool
|
||||||
|
accept := s.Should(string(meta.Type))
|
||||||
exists := v.Exists()
|
exists := v.Exists()
|
||||||
|
|
||||||
if !accept {
|
if !accept {
|
||||||
|
|||||||
@@ -115,9 +115,7 @@ func TestParseTreeTestdata(t *testing.T) {
|
|||||||
"wrong-slot-panel": {
|
"wrong-slot-panel": {
|
||||||
err: ErrImplementedSlots,
|
err: ErrImplementedSlots,
|
||||||
},
|
},
|
||||||
"missing-slot-impl": {
|
"missing-kind-datasource": {},
|
||||||
err: ErrImplementedSlots,
|
|
||||||
},
|
|
||||||
"panel-conflicting-joinschema": {
|
"panel-conflicting-joinschema": {
|
||||||
err: ErrInvalidLineage,
|
err: ErrInvalidLineage,
|
||||||
skip: "TODO implement BindOption in thema, SatisfiesJoinSchema, then use it here",
|
skip: "TODO implement BindOption in thema, SatisfiesJoinSchema, then use it here",
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func adaptToPipeline(j codejen.OneToOne[corecodegen.SchemaForGen]) codejen.OneTo
|
|||||||
return corecodegen.SchemaForGen{
|
return corecodegen.SchemaForGen{
|
||||||
Name: pd.PluginMeta.Name,
|
Name: pd.PluginMeta.Name,
|
||||||
Schema: pd.Lineage.Latest(),
|
Schema: pd.Lineage.Latest(),
|
||||||
IsGroup: pd.Slot.IsGroup(),
|
IsGroup: pd.SchemaInterface.IsGroup(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user