Chore: Remove CRD generation (#67286)
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/codejen"
|
||||
"github.com/grafana/kindsys"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
)
|
||||
|
||||
// CRDKindRegistryJenny generates a static registry of the CRD representations
|
||||
// of core Grafana kinds, layered on top of the publicly consumable generated
|
||||
// registry in pkg/corekinds.
|
||||
//
|
||||
// Path should be the relative path to the directory that will contain the
|
||||
// generated registry.
|
||||
func CRDKindRegistryJenny(path string) ManyToOne {
|
||||
return &crdregjenny{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
type crdregjenny struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (j *crdregjenny) JennyName() string {
|
||||
return "CRDKindRegistryJenny"
|
||||
}
|
||||
|
||||
func (j *crdregjenny) 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("core_crd_registry.tmpl").Execute(buf, tvars_kind_registry{
|
||||
PackageName: "corecrd",
|
||||
KindPackagePrefix: filepath.ToSlash(filepath.Join("github.com/grafana/grafana", cuectx.GoCoreKindParentPath)),
|
||||
Kinds: cores,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("failed executing core crd registry template: %w", err)
|
||||
}
|
||||
|
||||
b, err := postprocessGoFile(genGoFile{
|
||||
path: j.path,
|
||||
in: buf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return codejen.NewFile(filepath.Join(j.path, "registry_gen.go"), b, j), nil
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/codejen"
|
||||
"github.com/grafana/kindsys"
|
||||
)
|
||||
|
||||
// CRDTypesJenny generates the OpenAPI CRD representation for a core
|
||||
// structured kind that is expected by Kubernetes controller machinery.
|
||||
func CRDTypesJenny(path string) OneToOne {
|
||||
return crdTypesJenny{
|
||||
parentpath: path,
|
||||
}
|
||||
}
|
||||
|
||||
type crdTypesJenny struct {
|
||||
parentpath string
|
||||
}
|
||||
|
||||
func (j crdTypesJenny) JennyName() string {
|
||||
return "CRDTypesJenny"
|
||||
}
|
||||
|
||||
func (j crdTypesJenny) Generate(kind kindsys.Kind) (*codejen.File, error) {
|
||||
_, isCore := kind.(kindsys.Core)
|
||||
_, isCustom := kind.(kindsys.Core)
|
||||
if !(isCore || isCustom) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := tmpls.Lookup("core_crd_types.tmpl").Execute(buf, kind); err != nil {
|
||||
return nil, fmt.Errorf("failed executing crd types template: %w", err)
|
||||
}
|
||||
|
||||
name := kind.Props().Common().MachineName
|
||||
path := filepath.Join(j.parentpath, name, "crd", name+"_crd_gen.go")
|
||||
b, err := postprocessGoFile(genGoFile{
|
||||
path: path,
|
||||
in: buf.Bytes(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return codejen.NewFile(path, b, j), nil
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
package codegen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/encoding/openapi"
|
||||
cueyaml "cuelang.org/go/pkg/encoding/yaml"
|
||||
"github.com/grafana/codejen"
|
||||
"github.com/grafana/kindsys/k8ssys"
|
||||
"github.com/grafana/thema"
|
||||
goyaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/grafana/kindsys"
|
||||
)
|
||||
|
||||
// TODO this jenny is quite sloppy, having been quickly adapted from app-sdk. It needs love
|
||||
|
||||
// YamlCRDJenny generates a representation of a core structured kind in YAML CRD form.
|
||||
func YamlCRDJenny(path string) OneToOne {
|
||||
return yamlCRDJenny{
|
||||
parentpath: path,
|
||||
}
|
||||
}
|
||||
|
||||
type yamlCRDJenny struct {
|
||||
parentpath string
|
||||
}
|
||||
|
||||
func (yamlCRDJenny) JennyName() string {
|
||||
return "YamlCRDJenny"
|
||||
}
|
||||
|
||||
func (j yamlCRDJenny) Generate(k kindsys.Kind) (*codejen.File, error) {
|
||||
kind, is := k.(kindsys.Core)
|
||||
if !is {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
props := kind.Def().Properties
|
||||
lin := kind.Lineage()
|
||||
|
||||
// We need to go through every schema, as they all have to be defined in the CRD
|
||||
sch, err := lin.Schema(thema.SV(0, 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resource := customResourceDefinition{
|
||||
APIVersion: "apiextensions.k8s.io/v1",
|
||||
Kind: "CustomResourceDefinition",
|
||||
Metadata: customResourceDefinitionMetadata{
|
||||
Name: fmt.Sprintf("%s.%s", props.PluralMachineName, props.CRD.Group),
|
||||
},
|
||||
Spec: k8ssys.CustomResourceDefinitionSpec{
|
||||
Group: props.CRD.Group,
|
||||
Scope: props.CRD.Scope,
|
||||
Names: k8ssys.CustomResourceDefinitionSpecNames{
|
||||
Kind: props.Name,
|
||||
Plural: props.PluralMachineName,
|
||||
},
|
||||
Versions: make([]k8ssys.CustomResourceDefinitionSpecVersion, 0),
|
||||
},
|
||||
}
|
||||
latest := lin.Latest().Version()
|
||||
|
||||
for sch != nil {
|
||||
oapi, err := generateOpenAPI(sch, props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vstr := versionString(sch.Version())
|
||||
if props.Maturity.Less(kindsys.MaturityStable) {
|
||||
vstr = "v0-0alpha1"
|
||||
}
|
||||
|
||||
ver, err := valueToCRDSpecVersion(oapi, vstr, sch.Version() == latest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if props.CRD.DummySchema {
|
||||
ver.Schema = map[string]any{
|
||||
"openAPIV3Schema": map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"spec": map[string]any{
|
||||
"type": "object",
|
||||
"x-kubernetes-preserve-unknown-fields": true,
|
||||
},
|
||||
},
|
||||
"required": []any{
|
||||
"spec",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
resource.Spec.Versions = append(resource.Spec.Versions, ver)
|
||||
sch = sch.Successor()
|
||||
}
|
||||
contents, err := goyaml.Marshal(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if props.CRD.DummySchema {
|
||||
// Add a comment header for those with dummy schema
|
||||
b := new(bytes.Buffer)
|
||||
fmt.Fprintf(b, "# This CRD is generated with an empty schema body because Grafana's\n# code generators currently produce OpenAPI that Kubernetes will not\n# accept, despite being valid.\n\n%s", string(contents))
|
||||
contents = b.Bytes()
|
||||
}
|
||||
|
||||
return codejen.NewFile(filepath.Join(j.parentpath, props.MachineName, "crd", props.MachineName+".crd.yml"), contents, j), nil
|
||||
}
|
||||
|
||||
// customResourceDefinition differs from k8ssys.CustomResourceDefinition in that it doesn't use the metav1
|
||||
// TypeMeta and ObjectMeta, as those do not contain YAML tags and get improperly serialized to YAML.
|
||||
// Since we don't need to use it with the kubernetes go-client, we don't need the extra functionality attached.
|
||||
//
|
||||
//nolint:lll
|
||||
type customResourceDefinition struct {
|
||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
|
||||
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
|
||||
Metadata customResourceDefinitionMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec k8ssys.CustomResourceDefinitionSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type customResourceDefinitionMetadata struct {
|
||||
Name string `json:"name,omitempty" yaml:"name" protobuf:"bytes,1,opt,name=name"`
|
||||
// TODO: other fields as necessary for codegen
|
||||
}
|
||||
|
||||
type cueOpenAPIEncoded struct {
|
||||
Components cueOpenAPIEncodedComponents `json:"components"`
|
||||
}
|
||||
|
||||
type cueOpenAPIEncodedComponents struct {
|
||||
Schemas map[string]any `json:"schemas"`
|
||||
}
|
||||
|
||||
func valueToCRDSpecVersion(str string, name string, stored bool) (k8ssys.CustomResourceDefinitionSpecVersion, error) {
|
||||
// Decode the bytes back into an object where we can trim the openAPI clutter out
|
||||
// and grab just the schema as a map[string]any (which is what k8s wants)
|
||||
back := cueOpenAPIEncoded{}
|
||||
err := goyaml.Unmarshal([]byte(str), &back)
|
||||
if err != nil {
|
||||
return k8ssys.CustomResourceDefinitionSpecVersion{}, err
|
||||
}
|
||||
if len(back.Components.Schemas) != 1 {
|
||||
// There should only be one schema here...
|
||||
// TODO: this may change with subresources--but subresources should have defined names
|
||||
return k8ssys.CustomResourceDefinitionSpecVersion{}, fmt.Errorf("version %s has multiple schemas", name)
|
||||
}
|
||||
var def map[string]any
|
||||
for _, v := range back.Components.Schemas {
|
||||
ok := false
|
||||
def, ok = v.(map[string]any)
|
||||
if !ok {
|
||||
return k8ssys.CustomResourceDefinitionSpecVersion{},
|
||||
fmt.Errorf("error generating openapi schema - generated schema has invalid type")
|
||||
}
|
||||
}
|
||||
|
||||
return k8ssys.CustomResourceDefinitionSpecVersion{
|
||||
Name: name,
|
||||
Served: true,
|
||||
Storage: stored,
|
||||
Schema: map[string]any{
|
||||
"openAPIV3Schema": map[string]any{
|
||||
"properties": map[string]any{
|
||||
"spec": def,
|
||||
},
|
||||
"required": []any{
|
||||
"spec",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func versionString(version thema.SyntacticVersion) string {
|
||||
return fmt.Sprintf("v%d-%d", version[0], version[1])
|
||||
}
|
||||
|
||||
// Hoisting this out of thema until we resolve the proper approach there
|
||||
func generateOpenAPI(sch thema.Schema, props kindsys.CoreProperties) (string, error) {
|
||||
ctx := sch.Underlying().Context()
|
||||
v := ctx.CompileString(fmt.Sprintf("#%s: _", props.Name))
|
||||
defpath := cue.MakePath(cue.Def(props.Name))
|
||||
defsch := v.FillPath(defpath, sch.Underlying())
|
||||
|
||||
cfg := &openapi.Config{
|
||||
NameFunc: func(v cue.Value, path cue.Path) string {
|
||||
if path.String() == defpath.String() {
|
||||
return props.Name
|
||||
}
|
||||
return ""
|
||||
},
|
||||
Info: ast.NewStruct( // doesn't matter, we're throwing it away
|
||||
"title", ast.NewString(props.Name),
|
||||
"version", ast.NewString("0.0"),
|
||||
),
|
||||
}
|
||||
|
||||
f, err := openapi.Generate(defsch, cfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cueyaml.Marshal(sch.Lineage().Runtime().Context().BuildFile(f))
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package {{ .PackageName }}
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
{{range .Kinds }}
|
||||
{{ .Props.MachineName }} "{{ $.KindPackagePrefix }}/{{ .Props.MachineName }}/crd"{{end}}
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/grafana/kindsys/k8ssys"
|
||||
"github.com/grafana/grafana/pkg/registry/corekind"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Registry is a list of all of Grafana's core structured kinds, wrapped in a
|
||||
// standard [k8ssys.CRD] interface that makes them usable for interactions
|
||||
// with certain Kubernetes controller and apimachinery libraries.
|
||||
//
|
||||
// There are two access methods: individually via literal named methods, or as
|
||||
// a slice returned from All() method.
|
||||
//
|
||||
// Prefer the individual named methods for use cases where the particular kind(s)
|
||||
// that are needed are known to the caller. Prefer All() when performing operations
|
||||
// generically across all kinds.
|
||||
type Registry struct {
|
||||
all [{{ len .Kinds }}]k8ssys.Kind
|
||||
}
|
||||
|
||||
{{range $i, $k := .Kinds }}
|
||||
// {{ .Props.Name }} returns the [k8ssys.Kind] instance for the {{ .Props.Name }} kind.
|
||||
func (r *Registry) {{ .Props.Name }}() k8ssys.Kind {
|
||||
return r.all[{{ $i }}]
|
||||
}
|
||||
{{end}}
|
||||
|
||||
func doNewRegistry(breg *corekind.Base) *Registry {
|
||||
var err error
|
||||
var b []byte
|
||||
var kk k8ssys.Kind
|
||||
reg := &Registry{}
|
||||
|
||||
{{range $i, $k := .Kinds }}
|
||||
kk = k8ssys.Kind{
|
||||
GrafanaKind: breg.{{ $k.Props.Name }}(),
|
||||
Object: &{{ $k.Props.MachineName }}.{{ $k.Props.Name }}{},
|
||||
ObjectList: &{{ $k.Props.MachineName }}.{{ $k.Props.Name }}List{},
|
||||
}
|
||||
// TODO Having the committed form on disk in YAML is worth doing this for now...but fix this silliness
|
||||
map{{ $i }} := make(map[string]any)
|
||||
err = yaml.Unmarshal({{ $k.Props.MachineName }}.CRDYaml, map{{ $i }})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("generated CRD YAML for {{ $k.Props.Name }} failed to unmarshal: %s", err))
|
||||
}
|
||||
b, err = json.Marshal(map{{ $i }})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not re-marshal CRD JSON for {{ $k.Props.Name }}: %s", err))
|
||||
}
|
||||
err = json.Unmarshal(b, &kk.Schema)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not unmarshal CRD JSON for {{ $k.Props.Name }}: %s", err))
|
||||
}
|
||||
reg.all[{{ $i }}] = kk
|
||||
{{end}}
|
||||
|
||||
return reg
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package crd
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/{{ .Props.MachineName }}"
|
||||
"github.com/grafana/kindsys/k8ssys"
|
||||
)
|
||||
|
||||
// The CRD YAML representation of the {{ .Props.Name }} kind.
|
||||
//
|
||||
//go:embed {{ .Props.MachineName }}.crd.yml
|
||||
var CRDYaml []byte
|
||||
|
||||
// {{ .Props.Name }} is the Go CRD representation of a single {{ .Props.Name }} object.
|
||||
// It implements [runtime.Object], and is used in k8s scheme construction.
|
||||
type {{ .Props.Name }} struct {
|
||||
k8ssys.Base[{{ .Props.MachineName }}.{{ .Props.Name }}]
|
||||
}
|
||||
|
||||
// {{ .Props.Name }}List is the Go CRD representation of a list {{ .Props.Name }} objects.
|
||||
// It implements [runtime.Object], and is used in k8s scheme construction.
|
||||
type {{ .Props.Name }}List struct {
|
||||
k8ssys.ListBase[{{ .Props.MachineName }}.{{ .Props.Name }}]
|
||||
}
|
||||
Reference in New Issue
Block a user