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
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
package receiver
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc ReceiverService,
|
||||
namespacer request.NamespaceMapper,
|
||||
opts builder.APIGroupOptions,
|
||||
metadata MetadataService,
|
||||
) (rest.Storage, error) {
|
||||
legacyStore := &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
metadata: metadata,
|
||||
}
|
||||
if opts.OptsGetter != nil && opts.DualWriteBuilder != nil {
|
||||
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, ResourceInfo, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opts.DualWriteBuilder(ResourceInfo.GroupResource(), legacyStore, store)
|
||||
}
|
||||
return legacyStore, nil
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"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"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
|
||||
resource "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/receiver"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/templategroup"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/timeinterval"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ builder.APIGroupBuilder = (*NotificationsAPIBuilder)(nil)
|
||||
|
||||
// This is used just so wire has something unique to return
|
||||
type NotificationsAPIBuilder struct {
|
||||
authz accesscontrol.AccessControl
|
||||
receiverAuth receiver.AccessControlService
|
||||
ng *ngalert.AlertNG
|
||||
namespacer request.NamespaceMapper
|
||||
}
|
||||
|
||||
func RegisterAPIService(
|
||||
features featuremgmt.FeatureToggles,
|
||||
apiregistration builder.APIRegistrar,
|
||||
cfg *setting.Cfg,
|
||||
ng *ngalert.AlertNG,
|
||||
) *NotificationsAPIBuilder {
|
||||
if ng.IsDisabled() {
|
||||
return nil
|
||||
}
|
||||
builder := &NotificationsAPIBuilder{
|
||||
ng: ng,
|
||||
namespacer: request.GetNamespaceMapper(cfg),
|
||||
authz: ng.Api.AccessControl,
|
||||
receiverAuth: ac.NewReceiverAccess[*ngmodels.Receiver](ng.Api.AccessControl, false),
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
return builder
|
||||
}
|
||||
|
||||
func (t *NotificationsAPIBuilder) GetGroupVersion() schema.GroupVersion {
|
||||
return resource.GroupVersion
|
||||
}
|
||||
|
||||
func (t *NotificationsAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
if err := receiver.AddKnownTypes(scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := routingtree.AddKnownTypes(scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := templategroup.AddKnownTypes(scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := timeinterval.AddKnownTypes(scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NotificationsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
|
||||
addStorage := func(gvr schema.GroupVersionResource, s rest.Storage) {
|
||||
v, ok := apiGroupInfo.VersionedResourcesStorageMap[gvr.Version]
|
||||
if !ok {
|
||||
v = map[string]rest.Storage{}
|
||||
apiGroupInfo.VersionedResourcesStorageMap[gvr.Version] = v
|
||||
}
|
||||
v[gvr.Resource] = s
|
||||
}
|
||||
|
||||
intervals, err := timeinterval.NewStorage(t.ng.Api.MuteTimings, t.namespacer, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize time-interval storage: %w", err)
|
||||
}
|
||||
addStorage(timeinterval.ResourceInfo.GroupVersionResource(), intervals)
|
||||
|
||||
recvStorage, err := receiver.NewStorage(t.ng.Api.ReceiverService, t.namespacer, opts, t.ng.Api.ReceiverService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize receiver storage: %w", err)
|
||||
}
|
||||
addStorage(receiver.ResourceInfo.GroupVersionResource(), recvStorage)
|
||||
|
||||
templ, err := templategroup.NewStorage(t.ng.Api.Templates, t.namespacer, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize templates group storage: %w", err)
|
||||
}
|
||||
addStorage(templategroup.ResourceInfo.GroupVersionResource(), templ)
|
||||
|
||||
routeStorage, err := routingtree.NewStorage(t.ng.Api.Policies, t.namespacer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize route storage: %w", err)
|
||||
}
|
||||
addStorage(routingtree.ResourceInfo.GroupVersionResource(), routeStorage)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NotificationsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return func(c common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
tmpl := templategroup.GetOpenAPIDefinitions(c)
|
||||
tin := timeinterval.GetOpenAPIDefinitions(c)
|
||||
recv := receiver.GetOpenAPIDefinitions(c)
|
||||
rest := routingtree.GetOpenAPIDefinitions(c)
|
||||
result := make(map[string]common.OpenAPIDefinition, len(tmpl)+len(tin)+len(recv)+len(rest))
|
||||
maps.Copy(result, tmpl)
|
||||
maps.Copy(result, tin)
|
||||
maps.Copy(result, recv)
|
||||
maps.Copy(result, rest)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// PostProcessOpenAPI is a hook to alter OpenAPI3 specification of the API server.
|
||||
func (t *NotificationsAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
||||
// The plugin description
|
||||
oas.Info.Description = "Grafana Alerting Notification resources"
|
||||
|
||||
// The root api URL
|
||||
root := "/apis/" + t.GetGroupVersion().String() + "/"
|
||||
|
||||
// Hide the ability to list or watch across all tenants
|
||||
delete(oas.Paths.Paths, root+receiver.ResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+timeinterval.ResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+templategroup.ResourceInfo.GroupResource().Resource)
|
||||
delete(oas.Paths.Paths, root+routingtree.ResourceInfo.GroupResource().Resource)
|
||||
|
||||
return oas, nil
|
||||
}
|
||||
|
||||
func (t *NotificationsAPIBuilder) GetAuthorizer() authorizer.Authorizer {
|
||||
return authorizer.AuthorizerFunc(
|
||||
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
switch a.GetResource() {
|
||||
case templategroup.ResourceInfo.GroupResource().Resource:
|
||||
return templategroup.Authorize(ctx, t.authz, a)
|
||||
case timeinterval.ResourceInfo.GroupResource().Resource:
|
||||
return timeinterval.Authorize(ctx, t.authz, a)
|
||||
case receiver.ResourceInfo.GroupResource().Resource:
|
||||
return receiver.Authorize(ctx, t.receiverAuth, a)
|
||||
case routingtree.ResourceInfo.GroupResource().Resource:
|
||||
return routingtree.Authorize(ctx, t.authz, a)
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
})
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package templategroup
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc TemplateService,
|
||||
namespacer request.NamespaceMapper,
|
||||
opts builder.APIGroupOptions,
|
||||
) (rest.Storage, error) {
|
||||
legacyStore := &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
}
|
||||
if opts.OptsGetter != nil && opts.DualWriteBuilder != nil {
|
||||
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, ResourceInfo, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opts.DualWriteBuilder(ResourceInfo.GroupResource(), legacyStore, store)
|
||||
}
|
||||
return legacyStore, nil
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package timeinterval
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc TimeIntervalService,
|
||||
namespacer request.NamespaceMapper,
|
||||
opts builder.APIGroupOptions,
|
||||
) (rest.Storage, error) {
|
||||
legacyStore := &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
}
|
||||
if opts.OptsGetter != nil && opts.DualWriteBuilder != nil {
|
||||
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, ResourceInfo, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opts.DualWriteBuilder(ResourceInfo.GroupResource(), legacyStore, store)
|
||||
}
|
||||
return legacyStore, nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package apiregistry
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
@@ -26,7 +25,6 @@ func ProvideRegistryServiceSink(
|
||||
_ *folders.FolderAPIBuilder,
|
||||
_ *iam.IdentityAccessManagementAPIBuilder,
|
||||
_ *query.QueryAPIBuilder,
|
||||
_ *notifications.NotificationsAPIBuilder,
|
||||
_ *userstorage.UserStorageAPIBuilder,
|
||||
_ *secret.SecretAPIBuilder,
|
||||
_ *provisioning.APIBuilder,
|
||||
|
||||
@@ -3,7 +3,6 @@ package apiregistry
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
@@ -36,7 +35,6 @@ var WireSet = wire.NewSet(
|
||||
provisioning.RegisterAPIService,
|
||||
service.RegisterAPIService,
|
||||
query.RegisterAPIService,
|
||||
notifications.RegisterAPIService,
|
||||
secret.RegisterAPIService,
|
||||
userstorage.RegisterAPIService,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package receiver
|
||||
|
||||
import (
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc ReceiverService,
|
||||
namespacer request.NamespaceMapper,
|
||||
metadata MetadataService,
|
||||
) grafanarest.Storage {
|
||||
return &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
metadata: metadata,
|
||||
}
|
||||
}
|
||||
-26
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
var kind = model.Kind()
|
||||
var GetOpenAPIDefinitions = model.GetOpenAPIDefinitions
|
||||
var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
kind.GroupVersionResource().Resource, strings.ToLower(kind.Kind()), kind.Kind(),
|
||||
func() runtime.Object { return kind.ZeroValue() },
|
||||
@@ -35,28 +34,3 @@ var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
func AddKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(ResourceInfo.GroupVersion(),
|
||||
&model.Receiver{},
|
||||
&model.ReceiverList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, ResourceInfo.GroupVersion())
|
||||
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
ResourceInfo.GroupVersionKind(),
|
||||
func(label, value string) (string, string, error) {
|
||||
fieldSet := model.SelectableFields(&model.Receiver{})
|
||||
for key := range fieldSet {
|
||||
if label == key {
|
||||
return label, value, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("field label not supported for %s: %s", ResourceInfo.GroupVersionKind(), label)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
|
||||
notificationsResource "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis"
|
||||
notificationsApp "github.com/grafana/grafana/apps/alerting/notifications/pkg/app"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/receiver"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/templategroup"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/timeinterval"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder/runner"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type AlertingNotificationsAppProvider struct {
|
||||
app.Provider
|
||||
}
|
||||
|
||||
func RegisterApp(
|
||||
cfg *setting.Cfg,
|
||||
ng *ngalert.AlertNG,
|
||||
) *AlertingNotificationsAppProvider {
|
||||
if ng.IsDisabled() {
|
||||
return nil
|
||||
}
|
||||
appCfg := &runner.AppBuilderConfig{
|
||||
Authorizer: getAuthorizer(ng.Api.AccessControl),
|
||||
LegacyStorageGetter: getLegacyStorage(request.GetNamespaceMapper(cfg), ng),
|
||||
OpenAPIDefGetter: notificationsResource.GetOpenAPIDefinitions,
|
||||
ManagedKinds: notificationsResource.GetKinds(),
|
||||
}
|
||||
|
||||
return &AlertingNotificationsAppProvider{
|
||||
Provider: simple.NewAppProvider(notificationsResource.LocalManifest(), appCfg, notificationsApp.New),
|
||||
}
|
||||
}
|
||||
|
||||
func getAuthorizer(authz accesscontrol.AccessControl) authorizer.Authorizer {
|
||||
return authorizer.AuthorizerFunc(
|
||||
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
switch a.GetResource() {
|
||||
case templategroup.ResourceInfo.GroupResource().Resource:
|
||||
return templategroup.Authorize(ctx, authz, a)
|
||||
case timeinterval.ResourceInfo.GroupResource().Resource:
|
||||
return timeinterval.Authorize(ctx, authz, a)
|
||||
case receiver.ResourceInfo.GroupResource().Resource:
|
||||
return receiver.Authorize(ctx, ac.NewReceiverAccess[*ngmodels.Receiver](authz, false), a)
|
||||
case routingtree.ResourceInfo.GroupResource().Resource:
|
||||
return routingtree.Authorize(ctx, authz, a)
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
})
|
||||
}
|
||||
|
||||
func getLegacyStorage(namespacer request.NamespaceMapper, ng *ngalert.AlertNG) runner.LegacyStorageGetter {
|
||||
return func(gvr schema.GroupVersionResource) grafanarest.Storage {
|
||||
if gvr == receiver.ResourceInfo.GroupVersionResource() {
|
||||
return receiver.NewStorage(ng.Api.ReceiverService, namespacer, ng.Api.ReceiverService)
|
||||
} else if gvr == timeinterval.ResourceInfo.GroupVersionResource() {
|
||||
return timeinterval.NewStorage(ng.Api.MuteTimings, namespacer)
|
||||
} else if gvr == templategroup.ResourceInfo.GroupVersionResource() {
|
||||
return templategroup.NewStorage(ng.Api.Templates, namespacer)
|
||||
} else if gvr == routingtree.ResourceInfo.GroupVersionResource() {
|
||||
return routingtree.NewStorage(ng.Api.Policies, namespacer)
|
||||
}
|
||||
panic("unknown legacy storage requested: " + gvr.String())
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -3,15 +3,14 @@ package routingtree
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(legacySvc RouteService, namespacer request.NamespaceMapper) (rest.Storage, error) {
|
||||
legacyStore := &legacyStorage{
|
||||
func NewStorage(legacySvc RouteService, namespacer request.NamespaceMapper) grafanarest.Storage {
|
||||
return &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: rest.NewDefaultTableConvertor(ResourceInfo.GroupResource()),
|
||||
}
|
||||
// TODO implement dual write for routes. This API is a special beast - the resource is singleton.
|
||||
return legacyStore, nil
|
||||
}
|
||||
-11
@@ -3,7 +3,6 @@ package routingtree
|
||||
import (
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
model "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1"
|
||||
@@ -11,19 +10,9 @@ import (
|
||||
)
|
||||
|
||||
var kind = model.Kind()
|
||||
var GetOpenAPIDefinitions = model.GetOpenAPIDefinitions
|
||||
var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
kind.GroupVersionResource().Resource, strings.ToLower(kind.Kind()), kind.Kind(),
|
||||
func() runtime.Object { return kind.ZeroValue() },
|
||||
func() runtime.Object { return kind.ZeroListValue() },
|
||||
utils.TableColumns{},
|
||||
)
|
||||
|
||||
func AddKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(ResourceInfo.GroupVersion(),
|
||||
&model.RoutingTree{},
|
||||
&model.RoutingTreeList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, ResourceInfo.GroupVersion())
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package templategroup
|
||||
|
||||
import (
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc TemplateService,
|
||||
namespacer request.NamespaceMapper,
|
||||
) grafanarest.Storage {
|
||||
return &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
}
|
||||
}
|
||||
-26
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
var kind = model.Kind()
|
||||
var GetOpenAPIDefinitions = model.GetOpenAPIDefinitions
|
||||
var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
kind.GroupVersionResource().Resource, strings.ToLower(kind.Kind()), kind.Kind(),
|
||||
func() runtime.Object { return kind.ZeroValue() },
|
||||
@@ -32,28 +31,3 @@ var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
func AddKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(ResourceInfo.GroupVersion(),
|
||||
&model.TemplateGroup{},
|
||||
&model.TemplateGroupList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, ResourceInfo.GroupVersion())
|
||||
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
ResourceInfo.GroupVersionKind(),
|
||||
func(label, value string) (string, string, error) {
|
||||
fieldSet := model.SelectableFields(&model.TemplateGroup{})
|
||||
for key := range fieldSet {
|
||||
if label == key {
|
||||
return label, value, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("field label not supported for %s: %s", ResourceInfo.GroupVersionKind(), label)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package timeinterval
|
||||
|
||||
import (
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
func NewStorage(
|
||||
legacySvc TimeIntervalService,
|
||||
namespacer request.NamespaceMapper,
|
||||
) grafanarest.Storage {
|
||||
return &legacyStorage{
|
||||
service: legacySvc,
|
||||
namespacer: namespacer,
|
||||
tableConverter: ResourceInfo.TableConverter(),
|
||||
}
|
||||
}
|
||||
-26
@@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
var kind = model.Kind()
|
||||
var GetOpenAPIDefinitions = model.GetOpenAPIDefinitions
|
||||
var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
kind.GroupVersionResource().Resource, strings.ToLower(kind.Kind()), kind.Kind(),
|
||||
func() runtime.Object { return kind.ZeroValue() },
|
||||
@@ -32,28 +31,3 @@ var ResourceInfo = utils.NewResourceInfo(kind.Group(), kind.Version(),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
func AddKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(ResourceInfo.GroupVersion(),
|
||||
&model.TimeInterval{},
|
||||
&model.TimeIntervalList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, ResourceInfo.GroupVersion())
|
||||
|
||||
err := scheme.AddFieldLabelConversionFunc(
|
||||
ResourceInfo.GroupVersionKind(),
|
||||
func(label, value string) (string, string, error) {
|
||||
fieldSet := model.SelectableFields(&model.TimeInterval{})
|
||||
for key := range fieldSet {
|
||||
if label == key {
|
||||
return label, value, nil
|
||||
}
|
||||
}
|
||||
return "", "", fmt.Errorf("field label not supported for %s: %s", ResourceInfo.GroupVersionKind(), label)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
@@ -15,7 +18,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder/runner"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -35,6 +37,7 @@ func ProvideRegistryServiceSink(
|
||||
playlistAppProvider *playlist.PlaylistAppProvider,
|
||||
investigationAppProvider *investigations.InvestigationsAppProvider,
|
||||
advisorAppProvider *advisor.AdvisorAppProvider,
|
||||
alertingNotificationsAppProvider *notifications.AlertingNotificationsAppProvider,
|
||||
grafanaCfg *setting.Cfg,
|
||||
) (*Service, error) {
|
||||
cfgWrapper := func(ctx context.Context) (*rest.Config, error) {
|
||||
@@ -62,6 +65,9 @@ func ProvideRegistryServiceSink(
|
||||
!slices.Contains(grafanaCfg.DisablePlugins, "grafana-advisor-app") {
|
||||
providers = append(providers, advisorAppProvider)
|
||||
}
|
||||
if alertingNotificationsAppProvider != nil {
|
||||
providers = append(providers, alertingNotificationsAppProvider)
|
||||
}
|
||||
apiGroupRunner, err = runner.NewAPIGroupRunner(cfg, providers...)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
)
|
||||
@@ -15,5 +16,6 @@ var WireSet = wire.NewSet(
|
||||
investigations.RegisterApp,
|
||||
advisor.RegisterApp,
|
||||
checkregistry.ProvideService,
|
||||
notifications.RegisterApp,
|
||||
wire.Bind(new(checkregistry.CheckService), new(*checkregistry.Service)),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
@@ -10,6 +12,7 @@ import (
|
||||
|
||||
"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"
|
||||
@@ -74,6 +77,29 @@ func (b *appBuilder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/receiver/v0alpha1"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/routingtree"
|
||||
|
||||
test_common "github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/routingtree/v0alpha1"
|
||||
v0alpha1_timeinterval "github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/routingtree"
|
||||
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/fakes"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/timeinterval/v0alpha1/fakes"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/timeinterval"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/routingtree"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/timeinterval"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"description": "Grafana Alerting Notification resources",
|
||||
"title": "notifications.alerting.grafana.app/v0alpha1"
|
||||
},
|
||||
"paths": {
|
||||
@@ -3434,6 +3433,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "Receiver",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "Receiver",
|
||||
@@ -3477,6 +3481,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "ReceiverList",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "ReceiverList",
|
||||
@@ -3715,6 +3724,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "RoutingTree",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "RoutingTree",
|
||||
@@ -3758,6 +3772,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "RoutingTreeList",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "RoutingTreeList",
|
||||
@@ -3961,6 +3980,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TemplateGroup",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TemplateGroup",
|
||||
@@ -4004,6 +4028,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TemplateGroupList",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TemplateGroupList",
|
||||
@@ -4179,6 +4208,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TimeInterval",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TimeInterval",
|
||||
@@ -4222,6 +4256,11 @@
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TimeIntervalList",
|
||||
"version": "__internal"
|
||||
},
|
||||
{
|
||||
"group": "notifications.alerting.grafana.app",
|
||||
"kind": "TimeIntervalList",
|
||||
|
||||
Reference in New Issue
Block a user