mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 08:42:18 +08:00
168 lines
5.6 KiB
Go
168 lines
5.6 KiB
Go
package runner
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
|
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
|
"k8s.io/apiserver/pkg/registry/rest"
|
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
|
"k8s.io/kube-openapi/pkg/common"
|
|
|
|
"github.com/grafana/grafana-app-sdk/app"
|
|
"github.com/grafana/grafana-app-sdk/resource"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
|
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
|
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
|
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
|
)
|
|
|
|
var _ AppBuilder = (*appBuilder)(nil)
|
|
|
|
type LegacyStorageGetter func(schema.GroupVersionResource) grafanarest.Storage
|
|
|
|
type AppBuilderConfig struct {
|
|
Authorizer authorizer.Authorizer
|
|
LegacyStorageGetter LegacyStorageGetter
|
|
OpenAPIDefGetter common.GetOpenAPIDefinitions
|
|
ManagedKinds map[schema.GroupVersion][]resource.Kind
|
|
CustomConfig any
|
|
// Do not set anything here unless you have special circumstances! This is a list of resources that are allowed to be accessed in v0alpha1,
|
|
// to prevent accidental exposure of experimental APIs. While developing, use the feature flag `grafanaAPIServerWithExperimentalAPIs`.
|
|
// And then, when you're ready to expose this to the end user, go to v1beta1 instead.
|
|
AllowedV0Alpha1Resources []string
|
|
|
|
groupVersion schema.GroupVersion
|
|
}
|
|
|
|
type AppBuilder interface {
|
|
builder.APIGroupBuilder
|
|
builder.APIGroupMutation
|
|
builder.APIGroupValidation
|
|
SetApp(app app.App)
|
|
}
|
|
|
|
type appBuilder struct {
|
|
app app.App
|
|
config AppBuilderConfig
|
|
}
|
|
|
|
func NewAppBuilder(appBuilderConfig AppBuilderConfig) (*appBuilder, error) {
|
|
return &appBuilder{
|
|
config: appBuilderConfig,
|
|
}, nil
|
|
}
|
|
|
|
func (b *appBuilder) SetApp(app app.App) {
|
|
b.app = app
|
|
}
|
|
|
|
// GetGroupVersion implements APIGroupBuilder.GetGroupVersion
|
|
func (b *appBuilder) GetGroupVersion() schema.GroupVersion {
|
|
return b.config.groupVersion
|
|
}
|
|
|
|
// InstallSchema implements APIGroupBuilder.InstallSchema
|
|
func (b *appBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
|
gv := b.GetGroupVersion()
|
|
for _, kinds := range b.config.ManagedKinds {
|
|
for _, kind := range kinds {
|
|
scheme.AddKnownTypeWithName(gv.WithKind(kind.Kind()), kind.ZeroValue())
|
|
scheme.AddKnownTypeWithName(gv.WithKind(kind.Kind()+"List"), kind.ZeroListValue())
|
|
|
|
// Link this group to the internal representation.
|
|
// This is used for server-side-apply (PATCH), and avoids the error:
|
|
// "no kind is registered for the type"
|
|
gvInternal := schema.GroupVersion{
|
|
Group: gv.Group,
|
|
Version: runtime.APIVersionInternal,
|
|
}
|
|
|
|
// only register internal kind once
|
|
if _, ok := scheme.KnownTypes(gvInternal)[kind.Kind()]; !ok {
|
|
scheme.AddKnownTypeWithName(gvInternal.WithKind(kind.Kind()), kind.ZeroValue())
|
|
scheme.AddKnownTypeWithName(gvInternal.WithKind(kind.Kind()+"List"), kind.ZeroListValue())
|
|
}
|
|
|
|
if len(kind.SelectableFields()) == 0 {
|
|
continue
|
|
}
|
|
gvk := gv.WithKind(kind.Kind())
|
|
err := scheme.AddFieldLabelConversionFunc(
|
|
gvk,
|
|
func(label, value string) (string, string, error) {
|
|
if label == "metadata.name" || label == "metadata.namespace" {
|
|
return label, value, nil
|
|
}
|
|
fields := kind.SelectableFields()
|
|
for _, field := range fields {
|
|
if field.FieldSelector == label {
|
|
return label, value, nil
|
|
}
|
|
}
|
|
return "", "", fmt.Errorf("field label not supported for %s: %s", gvk, label)
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return scheme.SetVersionPriority(gv)
|
|
}
|
|
|
|
// AllowedV0Alpha1Resources returns the list of resources that are allowed to be accessed in v0alpha1
|
|
func (b *appBuilder) AllowedV0Alpha1Resources() []string {
|
|
return b.config.AllowedV0Alpha1Resources
|
|
}
|
|
|
|
// UpdateAPIGroupInfo implements APIGroupBuilder.UpdateAPIGroupInfo
|
|
func (b *appBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
|
|
for _, kinds := range b.config.ManagedKinds {
|
|
for _, kind := range kinds {
|
|
version := kind.GroupVersionKind().Version
|
|
if _, ok := apiGroupInfo.VersionedResourcesStorageMap[version]; !ok {
|
|
apiGroupInfo.VersionedResourcesStorageMap[version] = make(map[string]rest.Storage)
|
|
}
|
|
resourceInfo := KindToResourceInfo(kind)
|
|
store, err := b.getStorage(resourceInfo, opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
apiGroupInfo.VersionedResourcesStorageMap[version][resourceInfo.StoragePath()] = store
|
|
if registryStore, ok := store.(*genericregistry.Store); ok {
|
|
for subPath := range kind.ZeroValue().GetSubresources() {
|
|
apiGroupInfo.VersionedResourcesStorageMap[version][resourceInfo.StoragePath(subPath)] = grafanaregistry.NewRegistryStatusStore(opts.Scheme, registryStore)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *appBuilder) getStorage(resourceInfo utils.ResourceInfo, opts builder.APIGroupOptions) (grafanarest.Storage, error) {
|
|
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b.config.LegacyStorageGetter != nil && opts.DualWriteBuilder != nil {
|
|
if legacyStorage := b.config.LegacyStorageGetter(resourceInfo.GroupVersionResource()); legacyStorage != nil {
|
|
return opts.DualWriteBuilder(resourceInfo.GroupResource(), legacyStorage, store)
|
|
}
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
// GetOpenAPIDefinitions implements APIGroupBuilder.GetOpenAPIDefinitions
|
|
func (b *appBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
|
return b.config.OpenAPIDefGetter
|
|
}
|
|
|
|
// GetAuthorizer implements APIGroupBuilder.GetAuthorizer
|
|
func (b *appBuilder) GetAuthorizer() authorizer.Authorizer {
|
|
return b.config.Authorizer
|
|
}
|