Files
Yuri Tseretyan 85344e30c0 Alerting: Migrate notifications API to app-platform SDK application (#104424)
* introduce alerting notification app
* move code as is and remove from old registry
* update api server registration
* update make file and remove unnecessary args, copy some useful make commands from dashboards
* update codeowners

* move constants inside module and remove dependency from grafana
* add support for selectors to the app builder
2025-04-30 10:23:56 -04:00

149 lines
4.6 KiB
Go

package runner
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"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
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,
}
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)
}
// 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
}
}
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
}