Schemas: Replace registry generation and github workflow (#83490)

* Create small registries for core and composable kinds

* Update workflow with new registries

* Fix imports in plugin schemas and deleted old registry generation files

* Remove verification and maturity

* Modify registries and add missing composable information to make schemas in kind-registry work

* Add missing aliases

* Remove unused templates

* Remove kinds verification

* Format generated code

* Add gen header

* Delete unused code and clean path in composable template

* Delete kind-registry loader

* Delete unused code

* Update License link

* Update codeowners path

* Sort imports

* More cleanup

* Remove verify-kinds.yml from codeowners

* Fix lint

* Update composable_kidns

* Fix cue extension

* Restore verify-kinds to avoid to push outdated kind's registry

* Fix composable format

* Restore code owners for verify-kinds

* Remove verify check
This commit is contained in:
Selene
2024-03-13 18:05:21 +02:00
committed by GitHub
parent fd9031ca37
commit 5c7849417b
34 changed files with 1049 additions and 1710 deletions
+6
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"path/filepath"
"cuelang.org/go/cue"
"github.com/grafana/codejen"
"github.com/grafana/kindsys"
"github.com/grafana/thema"
@@ -59,3 +60,8 @@ type SchemaForGen struct {
// Whether the schema is grouped. See https://github.com/grafana/thema/issues/62
IsGroup bool
}
type CueSchema struct {
CueFile cue.Value
FilePath string
}
-64
View File
@@ -1,64 +0,0 @@
package codegen
import (
"bytes"
"fmt"
"path/filepath"
"github.com/grafana/codejen"
"github.com/grafana/kindsys"
)
// BaseCoreRegistryJenny generates a static registry for core kinds that
// only initializes their [kindsys.Kind]. No slot kinds are composed.
//
// Path should be the relative path to the directory that will contain the
// generated registry. kindrelroot should be the repo-root-relative path to the
// parent directory to all directories that contain generated kind bindings
// (e.g. pkg/kind).
func BaseCoreRegistryJenny(path, kindrelroot string) ManyToOne {
return &genBaseRegistry{
path: path,
kindrelroot: kindrelroot,
}
}
type genBaseRegistry struct {
path string
kindrelroot string
}
func (gen *genBaseRegistry) JennyName() string {
return "BaseCoreRegistryJenny"
}
func (gen *genBaseRegistry) Generate(kinds ...kindsys.Kind) (*codejen.File, error) {
cores := make([]kindsys.Core, 0, len(kinds))
for _, d := range kinds {
if corekind, is := d.(kindsys.Core); is {
cores = append(cores, corekind)
}
}
if len(cores) == 0 {
return nil, nil
}
buf := new(bytes.Buffer)
if err := tmpls.Lookup("kind_registry.tmpl").Execute(buf, tvars_kind_registry{
PackageName: filepath.Base(gen.path),
KindPackagePrefix: filepath.ToSlash(filepath.Join("github.com/grafana/grafana", gen.kindrelroot)),
Kinds: cores,
}); err != nil {
return nil, fmt.Errorf("failed executing kind registry template: %w", err)
}
b, err := postprocessGoFile(genGoFile{
path: gen.path,
in: buf.Bytes(),
})
if err != nil {
return nil, err
}
return codejen.NewFile(filepath.Join(gen.path, "base_gen.go"), b, gen), nil
}
+62
View File
@@ -0,0 +1,62 @@
package codegen
import (
"bytes"
"fmt"
"go/format"
"path/filepath"
"strings"
"cuelang.org/go/cue"
"github.com/grafana/codejen"
)
var registryPath = filepath.Join("pkg", "registry", "schemas")
// CoreRegistryJenny generates a registry with all core kinds.
type CoreRegistryJenny struct {
}
func (jenny *CoreRegistryJenny) JennyName() string {
return "CoreRegistryJenny"
}
func (jenny *CoreRegistryJenny) Generate(cueFiles []CueSchema) (codejen.Files, error) {
schemas := make([]Schema, len(cueFiles))
for i, v := range cueFiles {
name, err := getSchemaName(v.CueFile)
if err != nil {
return nil, err
}
schemas[i] = Schema{
Name: name,
FilePath: v.FilePath,
}
}
buf := new(bytes.Buffer)
if err := tmpls.Lookup("core_registry.tmpl").Execute(buf, tvars_registry{
Schemas: schemas,
}); err != nil {
return nil, fmt.Errorf("failed executing kind registry template: %w", err)
}
b, err := format.Source(buf.Bytes())
if err != nil {
return nil, err
}
file := codejen.NewFile(filepath.Join(registryPath, "core_kind.go"), b, jenny)
return codejen.Files{*file}, nil
}
func getSchemaName(v cue.Value) (string, error) {
name, err := getPackageName(v)
if err != nil {
return "", err
}
name = strings.Replace(name, "-", "_", -1)
return strings.ToLower(name), nil
}
-72
View File
@@ -1,72 +0,0 @@
package codegen
import (
"bytes"
"fmt"
"path/filepath"
"github.com/grafana/codejen"
"github.com/grafana/kindsys"
)
// CoreKindJenny generates the implementation of [kindsys.Core] for the provided
// kind declaration.
//
// gokindsdir should be the relative path to the parent directory that contains
// all generated kinds.
//
// This generator only has output for core structured kinds.
func CoreKindJenny(gokindsdir string, cfg *CoreKindJennyConfig) OneToOne {
if cfg == nil {
cfg = new(CoreKindJennyConfig)
}
if cfg.GenDirName == nil {
cfg.GenDirName = func(def kindsys.Kind) string {
return def.Props().Common().MachineName
}
}
return &coreKindJenny{
gokindsdir: gokindsdir,
cfg: cfg,
}
}
// CoreKindJennyConfig holds configuration options for [CoreKindJenny].
type CoreKindJennyConfig struct {
// GenDirName returns the name of the directory in which the file should be
// generated. Defaults to DefForGen.Lineage().Name() if nil.
GenDirName func(kindsys.Kind) string
}
type coreKindJenny struct {
gokindsdir string
cfg *CoreKindJennyConfig
}
var _ OneToOne = &coreKindJenny{}
func (gen *coreKindJenny) JennyName() string {
return "CoreKindJenny"
}
func (gen *coreKindJenny) Generate(kind kindsys.Kind) (*codejen.File, error) {
if _, is := kind.(kindsys.Core); !is {
return nil, nil
}
path := filepath.Join(gen.gokindsdir, gen.cfg.GenDirName(kind), kind.Props().Common().MachineName+"_kind_gen.go")
buf := new(bytes.Buffer)
if err := tmpls.Lookup("kind_core.tmpl").Execute(buf, kind); err != nil {
return nil, fmt.Errorf("failed executing kind_core template for %s: %w", path, err)
}
b, err := postprocessGoFile(genGoFile{
path: path,
in: buf.Bytes(),
})
if err != nil {
return nil, err
}
return codejen.NewFile(path, b, gen), nil
}
+3 -3
View File
@@ -18,15 +18,15 @@ func (jenny *K8ResourcesJenny) JennyName() string {
return "K8ResourcesJenny"
}
func (jenny *K8ResourcesJenny) Generate(cueFiles []cue.Value) (codejen.Files, error) {
func (jenny *K8ResourcesJenny) Generate(cueFiles []CueSchema) (codejen.Files, error) {
files := make(codejen.Files, 0)
for _, val := range cueFiles {
pkg, err := getPackageName(val)
pkg, err := getPackageName(val.CueFile)
if err != nil {
return nil, err
}
resource, err := jenny.genResource(pkg, val)
resource, err := jenny.genResource(pkg, val.CueFile)
if err != nil {
return nil, err
}
+10 -6
View File
@@ -7,7 +7,6 @@ import (
"time"
"github.com/grafana/codejen"
"github.com/grafana/kindsys"
)
// All the parsed templates in the tmpl subdirectory
@@ -33,11 +32,7 @@ type (
From string
Leader string
}
tvars_kind_registry struct {
PackageName string
KindPackagePrefix string
Kinds []kindsys.Core
}
tvars_resource struct {
PackageName string
KindName string
@@ -51,4 +46,13 @@ type (
tvars_status struct {
PackageName string
}
tvars_registry struct {
Schemas []Schema
}
Schema struct {
Name string
FilePath string
}
)
+46
View File
@@ -0,0 +1,46 @@
package schemas
import (
"os"
"path/filepath"
"runtime"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
)
type CoreKind struct {
Name string
CueFile cue.Value
}
func GetCoreKinds() ([]CoreKind, error) {
ctx := cuecontext.New()
kinds := make([]CoreKind, 0)
_, caller, _, _ := runtime.Caller(0)
root := filepath.Join(caller, "../../../..")
{{- range .Schemas }}
{{ .Name }}Cue, err := loadCueFile(ctx, filepath.Join(root, "{{ .FilePath }}"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "{{ .Name }}",
CueFile: {{ .Name }}Cue,
})
{{- end }}
return kinds, nil
}
func loadCueFile(ctx *cue.Context, path string) (cue.Value, error) {
cueFile, err := os.ReadFile(path)
if err != nil {
return cue.Value{}, err
}
return ctx.CompileBytes(cueFile), nil
}
-70
View File
@@ -1,70 +0,0 @@
package {{ .Props.MachineName }}
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/{{ .Props.MachineName }}"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("{{ .Props.MachineName }}.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the {{ .Props.Name }} [Resource] type generated from the current schema, v{{ .Props.CurrentVersion }}.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of {{ .Props.Name }} [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
-61
View File
@@ -1,61 +0,0 @@
package {{ .PackageName }}
import (
"fmt"
"sync"
{{range .Kinds }}
"{{ $.KindPackagePrefix }}/{{ .Props.MachineName }}"{{end}}
"github.com/grafana/grafana/pkg/cuectx"
"github.com/grafana/kindsys"
"github.com/grafana/thema"
)
// Base is a registry of all Grafana core kinds. It is designed for use both inside
// of Grafana itself, and for import by external Go programs wanting to work with Grafana's
// kind system.
//
// The registry provides two modes for accessing core kinds:
// * Per-kind methods, which return the kind-specific type, e.g. Dashboard() returns [dashboard.Dashboard].
// * All(), which returns a slice of [kindsys.Core].
//
// Prefer the individual named methods for use cases where the particular kind(s) that
// are needed are known to the caller. For example, a dashboard linter can know that it
// specifically wants the dashboard kind.
//
// Prefer All() when performing operations generically across all kinds. For example,
// a generic HTTP middleware for validating request bodies expected to contain some
// kind-schematized type.
type Base struct {
all []kindsys.Core
{{- range .Kinds }}
{{ .Props.MachineName }} *{{ .Props.MachineName }}.Kind{{end}}
}
// type guards
var (
{{- range .Kinds }}
_ kindsys.Core = &{{ .Props.MachineName }}.Kind{}{{end}}
)
{{range .Kinds }}
// {{ .Props.Name }} returns the [kindsys.Interface] implementation for the {{ .Props.MachineName }} kind.
func (b *Base) {{ .Props.Name }}() *{{ .Props.MachineName }}.Kind {
return b.{{ .Props.MachineName }}
}
{{end}}
func doNewBase(rt *thema.Runtime) *Base {
var err error
reg := &Base{}
{{range .Kinds }}
reg.{{ .Props.MachineName }}, err = {{ .Props.MachineName }}.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the {{ .Props.MachineName }} Kind: %s", err))
}
reg.all = append(reg.all, reg.{{ .Props.MachineName }})
{{end}}
return reg
}
-60
View File
@@ -1,74 +1,14 @@
package codegen
import (
"bytes"
"fmt"
"go/format"
"go/parser"
"go/token"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/dave/dst"
"github.com/dave/dst/decorator"
"github.com/dave/dst/dstutil"
"golang.org/x/tools/imports"
)
type genGoFile struct {
path string
walker dstutil.ApplyFunc
in []byte
}
func postprocessGoFile(cfg genGoFile) ([]byte, error) {
fname := filepath.Base(cfg.path)
buf := new(bytes.Buffer)
fset := token.NewFileSet()
gf, err := decorator.ParseFile(fset, fname, string(cfg.in), parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("error parsing generated file: %w", err)
}
if cfg.walker != nil {
dstutil.Apply(gf, cfg.walker, nil)
err = format.Node(buf, fset, gf)
if err != nil {
return nil, fmt.Errorf("error formatting Go AST: %w", err)
}
} else {
buf = bytes.NewBuffer(cfg.in)
}
byt, err := imports.Process(fname, buf.Bytes(), nil)
if err != nil {
return nil, fmt.Errorf("goimports processing failed: %w", err)
}
// Compare imports before and after; warn about performance if some were added
gfa, _ := parser.ParseFile(fset, fname, string(byt), parser.ParseComments)
imap := make(map[string]bool)
for _, im := range gf.Imports {
imap[im.Path.Value] = true
}
var added []string
for _, im := range gfa.Imports {
if !imap[im.Path.Value] {
added = append(added, im.Path.Value)
}
}
if len(added) != 0 {
// TODO improve the guidance in this error if/when we better abstract over imports to generate
fmt.Fprintf(os.Stderr, "The following imports were added by goimports while generating %s: \n\t%s\nRelying on goimports to find imports significantly slows down code generation. Consider adding these to the relevant template.\n", cfg.path, strings.Join(added, "\n\t"))
}
return byt, nil
}
type prefixmod struct {
prefix string
replace string
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package accesspolicy
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/accesspolicy"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("accesspolicy.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the AccessPolicy [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of AccessPolicy [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
-79
View File
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package dashboard
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/dashboard"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("dashboard.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the Dashboard [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of Dashboard [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package librarypanel
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/librarypanel"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("librarypanel.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the LibraryPanel [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of LibraryPanel [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package preferences
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/preferences"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("preferences.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the Preferences [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of Preferences [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package publicdashboard
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/publicdashboard"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("publicdashboard.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the PublicDashboard [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of PublicDashboard [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
-79
View File
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package role
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/role"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("role.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the Role [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of Role [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package rolebinding
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/rolebinding"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("rolebinding.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the RoleBinding [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of RoleBinding [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
-79
View File
@@ -1,79 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreKindJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package team
import (
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/thema/vmux"
"github.com/grafana/grafana/pkg/cuectx"
)
// rootrel is the relative path from the grafana repository root to the
// directory containing the .cue files in which this kind is defined. Necessary
// for runtime errors related to the definition and/or lineage to provide
// a real path to the correct .cue file.
const rootrel string = "kinds/team"
// TODO standard generated docs
type Kind struct {
kindsys.Core
lin thema.ConvergentLineage[*Resource]
jcodec vmux.Codec
valmux vmux.ValueMux[*Resource]
}
// type guard - ensure generated Kind type satisfies the kindsys.Core interface
var _ kindsys.Core = &Kind{}
// TODO standard generated docs
func NewKind(rt *thema.Runtime, opts ...thema.BindOption) (*Kind, error) {
def, err := cuectx.LoadCoreKindDef(rootrel, rt.Context(), nil)
if err != nil {
return nil, err
}
k := &Kind{}
k.Core, err = kindsys.BindCore(rt, def, opts...)
if err != nil {
return nil, err
}
// Get the thema.Schema that the meta says is in the current version (which
// codegen ensures is always the latest)
cursch := thema.SchemaP(k.Core.Lineage(), def.Properties.CurrentVersion)
tsch, err := thema.BindType(cursch, &Resource{})
if err != nil {
// Should be unreachable, modulo bugs in the Thema->Go code generator
return nil, err
}
k.jcodec = vmux.NewJSONCodec("team.json")
k.lin = tsch.ConvergentLineage()
k.valmux = vmux.NewValueMux(k.lin.TypedSchema(), k.jcodec)
return k, nil
}
// ConvergentLineage returns the same [thema.Lineage] as Lineage, but bound (see [thema.BindType])
// to the the Team [Resource] type generated from the current schema, v0.0.
func (k *Kind) ConvergentLineage() thema.ConvergentLineage[*Resource] {
return k.lin
}
// JSONValueMux is a version multiplexer that maps a []byte containing JSON data
// at any schematized dashboard version to an instance of Team [Resource].
//
// Validation and translation errors emitted from this func will identify the
// input bytes as "dashboard.json".
//
// This is a thin wrapper around Thema's [vmux.ValueMux].
func (k *Kind) JSONValueMux(b []byte) (*Resource, thema.TranslationLacunas, error) {
return k.valmux(b)
}
@@ -0,0 +1,74 @@
package codegen
import (
"bytes"
"fmt"
"go/format"
"path/filepath"
"strings"
"github.com/grafana/codejen"
)
var registryPath = filepath.Join("pkg", "registry", "schemas")
var renamedPlugins = map[string]string{
"cloud-monitoring": "googlecloudmonitoring",
"grafana-pyroscope-datasource": "grafanapyroscope",
"annolist": "annotationslist",
"grafanatestdatadatasource": "testdata",
"dashlist": "dashboardlist",
}
type PluginRegistryJenny struct {
}
func (jenny *PluginRegistryJenny) JennyName() string {
return "PluginRegistryJenny"
}
func (jenny *PluginRegistryJenny) Generate(files []string) (*codejen.File, error) {
if len(files) == 0 {
return nil, nil
}
schemas := make([]Schema, len(files))
for i, file := range files {
name, err := getSchemaName(file)
if err != nil {
return nil, fmt.Errorf("unable to find schema name: %s", err)
}
schemas[i] = Schema{
Name: name,
Filename: filepath.Base(file),
FilePath: file,
}
}
buf := new(bytes.Buffer)
if err := tmpls.Lookup("composable_registry.tmpl").Execute(buf, tmpl_vars_plugin_registry{
Schemas: schemas,
}); err != nil {
return nil, fmt.Errorf("failed executing kind registry template: %w", err)
}
b, err := format.Source(buf.Bytes())
if err != nil {
return nil, err
}
return codejen.NewFile(filepath.Join(registryPath, "composable_kind.go"), b, jenny), nil
}
func getSchemaName(path string) (string, error) {
parts := strings.Split(path, "/")
if len(parts) < 2 {
return "", fmt.Errorf("path should contain more than 2 elements")
}
folderName := parts[len(parts)-2]
if renamed, ok := renamedPlugins[folderName]; ok {
folderName = renamed
}
folderName = strings.ReplaceAll(folderName, "-", "")
return strings.ToLower(folderName), nil
}
-100
View File
@@ -1,100 +0,0 @@
package codegen
import (
"bytes"
"fmt"
"path"
"path/filepath"
"strings"
"github.com/grafana/codejen"
"github.com/grafana/grafana/pkg/plugins/pfs"
)
const prefix = "github.com/grafana/grafana/public/app/plugins"
// PluginTreeListJenny creates a [codejen.ManyToOne] that produces Go code
// for loading a [pfs.PluginList] given [*kindsys.PluginDecl] as inputs.
func PluginTreeListJenny() codejen.ManyToOne[*pfs.PluginDecl] {
outputFile := filepath.Join("pkg", "plugins", "pfs", "corelist", "corelist_load_gen.go")
return &ptlJenny{
outputFile: outputFile,
plugins: make(map[string]bool, 0),
}
}
type ptlJenny struct {
outputFile string
plugins map[string]bool
}
func (j *ptlJenny) JennyName() string {
return "PluginTreeListJenny"
}
func (j *ptlJenny) Generate(decls ...*pfs.PluginDecl) (*codejen.File, error) {
buf := new(bytes.Buffer)
vars := templateVars_plugin_registry{
Plugins: make([]struct {
PkgName, Path, ImportPath string
NoAlias bool
}, 0, len(decls)),
}
type tpl struct {
PkgName, Path, ImportPath string
NoAlias bool
}
for _, decl := range decls {
meta := decl.PluginMeta
if _, exists := j.plugins[meta.Id]; exists {
continue
}
pluginId := j.sanitizePluginId(meta.Id)
vars.Plugins = append(vars.Plugins, tpl{
PkgName: pluginId,
NoAlias: pluginId != filepath.Base(decl.PluginPath),
ImportPath: filepath.ToSlash(filepath.Join(prefix, decl.PluginPath)),
Path: path.Join(append(strings.Split(prefix, "/")[3:], decl.PluginPath)...),
})
j.plugins[meta.Id] = true
}
if err := tmpls.Lookup("plugin_registry.tmpl").Execute(buf, vars); err != nil {
return nil, fmt.Errorf("failed executing plugin registry template: %w", err)
}
byt, err := postprocessGoFile(genGoFile{
path: j.outputFile,
in: buf.Bytes(),
})
if err != nil {
return nil, fmt.Errorf("error postprocessing plugin registry: %w", err)
}
return codejen.NewFile(j.outputFile, byt, j), nil
}
func (j *ptlJenny) sanitizePluginId(s string) string {
return strings.Map(func(r rune) rune {
switch {
case r >= 'a' && r <= 'z':
fallthrough
case r >= 'A' && r <= 'Z':
fallthrough
case r >= '0' && r <= '9':
fallthrough
case r == '_':
return r
case r == '-':
return '_'
default:
return -1
}
}, s)
}
+8 -7
View File
@@ -22,12 +22,13 @@ var tmplFS embed.FS
// The following group of types, beginning with templateVars_*, all contain the set
// of variables expected by the corresponding named template file under tmpl/
type (
templateVars_plugin_registry struct {
Plugins []struct {
PkgName string
Path string
ImportPath string
NoAlias bool
}
tmpl_vars_plugin_registry struct {
Schemas []Schema
}
Schema struct {
Name string
Filename string
FilePath string
}
)
@@ -0,0 +1,132 @@
package schemas
import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"runtime"
"testing/fstest"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
)
var cueImportsPath = filepath.Join("packages", "grafana-schema", "src", "common")
var importPath = "github.com/grafana/grafana/packages/grafana-schema/src/common"
type ComposableKind struct {
Name string
Filename string
CueFile cue.Value
}
func GetComposableKinds() ([]ComposableKind, error) {
kinds := make([]ComposableKind, 0)
_, caller, _, _ := runtime.Caller(0)
root := filepath.Join(caller, "../../../..")
{{- range .Schemas }}
{{ .Name }}Cue, err := loadCueFileWithCommon(root, filepath.Join(root, "{{ .FilePath }}"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "{{ .Name }}",
Filename: "{{ .Filename }}",
CueFile: {{ .Name }}Cue,
})
{{- end }}
return kinds, nil
}
func loadCueFileWithCommon(root string, entrypoint string) (cue.Value, error) {
commonFS, err := mockCommonFS(root)
if err != nil {
fmt.Printf("cannot load common cue files: %s\n", err)
return cue.Value{}, err
}
overlay, err := buildOverlay(commonFS)
if err != nil {
fmt.Printf("Cannot build overlay: %s\n", err)
return cue.Value{}, err
}
bis := load.Instances([]string{entrypoint}, &load.Config{
ModuleRoot: "/",
Overlay: overlay,
})
values, err := cuecontext.New().BuildInstances(bis)
if err != nil {
fmt.Printf("Cannot build instance: %s\n", err)
return cue.Value{}, err
}
return values[0], nil
}
func mockCommonFS(root string) (fs.FS, error) {
path := filepath.Join(root, cueImportsPath)
dir, err := os.ReadDir(path)
if err != nil {
return nil, fmt.Errorf("cannot open common cue files directory: %s", err)
}
prefix := "cue.mod/pkg/" + importPath
commonFS := fstest.MapFS{}
for _, d := range dir {
if d.IsDir() {
continue
}
readPath := filepath.Join(path, d.Name())
b, err := os.ReadFile(filepath.Clean(readPath))
if err != nil {
return nil, err
}
commonFS[filepath.Join(prefix, d.Name())] = &fstest.MapFile{Data: b}
}
return commonFS, nil
}
// It loads common cue files into the schema to be able to make import works
func buildOverlay(commonFS fs.FS) (map[string]load.Source, error) {
overlay := make(map[string]load.Source)
err := fs.WalkDir(commonFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
f, err := commonFS.Open(path)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
b, err := io.ReadAll(f)
if err != nil {
return err
}
overlay[filepath.Join("/", path)] = load.FromBytes(b)
return nil
})
return overlay, err
}
@@ -1,30 +0,0 @@
package corelist
import (
"fmt"
"io/fs"
"sync"
"github.com/grafana/grafana"
"github.com/grafana/grafana/pkg/plugins/pfs"
"github.com/grafana/thema"
)
func parsePluginOrPanic(path string, pkgname string, rt *thema.Runtime) pfs.ParsedPlugin {
sub, err := fs.Sub(grafana.CueSchemaFS, path)
if err != nil {
panic("could not create fs sub to " + path)
}
pp, err := pfs.ParsePluginFS(sub, rt)
if err != nil {
panic(fmt.Sprintf("error parsing plugin metadata for %s: %s", pkgname, err))
}
return pp
}
func corePlugins(rt *thema.Runtime) []pfs.ParsedPlugin{
return []pfs.ParsedPlugin{
{{- range .Plugins }}
parsePluginOrPanic("{{ .Path }}", "{{ .PkgName }}", rt),
{{- end }}
}
}
-68
View File
@@ -1,68 +0,0 @@
package codegen
import (
"bytes"
"fmt"
"go/format"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
"github.com/dave/dst/decorator"
"github.com/dave/dst/dstutil"
"golang.org/x/tools/imports"
)
type genGoFile struct {
path string
walker dstutil.ApplyFunc
in []byte
}
func postprocessGoFile(cfg genGoFile) ([]byte, error) {
fname := filepath.Base(cfg.path)
buf := new(bytes.Buffer)
fset := token.NewFileSet()
gf, err := decorator.ParseFile(fset, fname, string(cfg.in), parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("error parsing generated file: %w", err)
}
if cfg.walker != nil {
dstutil.Apply(gf, cfg.walker, nil)
err = format.Node(buf, fset, gf)
if err != nil {
return nil, fmt.Errorf("error formatting Go AST: %w", err)
}
} else {
buf = bytes.NewBuffer(cfg.in)
}
byt, err := imports.Process(fname, buf.Bytes(), nil)
if err != nil {
return nil, fmt.Errorf("goimports processing failed: %w", err)
}
// Compare imports before and after; warn about performance if some were added
gfa, _ := parser.ParseFile(fset, fname, string(byt), parser.ParseComments)
imap := make(map[string]bool)
for _, im := range gf.Imports {
imap[im.Path.Value] = true
}
var added []string
for _, im := range gfa.Imports {
if !imap[im.Path.Value] {
added = append(added, im.Path.Value)
}
}
if len(added) != 0 {
// TODO improve the guidance in this error if/when we better abstract over imports to generate
fmt.Fprintf(os.Stderr, "The following imports were added by goimports while generating %s: \n\t%s\nRelying on goimports to find imports significantly slows down code generation. Consider adding these to the relevant template.\n", cfg.path, strings.Join(added, "\n\t"))
}
return byt, nil
}
-42
View File
@@ -1,42 +0,0 @@
package corekind
import (
"sync"
"github.com/grafana/kindsys"
"github.com/grafana/thema"
"github.com/grafana/grafana/pkg/cuectx"
)
var (
baseOnce sync.Once
defaultBase *Base
)
// NewBase provides a registry of all core raw and structured kinds, without any
// composition of slot kinds.
//
// All calling code within grafana/grafana is expected to use Grafana's
// singleton [thema.Runtime], returned from [cuectx.GrafanaThemaRuntime]. If nil
// is passed, the singleton will be used.
func NewBase(rt *thema.Runtime) *Base {
allrt := cuectx.GrafanaThemaRuntime()
if rt == nil || rt == allrt {
baseOnce.Do(func() {
defaultBase = doNewBase(allrt)
})
return defaultBase
}
return doNewBase(rt)
}
// All returns a slice of [kindsys.Core] containing all core Grafana kinds.
//
// The returned slice is sorted lexicographically by kind machine name.
func (b *Base) All() []kindsys.Core {
ret := make([]kindsys.Core, len(b.all))
copy(ret, b.all)
return ret
}
-159
View File
@@ -1,159 +0,0 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// BaseCoreRegistryJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package corekind
import (
"fmt"
"github.com/grafana/grafana/pkg/kinds/accesspolicy"
"github.com/grafana/grafana/pkg/kinds/dashboard"
"github.com/grafana/grafana/pkg/kinds/librarypanel"
"github.com/grafana/grafana/pkg/kinds/preferences"
"github.com/grafana/grafana/pkg/kinds/publicdashboard"
"github.com/grafana/grafana/pkg/kinds/role"
"github.com/grafana/grafana/pkg/kinds/rolebinding"
"github.com/grafana/grafana/pkg/kinds/team"
"github.com/grafana/kindsys"
"github.com/grafana/thema"
)
// Base is a registry of all Grafana core kinds. It is designed for use both inside
// of Grafana itself, and for import by external Go programs wanting to work with Grafana's
// kind system.
//
// The registry provides two modes for accessing core kinds:
// - Per-kind methods, which return the kind-specific type, e.g. Dashboard() returns [dashboard.Dashboard].
// - All(), which returns a slice of [kindsys.Core].
//
// Prefer the individual named methods for use cases where the particular kind(s) that
// are needed are known to the caller. For example, a dashboard linter can know that it
// specifically wants the dashboard kind.
//
// Prefer All() when performing operations generically across all kinds. For example,
// a generic HTTP middleware for validating request bodies expected to contain some
// kind-schematized type.
type Base struct {
all []kindsys.Core
accesspolicy *accesspolicy.Kind
dashboard *dashboard.Kind
librarypanel *librarypanel.Kind
preferences *preferences.Kind
publicdashboard *publicdashboard.Kind
role *role.Kind
rolebinding *rolebinding.Kind
team *team.Kind
}
// type guards
var (
_ kindsys.Core = &accesspolicy.Kind{}
_ kindsys.Core = &dashboard.Kind{}
_ kindsys.Core = &librarypanel.Kind{}
_ kindsys.Core = &preferences.Kind{}
_ kindsys.Core = &publicdashboard.Kind{}
_ kindsys.Core = &role.Kind{}
_ kindsys.Core = &rolebinding.Kind{}
_ kindsys.Core = &team.Kind{}
)
// AccessPolicy returns the [kindsys.Interface] implementation for the accesspolicy kind.
func (b *Base) AccessPolicy() *accesspolicy.Kind {
return b.accesspolicy
}
// Dashboard returns the [kindsys.Interface] implementation for the dashboard kind.
func (b *Base) Dashboard() *dashboard.Kind {
return b.dashboard
}
// LibraryPanel returns the [kindsys.Interface] implementation for the librarypanel kind.
func (b *Base) LibraryPanel() *librarypanel.Kind {
return b.librarypanel
}
// Preferences returns the [kindsys.Interface] implementation for the preferences kind.
func (b *Base) Preferences() *preferences.Kind {
return b.preferences
}
// PublicDashboard returns the [kindsys.Interface] implementation for the publicdashboard kind.
func (b *Base) PublicDashboard() *publicdashboard.Kind {
return b.publicdashboard
}
// Role returns the [kindsys.Interface] implementation for the role kind.
func (b *Base) Role() *role.Kind {
return b.role
}
// RoleBinding returns the [kindsys.Interface] implementation for the rolebinding kind.
func (b *Base) RoleBinding() *rolebinding.Kind {
return b.rolebinding
}
// Team returns the [kindsys.Interface] implementation for the team kind.
func (b *Base) Team() *team.Kind {
return b.team
}
func doNewBase(rt *thema.Runtime) *Base {
var err error
reg := &Base{}
reg.accesspolicy, err = accesspolicy.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the accesspolicy Kind: %s", err))
}
reg.all = append(reg.all, reg.accesspolicy)
reg.dashboard, err = dashboard.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the dashboard Kind: %s", err))
}
reg.all = append(reg.all, reg.dashboard)
reg.librarypanel, err = librarypanel.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the librarypanel Kind: %s", err))
}
reg.all = append(reg.all, reg.librarypanel)
reg.preferences, err = preferences.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the preferences Kind: %s", err))
}
reg.all = append(reg.all, reg.preferences)
reg.publicdashboard, err = publicdashboard.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the publicdashboard Kind: %s", err))
}
reg.all = append(reg.all, reg.publicdashboard)
reg.role, err = role.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the role Kind: %s", err))
}
reg.all = append(reg.all, reg.role)
reg.rolebinding, err = rolebinding.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the rolebinding Kind: %s", err))
}
reg.all = append(reg.all, reg.rolebinding)
reg.team, err = team.NewKind(rt)
if err != nil {
panic(fmt.Sprintf("error while initializing the team Kind: %s", err))
}
reg.all = append(reg.all, reg.team)
return reg
}
+468
View File
@@ -0,0 +1,468 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// public/app/plugins/gen.go
// Using jennies:
// PluginRegistryJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package schemas
import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"runtime"
"testing/fstest"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
)
var cueImportsPath = filepath.Join("packages", "grafana-schema", "src", "common")
var importPath = "github.com/grafana/grafana/packages/grafana-schema/src/common"
type ComposableKind struct {
Name string
Filename string
CueFile cue.Value
}
func GetComposableKinds() ([]ComposableKind, error) {
kinds := make([]ComposableKind, 0)
_, caller, _, _ := runtime.Caller(0)
root := filepath.Join(caller, "../../../..")
azuremonitorCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/azuremonitor/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "azuremonitor",
Filename: "dataquery.cue",
CueFile: azuremonitorCue,
})
googlecloudmonitoringCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/cloud-monitoring/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "googlecloudmonitoring",
Filename: "dataquery.cue",
CueFile: googlecloudmonitoringCue,
})
cloudwatchCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/cloudwatch/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "cloudwatch",
Filename: "dataquery.cue",
CueFile: cloudwatchCue,
})
elasticsearchCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/elasticsearch/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "elasticsearch",
Filename: "dataquery.cue",
CueFile: elasticsearchCue,
})
grafanapyroscopeCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/grafana-pyroscope-datasource/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "grafanapyroscope",
Filename: "dataquery.cue",
CueFile: grafanapyroscopeCue,
})
grafanatestdatadatasourceCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/grafana-testdata-datasource/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "grafanatestdatadatasource",
Filename: "dataquery.cue",
CueFile: grafanatestdatadatasourceCue,
})
lokiCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/loki/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "loki",
Filename: "dataquery.cue",
CueFile: lokiCue,
})
parcaCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/parca/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "parca",
Filename: "dataquery.cue",
CueFile: parcaCue,
})
tempoCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/datasource/tempo/dataquery.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "tempo",
Filename: "dataquery.cue",
CueFile: tempoCue,
})
alertgroupsCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/alertGroups/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "alertgroups",
Filename: "panelcfg.cue",
CueFile: alertgroupsCue,
})
annotationslistCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/annolist/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "annotationslist",
Filename: "panelcfg.cue",
CueFile: annotationslistCue,
})
barchartCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/barchart/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "barchart",
Filename: "panelcfg.cue",
CueFile: barchartCue,
})
bargaugeCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/bargauge/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "bargauge",
Filename: "panelcfg.cue",
CueFile: bargaugeCue,
})
candlestickCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/candlestick/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "candlestick",
Filename: "panelcfg.cue",
CueFile: candlestickCue,
})
canvasCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/canvas/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "canvas",
Filename: "panelcfg.cue",
CueFile: canvasCue,
})
dashboardlistCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/dashlist/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "dashboardlist",
Filename: "panelcfg.cue",
CueFile: dashboardlistCue,
})
datagridCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/datagrid/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "datagrid",
Filename: "panelcfg.cue",
CueFile: datagridCue,
})
debugCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/debug/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "debug",
Filename: "panelcfg.cue",
CueFile: debugCue,
})
gaugeCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/gauge/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "gauge",
Filename: "panelcfg.cue",
CueFile: gaugeCue,
})
geomapCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/geomap/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "geomap",
Filename: "panelcfg.cue",
CueFile: geomapCue,
})
heatmapCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/heatmap/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "heatmap",
Filename: "panelcfg.cue",
CueFile: heatmapCue,
})
histogramCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/histogram/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "histogram",
Filename: "panelcfg.cue",
CueFile: histogramCue,
})
logsCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/logs/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "logs",
Filename: "panelcfg.cue",
CueFile: logsCue,
})
newsCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/news/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "news",
Filename: "panelcfg.cue",
CueFile: newsCue,
})
nodegraphCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/nodeGraph/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "nodegraph",
Filename: "panelcfg.cue",
CueFile: nodegraphCue,
})
piechartCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/piechart/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "piechart",
Filename: "panelcfg.cue",
CueFile: piechartCue,
})
statCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/stat/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "stat",
Filename: "panelcfg.cue",
CueFile: statCue,
})
statetimelineCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/state-timeline/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "statetimeline",
Filename: "panelcfg.cue",
CueFile: statetimelineCue,
})
statushistoryCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/status-history/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "statushistory",
Filename: "panelcfg.cue",
CueFile: statushistoryCue,
})
tableCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/table/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "table",
Filename: "panelcfg.cue",
CueFile: tableCue,
})
textCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/text/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "text",
Filename: "panelcfg.cue",
CueFile: textCue,
})
timeseriesCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/timeseries/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "timeseries",
Filename: "panelcfg.cue",
CueFile: timeseriesCue,
})
trendCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/trend/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "trend",
Filename: "panelcfg.cue",
CueFile: trendCue,
})
xychartCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/xychart/panelcfg.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, ComposableKind{
Name: "xychart",
Filename: "panelcfg.cue",
CueFile: xychartCue,
})
return kinds, nil
}
func loadCueFileWithCommon(root string, entrypoint string) (cue.Value, error) {
commonFS, err := mockCommonFS(root)
if err != nil {
fmt.Printf("cannot load common cue files: %s\n", err)
return cue.Value{}, err
}
overlay, err := buildOverlay(commonFS)
if err != nil {
fmt.Printf("Cannot build overlay: %s\n", err)
return cue.Value{}, err
}
bis := load.Instances([]string{entrypoint}, &load.Config{
ModuleRoot: "/",
Overlay: overlay,
})
values, err := cuecontext.New().BuildInstances(bis)
if err != nil {
fmt.Printf("Cannot build instance: %s\n", err)
return cue.Value{}, err
}
return values[0], nil
}
func mockCommonFS(root string) (fs.FS, error) {
path := filepath.Join(root, cueImportsPath)
dir, err := os.ReadDir(path)
if err != nil {
return nil, fmt.Errorf("cannot open common cue files directory: %s", err)
}
prefix := "cue.mod/pkg/" + importPath
commonFS := fstest.MapFS{}
for _, d := range dir {
if d.IsDir() {
continue
}
readPath := filepath.Join(path, d.Name())
b, err := os.ReadFile(filepath.Clean(readPath))
if err != nil {
return nil, err
}
commonFS[filepath.Join(prefix, d.Name())] = &fstest.MapFile{Data: b}
}
return commonFS, nil
}
// It loads common cue files into the schema to be able to make import works
func buildOverlay(commonFS fs.FS) (map[string]load.Source, error) {
overlay := make(map[string]load.Source)
err := fs.WalkDir(commonFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
f, err := commonFS.Open(path)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
b, err := io.ReadAll(f)
if err != nil {
return err
}
overlay[filepath.Join("/", path)] = load.FromBytes(b)
return nil
})
return overlay, err
}
+115
View File
@@ -0,0 +1,115 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// kinds/gen.go
// Using jennies:
// CoreRegistryJenny
//
// Run 'make gen-cue' from repository root to regenerate.
package schemas
import (
"os"
"path/filepath"
"runtime"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
)
type CoreKind struct {
Name string
CueFile cue.Value
}
func GetCoreKinds() ([]CoreKind, error) {
ctx := cuecontext.New()
kinds := make([]CoreKind, 0)
_, caller, _, _ := runtime.Caller(0)
root := filepath.Join(caller, "../../../..")
accesspolicyCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/accesspolicy/access_policy_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "accesspolicy",
CueFile: accesspolicyCue,
})
dashboardCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/dashboard/dashboard_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "dashboard",
CueFile: dashboardCue,
})
librarypanelCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/librarypanel/librarypanel_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "librarypanel",
CueFile: librarypanelCue,
})
preferencesCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/preferences/preferences_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "preferences",
CueFile: preferencesCue,
})
publicdashboardCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/publicdashboard/public_dashboard_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "publicdashboard",
CueFile: publicdashboardCue,
})
roleCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/role/role_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "role",
CueFile: roleCue,
})
rolebindingCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/rolebinding/role_binding_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "rolebinding",
CueFile: rolebindingCue,
})
teamCue, err := loadCueFile(ctx, filepath.Join(root, "./kinds/team/team_kind.cue"))
if err != nil {
return nil, err
}
kinds = append(kinds, CoreKind{
Name: "team",
CueFile: teamCue,
})
return kinds, nil
}
func loadCueFile(ctx *cue.Context, path string) (cue.Value, error) {
cueFile, err := os.ReadFile(path)
if err != nil {
return cue.Value{}, err
}
return ctx.CompileBytes(cueFile), nil
}