mirror of https://github.com/grafana/grafana
commit
4d49cc3118
@ -0,0 +1,32 @@ |
||||
package core |
||||
|
||||
receiver: { |
||||
kind: "Receiver" |
||||
group: "notifications" |
||||
apiResource: { |
||||
groupOverride: "notifications.alerting.grafana.app" |
||||
} |
||||
codegen: { |
||||
frontend: false |
||||
backend: true |
||||
} |
||||
pluralName: "Receivers" |
||||
current: "v0alpha1" |
||||
versions: { |
||||
"v0alpha1": { |
||||
schema: { |
||||
#Integration: { |
||||
uid?: string |
||||
type: string |
||||
disableResolveMessage?: bool |
||||
settings: bytes |
||||
secureFields?: [string]: bool |
||||
} |
||||
spec: { |
||||
title: string |
||||
integrations : [...#Integration] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
import { CurrentUserDTO, WithAccessControlMetadata } from '../types'; |
||||
|
||||
export interface CurrentUser extends Omit<CurrentUserDTO, 'lightTheme'> {} |
||||
|
||||
export function userHasPermission(action: string, user: CurrentUser): boolean { |
||||
return !!user.permissions?.[action]; |
||||
} |
||||
|
||||
export function userHasPermissionInMetadata(action: string, object: WithAccessControlMetadata): boolean { |
||||
return !!object.accessControl?.[action]; |
||||
} |
||||
|
||||
export function userHasAllPermissions(actions: string[], user: CurrentUser) { |
||||
return actions.every((action) => userHasPermission(action, user)); |
||||
} |
||||
|
||||
export function userHasAnyPermission(actions: string[], user: CurrentUser) { |
||||
return actions.some((action) => userHasPermission(action, user)); |
||||
} |
@ -0,0 +1,29 @@ |
||||
import { CurrentUser } from '@grafana/data'; |
||||
|
||||
let singletonInstance: CurrentUser | null = null; |
||||
|
||||
/** |
||||
* Used during startup by Grafana to set the current user so it is available |
||||
* for rbac checks. |
||||
* |
||||
* @internal |
||||
*/ |
||||
export function setCurrentUser(instance: CurrentUser) { |
||||
if (singletonInstance) { |
||||
throw new Error('User should only be set once, when Grafana is starting.'); |
||||
} |
||||
singletonInstance = instance; |
||||
} |
||||
|
||||
/** |
||||
* Used to retrieve the current user. |
||||
* |
||||
* @internal |
||||
* |
||||
*/ |
||||
export function getCurrentUser(): CurrentUser { |
||||
if (!singletonInstance) { |
||||
throw new Error('User can only be used after Grafana instance has started.'); |
||||
} |
||||
return singletonInstance; |
||||
} |
@ -0,0 +1,18 @@ |
||||
import { |
||||
userHasPermission, |
||||
userHasPermissionInMetadata, |
||||
userHasAllPermissions, |
||||
userHasAnyPermission, |
||||
WithAccessControlMetadata, |
||||
} from '@grafana/data'; |
||||
|
||||
import { getCurrentUser } from '../services/user'; |
||||
|
||||
export const hasPermission = (action: string) => userHasPermission(action, getCurrentUser()); |
||||
|
||||
export const hasPermissionInMetadata = (action: string, object: WithAccessControlMetadata) => |
||||
userHasPermissionInMetadata(action, object); |
||||
|
||||
export const hasAllPermissions = (actions: string[]) => userHasAllPermissions(actions, getCurrentUser()); |
||||
|
||||
export const hasAnyPermission = (actions: string[]) => userHasAnyPermission(actions, getCurrentUser()); |
@ -0,0 +1,21 @@ |
||||
package v0alpha1 |
||||
|
||||
// Integration defines model for Integration.
|
||||
// +k8s:openapi-gen=true
|
||||
type Integration struct { |
||||
DisableResolveMessage *bool `json:"disableResolveMessage,omitempty"` |
||||
// +mapType=atomic
|
||||
SecureFields map[string]bool `json:"SecureFields,omitempty"` |
||||
// +listType=atomic
|
||||
Settings []byte `json:"settings"` |
||||
Type string `json:"type"` |
||||
Uid *string `json:"uid,omitempty"` |
||||
} |
||||
|
||||
// ReceiverSpec defines model for Spec.
|
||||
// +k8s:openapi-gen=true
|
||||
type ReceiverSpec struct { |
||||
// +listType=atomic
|
||||
Integrations []Integration `json:"integrations"` |
||||
Title string `json:"title"` |
||||
} |
@ -0,0 +1,69 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// IntegrationApplyConfiguration represents an declarative configuration of the Integration type for use
|
||||
// with apply.
|
||||
type IntegrationApplyConfiguration struct { |
||||
DisableResolveMessage *bool `json:"disableResolveMessage,omitempty"` |
||||
SecureFields map[string]bool `json:"SecureFields,omitempty"` |
||||
Settings []byte `json:"settings,omitempty"` |
||||
Type *string `json:"type,omitempty"` |
||||
Uid *string `json:"uid,omitempty"` |
||||
} |
||||
|
||||
// IntegrationApplyConfiguration constructs an declarative configuration of the Integration type for use with
|
||||
// apply.
|
||||
func Integration() *IntegrationApplyConfiguration { |
||||
return &IntegrationApplyConfiguration{} |
||||
} |
||||
|
||||
// WithDisableResolveMessage sets the DisableResolveMessage field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the DisableResolveMessage field is set to the value of the last call.
|
||||
func (b *IntegrationApplyConfiguration) WithDisableResolveMessage(value bool) *IntegrationApplyConfiguration { |
||||
b.DisableResolveMessage = &value |
||||
return b |
||||
} |
||||
|
||||
// WithSecureFields puts the entries into the SecureFields field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, the entries provided by each call will be put on the SecureFields field,
|
||||
// overwriting an existing map entries in SecureFields field with the same key.
|
||||
func (b *IntegrationApplyConfiguration) WithSecureFields(entries map[string]bool) *IntegrationApplyConfiguration { |
||||
if b.SecureFields == nil && len(entries) > 0 { |
||||
b.SecureFields = make(map[string]bool, len(entries)) |
||||
} |
||||
for k, v := range entries { |
||||
b.SecureFields[k] = v |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithSettings adds the given value to the Settings field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Settings field.
|
||||
func (b *IntegrationApplyConfiguration) WithSettings(values ...byte) *IntegrationApplyConfiguration { |
||||
for i := range values { |
||||
b.Settings = append(b.Settings, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithType sets the Type field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Type field is set to the value of the last call.
|
||||
func (b *IntegrationApplyConfiguration) WithType(value string) *IntegrationApplyConfiguration { |
||||
b.Type = &value |
||||
return b |
||||
} |
||||
|
||||
// WithUid sets the Uid field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Uid field is set to the value of the last call.
|
||||
func (b *IntegrationApplyConfiguration) WithUid(value string) *IntegrationApplyConfiguration { |
||||
b.Uid = &value |
||||
return b |
||||
} |
@ -0,0 +1,196 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
types "k8s.io/apimachinery/pkg/types" |
||||
v1 "k8s.io/client-go/applyconfigurations/meta/v1" |
||||
) |
||||
|
||||
// ReceiverApplyConfiguration represents an declarative configuration of the Receiver type for use
|
||||
// with apply.
|
||||
type ReceiverApplyConfiguration struct { |
||||
v1.TypeMetaApplyConfiguration `json:",inline"` |
||||
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` |
||||
Spec *ReceiverSpecApplyConfiguration `json:"spec,omitempty"` |
||||
} |
||||
|
||||
// Receiver constructs an declarative configuration of the Receiver type for use with
|
||||
// apply.
|
||||
func Receiver(name, namespace string) *ReceiverApplyConfiguration { |
||||
b := &ReceiverApplyConfiguration{} |
||||
b.WithName(name) |
||||
b.WithNamespace(namespace) |
||||
b.WithKind("Receiver") |
||||
b.WithAPIVersion("notifications.alerting.grafana.app/v0alpha1") |
||||
return b |
||||
} |
||||
|
||||
// WithKind sets the Kind field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Kind field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithKind(value string) *ReceiverApplyConfiguration { |
||||
b.Kind = &value |
||||
return b |
||||
} |
||||
|
||||
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the APIVersion field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithAPIVersion(value string) *ReceiverApplyConfiguration { |
||||
b.APIVersion = &value |
||||
return b |
||||
} |
||||
|
||||
// WithName sets the Name field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Name field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithName(value string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.Name = &value |
||||
return b |
||||
} |
||||
|
||||
// WithGenerateName sets the GenerateName field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the GenerateName field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithGenerateName(value string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.GenerateName = &value |
||||
return b |
||||
} |
||||
|
||||
// WithNamespace sets the Namespace field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Namespace field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithNamespace(value string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.Namespace = &value |
||||
return b |
||||
} |
||||
|
||||
// WithUID sets the UID field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the UID field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithUID(value types.UID) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.UID = &value |
||||
return b |
||||
} |
||||
|
||||
// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the ResourceVersion field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithResourceVersion(value string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.ResourceVersion = &value |
||||
return b |
||||
} |
||||
|
||||
// WithGeneration sets the Generation field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Generation field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithGeneration(value int64) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.Generation = &value |
||||
return b |
||||
} |
||||
|
||||
// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CreationTimestamp field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithCreationTimestamp(value metav1.Time) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.CreationTimestamp = &value |
||||
return b |
||||
} |
||||
|
||||
// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the DeletionTimestamp field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.DeletionTimestamp = &value |
||||
return b |
||||
} |
||||
|
||||
// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
b.DeletionGracePeriodSeconds = &value |
||||
return b |
||||
} |
||||
|
||||
// WithLabels puts the entries into the Labels field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, the entries provided by each call will be put on the Labels field,
|
||||
// overwriting an existing map entries in Labels field with the same key.
|
||||
func (b *ReceiverApplyConfiguration) WithLabels(entries map[string]string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
if b.Labels == nil && len(entries) > 0 { |
||||
b.Labels = make(map[string]string, len(entries)) |
||||
} |
||||
for k, v := range entries { |
||||
b.Labels[k] = v |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithAnnotations puts the entries into the Annotations field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, the entries provided by each call will be put on the Annotations field,
|
||||
// overwriting an existing map entries in Annotations field with the same key.
|
||||
func (b *ReceiverApplyConfiguration) WithAnnotations(entries map[string]string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
if b.Annotations == nil && len(entries) > 0 { |
||||
b.Annotations = make(map[string]string, len(entries)) |
||||
} |
||||
for k, v := range entries { |
||||
b.Annotations[k] = v |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the OwnerReferences field.
|
||||
func (b *ReceiverApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
for i := range values { |
||||
if values[i] == nil { |
||||
panic("nil value passed to WithOwnerReferences") |
||||
} |
||||
b.OwnerReferences = append(b.OwnerReferences, *values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithFinalizers adds the given value to the Finalizers field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Finalizers field.
|
||||
func (b *ReceiverApplyConfiguration) WithFinalizers(values ...string) *ReceiverApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
for i := range values { |
||||
b.Finalizers = append(b.Finalizers, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
func (b *ReceiverApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { |
||||
if b.ObjectMetaApplyConfiguration == nil { |
||||
b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} |
||||
} |
||||
} |
||||
|
||||
// WithSpec sets the Spec field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Spec field is set to the value of the last call.
|
||||
func (b *ReceiverApplyConfiguration) WithSpec(value *ReceiverSpecApplyConfiguration) *ReceiverApplyConfiguration { |
||||
b.Spec = value |
||||
return b |
||||
} |
@ -0,0 +1,39 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// ReceiverSpecApplyConfiguration represents an declarative configuration of the ReceiverSpec type for use
|
||||
// with apply.
|
||||
type ReceiverSpecApplyConfiguration struct { |
||||
Integrations []IntegrationApplyConfiguration `json:"integrations,omitempty"` |
||||
Title *string `json:"title,omitempty"` |
||||
} |
||||
|
||||
// ReceiverSpecApplyConfiguration constructs an declarative configuration of the ReceiverSpec type for use with
|
||||
// apply.
|
||||
func ReceiverSpec() *ReceiverSpecApplyConfiguration { |
||||
return &ReceiverSpecApplyConfiguration{} |
||||
} |
||||
|
||||
// WithIntegrations adds the given value to the Integrations field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Integrations field.
|
||||
func (b *ReceiverSpecApplyConfiguration) WithIntegrations(values ...*IntegrationApplyConfiguration) *ReceiverSpecApplyConfiguration { |
||||
for i := range values { |
||||
if values[i] == nil { |
||||
panic("nil value passed to WithIntegrations") |
||||
} |
||||
b.Integrations = append(b.Integrations, *values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithTitle sets the Title field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Title field is set to the value of the last call.
|
||||
func (b *ReceiverSpecApplyConfiguration) WithTitle(value string) *ReceiverSpecApplyConfiguration { |
||||
b.Title = &value |
||||
return b |
||||
} |
@ -0,0 +1,140 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake |
||||
|
||||
import ( |
||||
"context" |
||||
json "encoding/json" |
||||
"fmt" |
||||
|
||||
v0alpha1 "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
alertingnotificationsv0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/alerting_notifications/v0alpha1" |
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
labels "k8s.io/apimachinery/pkg/labels" |
||||
types "k8s.io/apimachinery/pkg/types" |
||||
watch "k8s.io/apimachinery/pkg/watch" |
||||
testing "k8s.io/client-go/testing" |
||||
) |
||||
|
||||
// FakeReceivers implements ReceiverInterface
|
||||
type FakeReceivers struct { |
||||
Fake *FakeNotificationsV0alpha1 |
||||
ns string |
||||
} |
||||
|
||||
var receiversResource = v0alpha1.SchemeGroupVersion.WithResource("receivers") |
||||
|
||||
var receiversKind = v0alpha1.SchemeGroupVersion.WithKind("Receiver") |
||||
|
||||
// Get takes name of the receiver, and returns the corresponding receiver object, and an error if there is any.
|
||||
func (c *FakeReceivers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v0alpha1.Receiver, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewGetAction(receiversResource, c.ns, name), &v0alpha1.Receiver{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.Receiver), err |
||||
} |
||||
|
||||
// List takes label and field selectors, and returns the list of Receivers that match those selectors.
|
||||
func (c *FakeReceivers) List(ctx context.Context, opts v1.ListOptions) (result *v0alpha1.ReceiverList, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewListAction(receiversResource, receiversKind, c.ns, opts), &v0alpha1.ReceiverList{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts) |
||||
if label == nil { |
||||
label = labels.Everything() |
||||
} |
||||
list := &v0alpha1.ReceiverList{ListMeta: obj.(*v0alpha1.ReceiverList).ListMeta} |
||||
for _, item := range obj.(*v0alpha1.ReceiverList).Items { |
||||
if label.Matches(labels.Set(item.Labels)) { |
||||
list.Items = append(list.Items, item) |
||||
} |
||||
} |
||||
return list, err |
||||
} |
||||
|
||||
// Watch returns a watch.Interface that watches the requested receivers.
|
||||
func (c *FakeReceivers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { |
||||
return c.Fake. |
||||
InvokesWatch(testing.NewWatchAction(receiversResource, c.ns, opts)) |
||||
|
||||
} |
||||
|
||||
// Create takes the representation of a receiver and creates it. Returns the server's representation of the receiver, and an error, if there is any.
|
||||
func (c *FakeReceivers) Create(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.CreateOptions) (result *v0alpha1.Receiver, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewCreateAction(receiversResource, c.ns, receiver), &v0alpha1.Receiver{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.Receiver), err |
||||
} |
||||
|
||||
// Update takes the representation of a receiver and updates it. Returns the server's representation of the receiver, and an error, if there is any.
|
||||
func (c *FakeReceivers) Update(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.UpdateOptions) (result *v0alpha1.Receiver, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewUpdateAction(receiversResource, c.ns, receiver), &v0alpha1.Receiver{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.Receiver), err |
||||
} |
||||
|
||||
// Delete takes name of the receiver and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeReceivers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { |
||||
_, err := c.Fake. |
||||
Invokes(testing.NewDeleteActionWithOptions(receiversResource, c.ns, name, opts), &v0alpha1.Receiver{}) |
||||
|
||||
return err |
||||
} |
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeReceivers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { |
||||
action := testing.NewDeleteCollectionAction(receiversResource, c.ns, listOpts) |
||||
|
||||
_, err := c.Fake.Invokes(action, &v0alpha1.ReceiverList{}) |
||||
return err |
||||
} |
||||
|
||||
// Patch applies the patch and returns the patched receiver.
|
||||
func (c *FakeReceivers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.Receiver, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceAction(receiversResource, c.ns, name, pt, data, subresources...), &v0alpha1.Receiver{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.Receiver), err |
||||
} |
||||
|
||||
// Apply takes the given apply declarative configuration, applies it and returns the applied receiver.
|
||||
func (c *FakeReceivers) Apply(ctx context.Context, receiver *alertingnotificationsv0alpha1.ReceiverApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.Receiver, err error) { |
||||
if receiver == nil { |
||||
return nil, fmt.Errorf("receiver provided to Apply must not be nil") |
||||
} |
||||
data, err := json.Marshal(receiver) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
name := receiver.Name |
||||
if name == nil { |
||||
return nil, fmt.Errorf("receiver.Name must be provided to Apply") |
||||
} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceAction(receiversResource, c.ns, *name, types.ApplyPatchType, data), &v0alpha1.Receiver{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.Receiver), err |
||||
} |
@ -0,0 +1,194 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"context" |
||||
json "encoding/json" |
||||
"fmt" |
||||
"time" |
||||
|
||||
v0alpha1 "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
alertingnotificationsv0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/alerting_notifications/v0alpha1" |
||||
scheme "github.com/grafana/grafana/pkg/generated/clientset/versioned/scheme" |
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
types "k8s.io/apimachinery/pkg/types" |
||||
watch "k8s.io/apimachinery/pkg/watch" |
||||
rest "k8s.io/client-go/rest" |
||||
) |
||||
|
||||
// ReceiversGetter has a method to return a ReceiverInterface.
|
||||
// A group's client should implement this interface.
|
||||
type ReceiversGetter interface { |
||||
Receivers(namespace string) ReceiverInterface |
||||
} |
||||
|
||||
// ReceiverInterface has methods to work with Receiver resources.
|
||||
type ReceiverInterface interface { |
||||
Create(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.CreateOptions) (*v0alpha1.Receiver, error) |
||||
Update(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.UpdateOptions) (*v0alpha1.Receiver, error) |
||||
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error |
||||
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error |
||||
Get(ctx context.Context, name string, opts v1.GetOptions) (*v0alpha1.Receiver, error) |
||||
List(ctx context.Context, opts v1.ListOptions) (*v0alpha1.ReceiverList, error) |
||||
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) |
||||
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.Receiver, err error) |
||||
Apply(ctx context.Context, receiver *alertingnotificationsv0alpha1.ReceiverApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.Receiver, err error) |
||||
ReceiverExpansion |
||||
} |
||||
|
||||
// receivers implements ReceiverInterface
|
||||
type receivers struct { |
||||
client rest.Interface |
||||
ns string |
||||
} |
||||
|
||||
// newReceivers returns a Receivers
|
||||
func newReceivers(c *NotificationsV0alpha1Client, namespace string) *receivers { |
||||
return &receivers{ |
||||
client: c.RESTClient(), |
||||
ns: namespace, |
||||
} |
||||
} |
||||
|
||||
// Get takes name of the receiver, and returns the corresponding receiver object, and an error if there is any.
|
||||
func (c *receivers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v0alpha1.Receiver, err error) { |
||||
result = &v0alpha1.Receiver{} |
||||
err = c.client.Get(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
Name(name). |
||||
VersionedParams(&options, scheme.ParameterCodec). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// List takes label and field selectors, and returns the list of Receivers that match those selectors.
|
||||
func (c *receivers) List(ctx context.Context, opts v1.ListOptions) (result *v0alpha1.ReceiverList, err error) { |
||||
var timeout time.Duration |
||||
if opts.TimeoutSeconds != nil { |
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second |
||||
} |
||||
result = &v0alpha1.ReceiverList{} |
||||
err = c.client.Get(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Watch returns a watch.Interface that watches the requested receivers.
|
||||
func (c *receivers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { |
||||
var timeout time.Duration |
||||
if opts.TimeoutSeconds != nil { |
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second |
||||
} |
||||
opts.Watch = true |
||||
return c.client.Get(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Watch(ctx) |
||||
} |
||||
|
||||
// Create takes the representation of a receiver and creates it. Returns the server's representation of the receiver, and an error, if there is any.
|
||||
func (c *receivers) Create(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.CreateOptions) (result *v0alpha1.Receiver, err error) { |
||||
result = &v0alpha1.Receiver{} |
||||
err = c.client.Post(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Body(receiver). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Update takes the representation of a receiver and updates it. Returns the server's representation of the receiver, and an error, if there is any.
|
||||
func (c *receivers) Update(ctx context.Context, receiver *v0alpha1.Receiver, opts v1.UpdateOptions) (result *v0alpha1.Receiver, err error) { |
||||
result = &v0alpha1.Receiver{} |
||||
err = c.client.Put(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
Name(receiver.Name). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Body(receiver). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Delete takes name of the receiver and deletes it. Returns an error if one occurs.
|
||||
func (c *receivers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { |
||||
return c.client.Delete(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
Name(name). |
||||
Body(&opts). |
||||
Do(ctx). |
||||
Error() |
||||
} |
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *receivers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { |
||||
var timeout time.Duration |
||||
if listOpts.TimeoutSeconds != nil { |
||||
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second |
||||
} |
||||
return c.client.Delete(). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
VersionedParams(&listOpts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Body(&opts). |
||||
Do(ctx). |
||||
Error() |
||||
} |
||||
|
||||
// Patch applies the patch and returns the patched receiver.
|
||||
func (c *receivers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.Receiver, err error) { |
||||
result = &v0alpha1.Receiver{} |
||||
err = c.client.Patch(pt). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
Name(name). |
||||
SubResource(subresources...). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Body(data). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Apply takes the given apply declarative configuration, applies it and returns the applied receiver.
|
||||
func (c *receivers) Apply(ctx context.Context, receiver *alertingnotificationsv0alpha1.ReceiverApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.Receiver, err error) { |
||||
if receiver == nil { |
||||
return nil, fmt.Errorf("receiver provided to Apply must not be nil") |
||||
} |
||||
patchOpts := opts.ToPatchOptions() |
||||
data, err := json.Marshal(receiver) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
name := receiver.Name |
||||
if name == nil { |
||||
return nil, fmt.Errorf("receiver.Name must be provided to Apply") |
||||
} |
||||
result = &v0alpha1.Receiver{} |
||||
err = c.client.Patch(types.ApplyPatchType). |
||||
Namespace(c.ns). |
||||
Resource("receivers"). |
||||
Name(*name). |
||||
VersionedParams(&patchOpts, scheme.ParameterCodec). |
||||
Body(data). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
@ -0,0 +1,76 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"context" |
||||
time "time" |
||||
|
||||
alertingnotificationsv0alpha1 "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
versioned "github.com/grafana/grafana/pkg/generated/clientset/versioned" |
||||
internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" |
||||
v0alpha1 "github.com/grafana/grafana/pkg/generated/listers/alerting_notifications/v0alpha1" |
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
runtime "k8s.io/apimachinery/pkg/runtime" |
||||
watch "k8s.io/apimachinery/pkg/watch" |
||||
cache "k8s.io/client-go/tools/cache" |
||||
) |
||||
|
||||
// ReceiverInformer provides access to a shared informer and lister for
|
||||
// Receivers.
|
||||
type ReceiverInformer interface { |
||||
Informer() cache.SharedIndexInformer |
||||
Lister() v0alpha1.ReceiverLister |
||||
} |
||||
|
||||
type receiverInformer struct { |
||||
factory internalinterfaces.SharedInformerFactory |
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc |
||||
namespace string |
||||
} |
||||
|
||||
// NewReceiverInformer constructs a new informer for Receiver type.
|
||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||
// one. This reduces memory footprint and number of connections to the server.
|
||||
func NewReceiverInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { |
||||
return NewFilteredReceiverInformer(client, namespace, resyncPeriod, indexers, nil) |
||||
} |
||||
|
||||
// NewFilteredReceiverInformer constructs a new informer for Receiver type.
|
||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||
// one. This reduces memory footprint and number of connections to the server.
|
||||
func NewFilteredReceiverInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { |
||||
return cache.NewSharedIndexInformer( |
||||
&cache.ListWatch{ |
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) { |
||||
if tweakListOptions != nil { |
||||
tweakListOptions(&options) |
||||
} |
||||
return client.NotificationsV0alpha1().Receivers(namespace).List(context.TODO(), options) |
||||
}, |
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { |
||||
if tweakListOptions != nil { |
||||
tweakListOptions(&options) |
||||
} |
||||
return client.NotificationsV0alpha1().Receivers(namespace).Watch(context.TODO(), options) |
||||
}, |
||||
}, |
||||
&alertingnotificationsv0alpha1.Receiver{}, |
||||
resyncPeriod, |
||||
indexers, |
||||
) |
||||
} |
||||
|
||||
func (f *receiverInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { |
||||
return NewFilteredReceiverInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) |
||||
} |
||||
|
||||
func (f *receiverInformer) Informer() cache.SharedIndexInformer { |
||||
return f.factory.InformerFor(&alertingnotificationsv0alpha1.Receiver{}, f.defaultInformer) |
||||
} |
||||
|
||||
func (f *receiverInformer) Lister() v0alpha1.ReceiverLister { |
||||
return v0alpha1.NewReceiverLister(f.Informer().GetIndexer()) |
||||
} |
@ -0,0 +1,85 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
v0alpha1 "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
"k8s.io/apimachinery/pkg/api/errors" |
||||
"k8s.io/apimachinery/pkg/labels" |
||||
"k8s.io/client-go/tools/cache" |
||||
) |
||||
|
||||
// ReceiverLister helps list Receivers.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type ReceiverLister interface { |
||||
// List lists all Receivers in the indexer.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.Receiver, err error) |
||||
// Receivers returns an object that can list and get Receivers.
|
||||
Receivers(namespace string) ReceiverNamespaceLister |
||||
ReceiverListerExpansion |
||||
} |
||||
|
||||
// receiverLister implements the ReceiverLister interface.
|
||||
type receiverLister struct { |
||||
indexer cache.Indexer |
||||
} |
||||
|
||||
// NewReceiverLister returns a new ReceiverLister.
|
||||
func NewReceiverLister(indexer cache.Indexer) ReceiverLister { |
||||
return &receiverLister{indexer: indexer} |
||||
} |
||||
|
||||
// List lists all Receivers in the indexer.
|
||||
func (s *receiverLister) List(selector labels.Selector) (ret []*v0alpha1.Receiver, err error) { |
||||
err = cache.ListAll(s.indexer, selector, func(m interface{}) { |
||||
ret = append(ret, m.(*v0alpha1.Receiver)) |
||||
}) |
||||
return ret, err |
||||
} |
||||
|
||||
// Receivers returns an object that can list and get Receivers.
|
||||
func (s *receiverLister) Receivers(namespace string) ReceiverNamespaceLister { |
||||
return receiverNamespaceLister{indexer: s.indexer, namespace: namespace} |
||||
} |
||||
|
||||
// ReceiverNamespaceLister helps list and get Receivers.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type ReceiverNamespaceLister interface { |
||||
// List lists all Receivers in the indexer for a given namespace.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.Receiver, err error) |
||||
// Get retrieves the Receiver from the indexer for a given namespace and name.
|
||||
// Objects returned here must be treated as read-only.
|
||||
Get(name string) (*v0alpha1.Receiver, error) |
||||
ReceiverNamespaceListerExpansion |
||||
} |
||||
|
||||
// receiverNamespaceLister implements the ReceiverNamespaceLister
|
||||
// interface.
|
||||
type receiverNamespaceLister struct { |
||||
indexer cache.Indexer |
||||
namespace string |
||||
} |
||||
|
||||
// List lists all Receivers in the indexer for a given namespace.
|
||||
func (s receiverNamespaceLister) List(selector labels.Selector) (ret []*v0alpha1.Receiver, err error) { |
||||
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { |
||||
ret = append(ret, m.(*v0alpha1.Receiver)) |
||||
}) |
||||
return ret, err |
||||
} |
||||
|
||||
// Get retrieves the Receiver from the indexer for a given namespace and name.
|
||||
func (s receiverNamespaceLister) Get(name string) (*v0alpha1.Receiver, error) { |
||||
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if !exists { |
||||
return nil, errors.NewNotFound(v0alpha1.Resource("receiver"), name) |
||||
} |
||||
return obj.(*v0alpha1.Receiver), nil |
||||
} |
@ -0,0 +1,53 @@ |
||||
package receiver |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer" |
||||
|
||||
"github.com/grafana/grafana/pkg/infra/appcontext" |
||||
"github.com/grafana/grafana/pkg/services/accesscontrol" |
||||
) |
||||
|
||||
func Authorize(ctx context.Context, ac accesscontrol.AccessControl, attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { |
||||
if attr.GetResource() != resourceInfo.GroupResource().Resource { |
||||
return authorizer.DecisionNoOpinion, "", nil |
||||
} |
||||
user, err := appcontext.User(ctx) |
||||
if err != nil { |
||||
return authorizer.DecisionDeny, "valid user is required", err |
||||
} |
||||
|
||||
var action accesscontrol.Evaluator |
||||
switch attr.GetVerb() { |
||||
case "patch": |
||||
fallthrough |
||||
case "create": |
||||
fallthrough // TODO: Add alert.notifications.receivers:create permission
|
||||
case "update": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), // TODO: Add alert.notifications.receivers:write permission
|
||||
) |
||||
case "deletecollection": |
||||
fallthrough |
||||
case "delete": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), // TODO: Add alert.notifications.receivers:delete permission
|
||||
) |
||||
} |
||||
|
||||
eval := accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingReceiversRead), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingReceiversReadSecrets), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsRead), |
||||
) |
||||
if action != nil { |
||||
eval = accesscontrol.EvalAll(eval, action) |
||||
} |
||||
|
||||
ok, err := ac.Evaluate(ctx, user, eval) |
||||
if ok { |
||||
return authorizer.DecisionAllow, "", nil |
||||
} |
||||
return authorizer.DecisionDeny, "", err |
||||
} |
@ -0,0 +1,101 @@ |
||||
package receiver |
||||
|
||||
import ( |
||||
"fmt" |
||||
"hash/fnv" |
||||
|
||||
"github.com/prometheus/alertmanager/config" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
|
||||
model "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
) |
||||
|
||||
func getUID(t definitions.GettableApiReceiver) string { |
||||
sum := fnv.New64() |
||||
_, _ = sum.Write([]byte(t.Name)) |
||||
return fmt.Sprintf("%016x", sum.Sum64()) |
||||
} |
||||
|
||||
func convertToK8sResources(orgID int64, receivers []definitions.GettableApiReceiver, namespacer request.NamespaceMapper) (*model.ReceiverList, error) { |
||||
result := &model.ReceiverList{ |
||||
Items: make([]model.Receiver, 0, len(receivers)), |
||||
} |
||||
for _, receiver := range receivers { |
||||
k8sResource, err := convertToK8sResource(orgID, receiver, namespacer) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
result.Items = append(result.Items, *k8sResource) |
||||
} |
||||
return result, nil |
||||
} |
||||
|
||||
func convertToK8sResource(orgID int64, receiver definitions.GettableApiReceiver, namespacer request.NamespaceMapper) (*model.Receiver, error) { |
||||
spec := model.ReceiverSpec{ |
||||
Title: receiver.Receiver.Name, |
||||
} |
||||
provenance := definitions.Provenance(models.ProvenanceNone) |
||||
for _, integration := range receiver.GrafanaManagedReceivers { |
||||
if integration.Provenance != receiver.GrafanaManagedReceivers[0].Provenance { |
||||
return nil, fmt.Errorf("all integrations must have the same provenance") |
||||
} |
||||
provenance = integration.Provenance |
||||
spec.Integrations = append(spec.Integrations, model.Integration{ |
||||
Uid: &integration.UID, |
||||
Type: integration.Type, |
||||
DisableResolveMessage: &integration.DisableResolveMessage, |
||||
Settings: integration.Settings, |
||||
SecureFields: integration.SecureFields, |
||||
}) |
||||
} |
||||
|
||||
uid := getUID(receiver) // TODO replace to stable UID when we switch to normal storage
|
||||
return &model.Receiver{ |
||||
TypeMeta: resourceInfo.TypeMeta(), |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
UID: types.UID(uid), // This is needed to make PATCH work
|
||||
Name: uid, // TODO replace to stable UID when we switch to normal storage
|
||||
Namespace: namespacer(orgID), |
||||
Annotations: map[string]string{ // TODO find a better place for provenance?
|
||||
"grafana.com/provenance": string(provenance), |
||||
}, |
||||
ResourceVersion: "", // TODO: Implement optimistic concurrency.
|
||||
}, |
||||
Spec: spec, |
||||
}, nil |
||||
} |
||||
|
||||
func convertToDomainModel(receiver *model.Receiver) (definitions.GettableApiReceiver, error) { |
||||
// TODO: Using GettableApiReceiver instead of PostableApiReceiver so that SecureFields type matches.
|
||||
gettable := definitions.GettableApiReceiver{ |
||||
Receiver: config.Receiver{ |
||||
Name: receiver.Spec.Title, |
||||
}, |
||||
GettableGrafanaReceivers: definitions.GettableGrafanaReceivers{ |
||||
GrafanaManagedReceivers: []*definitions.GettableGrafanaReceiver{}, |
||||
}, |
||||
} |
||||
|
||||
for _, integration := range receiver.Spec.Integrations { |
||||
grafanaIntegration := definitions.GettableGrafanaReceiver{ |
||||
Name: receiver.Spec.Title, |
||||
Type: integration.Type, |
||||
Settings: integration.Settings, |
||||
SecureFields: integration.SecureFields, |
||||
//Provenance: "", //TODO: Convert provenance?
|
||||
} |
||||
if integration.Uid != nil { |
||||
grafanaIntegration.UID = *integration.Uid |
||||
} |
||||
if integration.DisableResolveMessage != nil { |
||||
grafanaIntegration.DisableResolveMessage = *integration.DisableResolveMessage |
||||
} |
||||
gettable.GettableGrafanaReceivers.GrafanaManagedReceivers = append(gettable.GettableGrafanaReceivers.GrafanaManagedReceivers, &grafanaIntegration) |
||||
} |
||||
|
||||
return gettable, nil |
||||
} |
@ -0,0 +1,225 @@ |
||||
package receiver |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors" |
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apiserver/pkg/registry/rest" |
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity" |
||||
notifications "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
grafanaRest "github.com/grafana/grafana/pkg/apiserver/rest" |
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
) |
||||
|
||||
var ( |
||||
_ grafanaRest.LegacyStorage = (*legacyStorage)(nil) |
||||
) |
||||
|
||||
var resourceInfo = notifications.ReceiverResourceInfo |
||||
|
||||
type ReceiverService interface { |
||||
GetReceiver(ctx context.Context, q models.GetReceiverQuery, user identity.Requester) (definitions.GettableApiReceiver, error) |
||||
GetReceivers(ctx context.Context, q models.GetReceiversQuery, user identity.Requester) ([]definitions.GettableApiReceiver, error) |
||||
CreateReceiver(ctx context.Context, r definitions.GettableApiReceiver, orgID int64) (definitions.GettableApiReceiver, error) // TODO: Uses Gettable for Write, consider creating new struct.
|
||||
UpdateReceiver(ctx context.Context, r definitions.GettableApiReceiver, orgID int64) (definitions.GettableApiReceiver, error) // TODO: Uses Gettable for Write, consider creating new struct.
|
||||
DeleteReceiver(ctx context.Context, name string, orgID int64, provenance definitions.Provenance, version string) error |
||||
} |
||||
|
||||
type legacyStorage struct { |
||||
service ReceiverService |
||||
namespacer request.NamespaceMapper |
||||
tableConverter rest.TableConvertor |
||||
} |
||||
|
||||
func (s *legacyStorage) New() runtime.Object { |
||||
return resourceInfo.NewFunc() |
||||
} |
||||
|
||||
func (s *legacyStorage) Destroy() {} |
||||
|
||||
func (s *legacyStorage) NamespaceScoped() bool { |
||||
return true // namespace == org
|
||||
} |
||||
|
||||
func (s *legacyStorage) GetSingularName() string { |
||||
return resourceInfo.GetSingularName() |
||||
} |
||||
|
||||
func (s *legacyStorage) NewList() runtime.Object { |
||||
return resourceInfo.NewListFunc() |
||||
} |
||||
|
||||
func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { |
||||
return s.tableConverter.ConvertToTable(ctx, object, tableOptions) |
||||
} |
||||
|
||||
func (s *legacyStorage) List(ctx context.Context, _ *internalversion.ListOptions) (runtime.Object, error) { |
||||
orgId, err := request.OrgIDForList(ctx) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
q := models.GetReceiversQuery{ |
||||
OrgID: orgId, |
||||
//Names: ctx.QueryStrings("names"), // TODO: Query params.
|
||||
//Limit: ctx.QueryInt("limit"),
|
||||
//Offset: ctx.QueryInt("offset"),
|
||||
//Decrypt: ctx.QueryBool("decrypt"),
|
||||
} |
||||
|
||||
user, err := identity.GetRequester(ctx) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
res, err := s.service.GetReceivers(ctx, q, user) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return convertToK8sResources(orgId, res, s.namespacer) |
||||
} |
||||
|
||||
func (s *legacyStorage) Get(ctx context.Context, uid string, _ *metav1.GetOptions) (runtime.Object, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
q := models.GetReceiverQuery{ |
||||
OrgID: info.OrgID, |
||||
Name: uid, // TODO: Name/UID mapping or change signature of service.
|
||||
//Decrypt: ctx.QueryBool("decrypt"), // TODO: Query params.
|
||||
} |
||||
|
||||
user, err := identity.GetRequester(ctx) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
res, err := s.service.GetReceiver(ctx, q, user) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return convertToK8sResource(info.OrgID, res, s.namespacer) |
||||
} |
||||
|
||||
func (s *legacyStorage) Create(ctx context.Context, |
||||
obj runtime.Object, |
||||
createValidation rest.ValidateObjectFunc, |
||||
_ *metav1.CreateOptions, |
||||
) (runtime.Object, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if createValidation != nil { |
||||
if err := createValidation(ctx, obj.DeepCopyObject()); err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
p, ok := obj.(*notifications.Receiver) |
||||
if !ok { |
||||
return nil, fmt.Errorf("expected receiver but got %s", obj.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
if p.ObjectMeta.Name != "" { // TODO remove when metadata.name can be defined by user
|
||||
return nil, errors.NewBadRequest("object's metadata.name should be empty") |
||||
} |
||||
model, err := convertToDomainModel(p) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
out, err := s.service.CreateReceiver(ctx, model, info.OrgID) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return convertToK8sResource(info.OrgID, out, s.namespacer) |
||||
} |
||||
|
||||
func (s *legacyStorage) Update(ctx context.Context, |
||||
uid string, |
||||
objInfo rest.UpdatedObjectInfo, |
||||
createValidation rest.ValidateObjectFunc, |
||||
updateValidation rest.ValidateObjectUpdateFunc, |
||||
_ bool, |
||||
_ *metav1.UpdateOptions, |
||||
) (runtime.Object, bool, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, false, err |
||||
} |
||||
|
||||
old, err := s.Get(ctx, uid, nil) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
obj, err := objInfo.UpdatedObject(ctx, old) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
if updateValidation != nil { |
||||
if err := updateValidation(ctx, obj, old); err != nil { |
||||
return nil, false, err |
||||
} |
||||
} |
||||
p, ok := obj.(*notifications.Receiver) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("expected receiver but got %s", obj.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
model, err := convertToDomainModel(p) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
|
||||
if p.ObjectMeta.Name != getUID(model) { |
||||
return nil, false, errors.NewBadRequest("title cannot be changed. Consider creating a new resource.") |
||||
} |
||||
|
||||
updated, err := s.service.UpdateReceiver(ctx, model, info.OrgID) |
||||
if err != nil { |
||||
return nil, false, err |
||||
} |
||||
|
||||
r, err := convertToK8sResource(info.OrgID, updated, s.namespacer) |
||||
return r, false, err |
||||
} |
||||
|
||||
// GracefulDeleter
|
||||
func (s *legacyStorage) Delete(ctx context.Context, uid string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, false, err |
||||
} |
||||
old, err := s.Get(ctx, uid, nil) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
if deleteValidation != nil { |
||||
if err = deleteValidation(ctx, old); err != nil { |
||||
return nil, false, err |
||||
} |
||||
} |
||||
version := "" |
||||
if options.Preconditions != nil && options.Preconditions.ResourceVersion != nil { |
||||
version = *options.Preconditions.ResourceVersion |
||||
} |
||||
p, ok := old.(*notifications.Receiver) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("expected receiver but got %s", old.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
|
||||
err = s.service.DeleteReceiver(ctx, p.Spec.Title, info.OrgID, definitions.Provenance(models.ProvenanceNone), version) // TODO add support for dry-run option
|
||||
return old, false, err // false - will be deleted async
|
||||
} |
||||
|
||||
func (s *legacyStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) { |
||||
return nil, errors.NewMethodNotSupported(resourceInfo.GroupResource(), "deleteCollection") |
||||
} |
@ -0,0 +1,80 @@ |
||||
package receiver |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apiserver/pkg/registry/generic" |
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" |
||||
"k8s.io/apiserver/pkg/registry/rest" |
||||
|
||||
"github.com/prometheus/client_golang/prometheus" |
||||
|
||||
model "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" |
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" |
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" |
||||
"github.com/grafana/grafana/pkg/services/apiserver/utils" |
||||
) |
||||
|
||||
var _ grafanarest.Storage = (*storage)(nil) |
||||
|
||||
type storage struct { |
||||
*genericregistry.Store |
||||
} |
||||
|
||||
func (s storage) Compare(storageObj, legacyObj runtime.Object) bool { |
||||
// TODO implement when supported dual write mode is not Mode0
|
||||
return false |
||||
} |
||||
|
||||
func NewStorage( |
||||
legacySvc ReceiverService, |
||||
namespacer request.NamespaceMapper, |
||||
scheme *runtime.Scheme, |
||||
desiredMode grafanarest.DualWriterMode, |
||||
optsGetter generic.RESTOptionsGetter, |
||||
reg prometheus.Registerer) (rest.Storage, error) { |
||||
legacyStore := &legacyStorage{ |
||||
service: legacySvc, |
||||
namespacer: namespacer, |
||||
tableConverter: utils.NewTableConverter( |
||||
resourceInfo.GroupResource(), |
||||
[]metav1.TableColumnDefinition{ |
||||
{Name: "Name", Type: "string", Format: "name"}, |
||||
{Name: "Title", Type: "string", Format: "string", Description: "The receiver name"}, // TODO: Add integration types.
|
||||
}, |
||||
func(obj any) ([]interface{}, error) { |
||||
r, ok := obj.(*model.Receiver) |
||||
if ok { |
||||
return []interface{}{ |
||||
r.Name, |
||||
r.Spec.Title, |
||||
// r.Spec, //TODO implement formatting for Spec, same as UI?
|
||||
}, nil |
||||
} |
||||
return nil, fmt.Errorf("expected resource or info") |
||||
}), |
||||
} |
||||
if optsGetter != nil && desiredMode != grafanarest.Mode0 { |
||||
strategy := grafanaregistry.NewStrategy(scheme) |
||||
s := &genericregistry.Store{ |
||||
NewFunc: resourceInfo.NewFunc, |
||||
NewListFunc: resourceInfo.NewListFunc, |
||||
PredicateFunc: grafanaregistry.Matcher, |
||||
DefaultQualifiedResource: resourceInfo.GroupResource(), |
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(), |
||||
TableConvertor: legacyStore.tableConverter, |
||||
CreateStrategy: strategy, |
||||
UpdateStrategy: strategy, |
||||
DeleteStrategy: strategy, |
||||
} |
||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: grafanaregistry.GetAttrs} |
||||
if err := s.CompleteWithOptions(options); err != nil { |
||||
return nil, err |
||||
} |
||||
return grafanarest.NewDualWriter(desiredMode, legacyStore, storage{Store: s}, reg), nil |
||||
} |
||||
return legacyStore, nil |
||||
} |
Loading…
Reference in new issue