mirror of https://github.com/grafana/grafana
Alerting: Notifications Templates API (#91349)
parent
5472478ee8
commit
10582e48f7
@ -0,0 +1,25 @@ |
||||
package core |
||||
|
||||
templateGroup: { |
||||
kind: "TemplateGroup" |
||||
group: "notifications" |
||||
apiResource: { |
||||
groupOverride: "notifications.alerting.grafana.app" |
||||
} |
||||
codegen: { |
||||
frontend: false |
||||
backend: true |
||||
} |
||||
pluralName: "TemplatesGroups" |
||||
current: "v0alpha1" |
||||
versions: { |
||||
"v0alpha1": { |
||||
schema: { |
||||
spec: { |
||||
title: string |
||||
content: string |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,8 @@ |
||||
package v0alpha1 |
||||
|
||||
// TemplateGroupSpec defines model for TemplateGroupSpec.
|
||||
// +k8s:openapi-gen=true
|
||||
type TemplateGroupSpec struct { |
||||
Title string `json:"title"` |
||||
Content string `json:"content"` |
||||
} |
@ -0,0 +1,202 @@ |
||||
// 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" |
||||
) |
||||
|
||||
// TemplateGroupApplyConfiguration represents a declarative configuration of the TemplateGroup type for use
|
||||
// with apply.
|
||||
type TemplateGroupApplyConfiguration struct { |
||||
v1.TypeMetaApplyConfiguration `json:",inline"` |
||||
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` |
||||
Spec *TemplateGroupSpecApplyConfiguration `json:"spec,omitempty"` |
||||
} |
||||
|
||||
// TemplateGroup constructs a declarative configuration of the TemplateGroup type for use with
|
||||
// apply.
|
||||
func TemplateGroup(name, namespace string) *TemplateGroupApplyConfiguration { |
||||
b := &TemplateGroupApplyConfiguration{} |
||||
b.WithName(name) |
||||
b.WithNamespace(namespace) |
||||
b.WithKind("TemplateGroup") |
||||
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 *TemplateGroupApplyConfiguration) WithKind(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithAPIVersion(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithName(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithGenerateName(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithNamespace(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithUID(value types.UID) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithResourceVersion(value string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithGeneration(value int64) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithLabels(entries map[string]string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithAnnotations(entries map[string]string) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TemplateGroupApplyConfiguration { |
||||
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 *TemplateGroupApplyConfiguration) WithFinalizers(values ...string) *TemplateGroupApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
for i := range values { |
||||
b.Finalizers = append(b.Finalizers, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
func (b *TemplateGroupApplyConfiguration) 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 *TemplateGroupApplyConfiguration) WithSpec(value *TemplateGroupSpecApplyConfiguration) *TemplateGroupApplyConfiguration { |
||||
b.Spec = value |
||||
return b |
||||
} |
||||
|
||||
// GetName retrieves the value of the Name field in the declarative configuration.
|
||||
func (b *TemplateGroupApplyConfiguration) GetName() *string { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
return b.Name |
||||
} |
@ -0,0 +1,34 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// TemplateGroupSpecApplyConfiguration represents a declarative configuration of the TemplateGroupSpec type for use
|
||||
// with apply.
|
||||
type TemplateGroupSpecApplyConfiguration struct { |
||||
Title *string `json:"title,omitempty"` |
||||
Content *string `json:"content,omitempty"` |
||||
} |
||||
|
||||
// TemplateGroupSpecApplyConfiguration constructs a declarative configuration of the TemplateGroupSpec type for use with
|
||||
// apply.
|
||||
func TemplateGroupSpec() *TemplateGroupSpecApplyConfiguration { |
||||
return &TemplateGroupSpecApplyConfiguration{} |
||||
} |
||||
|
||||
// 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 *TemplateGroupSpecApplyConfiguration) WithTitle(value string) *TemplateGroupSpecApplyConfiguration { |
||||
b.Title = &value |
||||
return b |
||||
} |
||||
|
||||
// WithContent sets the Content 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 Content field is set to the value of the last call.
|
||||
func (b *TemplateGroupSpecApplyConfiguration) WithContent(value string) *TemplateGroupSpecApplyConfiguration { |
||||
b.Content = &value |
||||
return b |
||||
} |
@ -0,0 +1,146 @@ |
||||
// 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" |
||||
) |
||||
|
||||
// FakeTemplateGroups implements TemplateGroupInterface
|
||||
type FakeTemplateGroups struct { |
||||
Fake *FakeNotificationsV0alpha1 |
||||
ns string |
||||
} |
||||
|
||||
var templategroupsResource = v0alpha1.SchemeGroupVersion.WithResource("templategroups") |
||||
|
||||
var templategroupsKind = v0alpha1.SchemeGroupVersion.WithKind("TemplateGroup") |
||||
|
||||
// Get takes name of the templateGroup, and returns the corresponding templateGroup object, and an error if there is any.
|
||||
func (c *FakeTemplateGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v0alpha1.TemplateGroup, err error) { |
||||
emptyResult := &v0alpha1.TemplateGroup{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewGetActionWithOptions(templategroupsResource, c.ns, name, options), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
return obj.(*v0alpha1.TemplateGroup), err |
||||
} |
||||
|
||||
// List takes label and field selectors, and returns the list of TemplateGroups that match those selectors.
|
||||
func (c *FakeTemplateGroups) List(ctx context.Context, opts v1.ListOptions) (result *v0alpha1.TemplateGroupList, err error) { |
||||
emptyResult := &v0alpha1.TemplateGroupList{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewListActionWithOptions(templategroupsResource, templategroupsKind, c.ns, opts), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts) |
||||
if label == nil { |
||||
label = labels.Everything() |
||||
} |
||||
list := &v0alpha1.TemplateGroupList{ListMeta: obj.(*v0alpha1.TemplateGroupList).ListMeta} |
||||
for _, item := range obj.(*v0alpha1.TemplateGroupList).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 templateGroups.
|
||||
func (c *FakeTemplateGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { |
||||
return c.Fake. |
||||
InvokesWatch(testing.NewWatchActionWithOptions(templategroupsResource, c.ns, opts)) |
||||
|
||||
} |
||||
|
||||
// Create takes the representation of a templateGroup and creates it. Returns the server's representation of the templateGroup, and an error, if there is any.
|
||||
func (c *FakeTemplateGroups) Create(ctx context.Context, templateGroup *v0alpha1.TemplateGroup, opts v1.CreateOptions) (result *v0alpha1.TemplateGroup, err error) { |
||||
emptyResult := &v0alpha1.TemplateGroup{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewCreateActionWithOptions(templategroupsResource, c.ns, templateGroup, opts), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
return obj.(*v0alpha1.TemplateGroup), err |
||||
} |
||||
|
||||
// Update takes the representation of a templateGroup and updates it. Returns the server's representation of the templateGroup, and an error, if there is any.
|
||||
func (c *FakeTemplateGroups) Update(ctx context.Context, templateGroup *v0alpha1.TemplateGroup, opts v1.UpdateOptions) (result *v0alpha1.TemplateGroup, err error) { |
||||
emptyResult := &v0alpha1.TemplateGroup{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewUpdateActionWithOptions(templategroupsResource, c.ns, templateGroup, opts), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
return obj.(*v0alpha1.TemplateGroup), err |
||||
} |
||||
|
||||
// Delete takes name of the templateGroup and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeTemplateGroups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { |
||||
_, err := c.Fake. |
||||
Invokes(testing.NewDeleteActionWithOptions(templategroupsResource, c.ns, name, opts), &v0alpha1.TemplateGroup{}) |
||||
|
||||
return err |
||||
} |
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeTemplateGroups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { |
||||
action := testing.NewDeleteCollectionActionWithOptions(templategroupsResource, c.ns, opts, listOpts) |
||||
|
||||
_, err := c.Fake.Invokes(action, &v0alpha1.TemplateGroupList{}) |
||||
return err |
||||
} |
||||
|
||||
// Patch applies the patch and returns the patched templateGroup.
|
||||
func (c *FakeTemplateGroups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.TemplateGroup, err error) { |
||||
emptyResult := &v0alpha1.TemplateGroup{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceActionWithOptions(templategroupsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
return obj.(*v0alpha1.TemplateGroup), err |
||||
} |
||||
|
||||
// Apply takes the given apply declarative configuration, applies it and returns the applied templateGroup.
|
||||
func (c *FakeTemplateGroups) Apply(ctx context.Context, templateGroup *alertingnotificationsv0alpha1.TemplateGroupApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.TemplateGroup, err error) { |
||||
if templateGroup == nil { |
||||
return nil, fmt.Errorf("templateGroup provided to Apply must not be nil") |
||||
} |
||||
data, err := json.Marshal(templateGroup) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
name := templateGroup.Name |
||||
if name == nil { |
||||
return nil, fmt.Errorf("templateGroup.Name must be provided to Apply") |
||||
} |
||||
emptyResult := &v0alpha1.TemplateGroup{} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceActionWithOptions(templategroupsResource, c.ns, *name, types.ApplyPatchType, data, opts.ToPatchOptions()), emptyResult) |
||||
|
||||
if obj == nil { |
||||
return emptyResult, err |
||||
} |
||||
return obj.(*v0alpha1.TemplateGroup), err |
||||
} |
@ -0,0 +1,55 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
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" |
||||
gentype "k8s.io/client-go/gentype" |
||||
) |
||||
|
||||
// TemplateGroupsGetter has a method to return a TemplateGroupInterface.
|
||||
// A group's client should implement this interface.
|
||||
type TemplateGroupsGetter interface { |
||||
TemplateGroups(namespace string) TemplateGroupInterface |
||||
} |
||||
|
||||
// TemplateGroupInterface has methods to work with TemplateGroup resources.
|
||||
type TemplateGroupInterface interface { |
||||
Create(ctx context.Context, templateGroup *v0alpha1.TemplateGroup, opts v1.CreateOptions) (*v0alpha1.TemplateGroup, error) |
||||
Update(ctx context.Context, templateGroup *v0alpha1.TemplateGroup, opts v1.UpdateOptions) (*v0alpha1.TemplateGroup, 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.TemplateGroup, error) |
||||
List(ctx context.Context, opts v1.ListOptions) (*v0alpha1.TemplateGroupList, 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.TemplateGroup, err error) |
||||
Apply(ctx context.Context, templateGroup *alertingnotificationsv0alpha1.TemplateGroupApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.TemplateGroup, err error) |
||||
TemplateGroupExpansion |
||||
} |
||||
|
||||
// templateGroups implements TemplateGroupInterface
|
||||
type templateGroups struct { |
||||
*gentype.ClientWithListAndApply[*v0alpha1.TemplateGroup, *v0alpha1.TemplateGroupList, *alertingnotificationsv0alpha1.TemplateGroupApplyConfiguration] |
||||
} |
||||
|
||||
// newTemplateGroups returns a TemplateGroups
|
||||
func newTemplateGroups(c *NotificationsV0alpha1Client, namespace string) *templateGroups { |
||||
return &templateGroups{ |
||||
gentype.NewClientWithListAndApply[*v0alpha1.TemplateGroup, *v0alpha1.TemplateGroupList, *alertingnotificationsv0alpha1.TemplateGroupApplyConfiguration]( |
||||
"templategroups", |
||||
c.RESTClient(), |
||||
scheme.ParameterCodec, |
||||
namespace, |
||||
func() *v0alpha1.TemplateGroup { return &v0alpha1.TemplateGroup{} }, |
||||
func() *v0alpha1.TemplateGroupList { return &v0alpha1.TemplateGroupList{} }), |
||||
} |
||||
} |
@ -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" |
||||
) |
||||
|
||||
// TemplateGroupInformer provides access to a shared informer and lister for
|
||||
// TemplateGroups.
|
||||
type TemplateGroupInformer interface { |
||||
Informer() cache.SharedIndexInformer |
||||
Lister() v0alpha1.TemplateGroupLister |
||||
} |
||||
|
||||
type templateGroupInformer struct { |
||||
factory internalinterfaces.SharedInformerFactory |
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc |
||||
namespace string |
||||
} |
||||
|
||||
// NewTemplateGroupInformer constructs a new informer for TemplateGroup 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 NewTemplateGroupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { |
||||
return NewFilteredTemplateGroupInformer(client, namespace, resyncPeriod, indexers, nil) |
||||
} |
||||
|
||||
// NewFilteredTemplateGroupInformer constructs a new informer for TemplateGroup 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 NewFilteredTemplateGroupInformer(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().TemplateGroups(namespace).List(context.TODO(), options) |
||||
}, |
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { |
||||
if tweakListOptions != nil { |
||||
tweakListOptions(&options) |
||||
} |
||||
return client.NotificationsV0alpha1().TemplateGroups(namespace).Watch(context.TODO(), options) |
||||
}, |
||||
}, |
||||
&alertingnotificationsv0alpha1.TemplateGroup{}, |
||||
resyncPeriod, |
||||
indexers, |
||||
) |
||||
} |
||||
|
||||
func (f *templateGroupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { |
||||
return NewFilteredTemplateGroupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) |
||||
} |
||||
|
||||
func (f *templateGroupInformer) Informer() cache.SharedIndexInformer { |
||||
return f.factory.InformerFor(&alertingnotificationsv0alpha1.TemplateGroup{}, f.defaultInformer) |
||||
} |
||||
|
||||
func (f *templateGroupInformer) Lister() v0alpha1.TemplateGroupLister { |
||||
return v0alpha1.NewTemplateGroupLister(f.Informer().GetIndexer()) |
||||
} |
@ -0,0 +1,56 @@ |
||||
// 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/labels" |
||||
"k8s.io/client-go/listers" |
||||
"k8s.io/client-go/tools/cache" |
||||
) |
||||
|
||||
// TemplateGroupLister helps list TemplateGroups.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type TemplateGroupLister interface { |
||||
// List lists all TemplateGroups in the indexer.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.TemplateGroup, err error) |
||||
// TemplateGroups returns an object that can list and get TemplateGroups.
|
||||
TemplateGroups(namespace string) TemplateGroupNamespaceLister |
||||
TemplateGroupListerExpansion |
||||
} |
||||
|
||||
// templateGroupLister implements the TemplateGroupLister interface.
|
||||
type templateGroupLister struct { |
||||
listers.ResourceIndexer[*v0alpha1.TemplateGroup] |
||||
} |
||||
|
||||
// NewTemplateGroupLister returns a new TemplateGroupLister.
|
||||
func NewTemplateGroupLister(indexer cache.Indexer) TemplateGroupLister { |
||||
return &templateGroupLister{listers.New[*v0alpha1.TemplateGroup](indexer, v0alpha1.Resource("templategroup"))} |
||||
} |
||||
|
||||
// TemplateGroups returns an object that can list and get TemplateGroups.
|
||||
func (s *templateGroupLister) TemplateGroups(namespace string) TemplateGroupNamespaceLister { |
||||
return templateGroupNamespaceLister{listers.NewNamespaced[*v0alpha1.TemplateGroup](s.ResourceIndexer, namespace)} |
||||
} |
||||
|
||||
// TemplateGroupNamespaceLister helps list and get TemplateGroups.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type TemplateGroupNamespaceLister interface { |
||||
// List lists all TemplateGroups in the indexer for a given namespace.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.TemplateGroup, err error) |
||||
// Get retrieves the TemplateGroup from the indexer for a given namespace and name.
|
||||
// Objects returned here must be treated as read-only.
|
||||
Get(name string) (*v0alpha1.TemplateGroup, error) |
||||
TemplateGroupNamespaceListerExpansion |
||||
} |
||||
|
||||
// templateGroupNamespaceLister implements the TemplateGroupNamespaceLister
|
||||
// interface.
|
||||
type templateGroupNamespaceLister struct { |
||||
listers.ResourceIndexer[*v0alpha1.TemplateGroup] |
||||
} |
@ -0,0 +1,54 @@ |
||||
package template_group |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer" |
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity" |
||||
"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 := identity.GetRequester(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 |
||||
case "update": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTemplatesWrite), |
||||
) |
||||
case "deletecollection": |
||||
fallthrough |
||||
case "delete": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTemplatesDelete), |
||||
) |
||||
} |
||||
|
||||
eval := accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsRead), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTemplatesRead), |
||||
) |
||||
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,52 @@ |
||||
package template_group |
||||
|
||||
import ( |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/fields" |
||||
"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" |
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" |
||||
) |
||||
|
||||
func convertToK8sResources(orgID int64, list []definitions.NotificationTemplate, namespacer request.NamespaceMapper, selector fields.Selector) (*model.TemplateGroupList, error) { |
||||
result := &model.TemplateGroupList{} |
||||
for _, t := range list { |
||||
item := convertToK8sResource(orgID, t, namespacer) |
||||
if selector != nil && !selector.Empty() && !selector.Matches(model.SelectableTemplateGroupFields(item)) { |
||||
continue |
||||
} |
||||
result.Items = append(result.Items, *item) |
||||
} |
||||
return result, nil |
||||
} |
||||
|
||||
func convertToK8sResource(orgID int64, template definitions.NotificationTemplate, namespacer request.NamespaceMapper) *model.TemplateGroup { |
||||
result := &model.TemplateGroup{ |
||||
TypeMeta: resourceInfo.TypeMeta(), |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
UID: types.UID(template.UID), |
||||
Name: template.UID, |
||||
Namespace: namespacer(orgID), |
||||
ResourceVersion: template.ResourceVersion, |
||||
}, |
||||
Spec: model.TemplateGroupSpec{ |
||||
Title: template.Name, |
||||
Content: template.Template, |
||||
}, |
||||
} |
||||
result.SetProvenanceStatus(string(template.Provenance)) |
||||
return result |
||||
} |
||||
|
||||
func convertToDomainModel(template *model.TemplateGroup) definitions.NotificationTemplate { |
||||
return definitions.NotificationTemplate{ |
||||
UID: template.ObjectMeta.Name, |
||||
Name: template.Spec.Title, |
||||
Template: template.Spec.Content, |
||||
ResourceVersion: template.ResourceVersion, |
||||
Provenance: definitions.Provenance(ngmodels.ProvenanceNone), |
||||
} |
||||
} |
@ -0,0 +1,187 @@ |
||||
package template_group |
||||
|
||||
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" |
||||
|
||||
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) |
||||
) |
||||
|
||||
type TemplateService interface { |
||||
GetTemplate(ctx context.Context, orgID int64, nameOrUid string) (definitions.NotificationTemplate, error) |
||||
GetTemplates(ctx context.Context, orgID int64) ([]definitions.NotificationTemplate, error) |
||||
CreateTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) |
||||
UpdateTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) |
||||
DeleteTemplate(ctx context.Context, orgID int64, nameOrUid string, provenance definitions.Provenance, version string) error |
||||
} |
||||
|
||||
var resourceInfo = notifications.TemplateGroupResourceInfo |
||||
|
||||
type legacyStorage struct { |
||||
service TemplateService |
||||
namespacer request.NamespaceMapper |
||||
tableConverter rest.TableConvertor |
||||
} |
||||
|
||||
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") |
||||
} |
||||
|
||||
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, opts *internalversion.ListOptions) (runtime.Object, error) { |
||||
orgId, err := request.OrgIDForList(ctx) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
res, err := s.service.GetTemplates(ctx, orgId) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return convertToK8sResources(orgId, res, s.namespacer, opts.FieldSelector) |
||||
} |
||||
|
||||
func (s *legacyStorage) Get(ctx context.Context, name string, _ *metav1.GetOptions) (runtime.Object, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
dto, err := s.service.GetTemplate(ctx, info.OrgID, name) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return convertToK8sResource(info.OrgID, dto, s.namespacer), nil |
||||
} |
||||
|
||||
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.TemplateGroup) |
||||
if !ok { |
||||
return nil, fmt.Errorf("expected template 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") |
||||
} |
||||
out, err := s.service.CreateTemplate(ctx, info.OrgID, convertToDomainModel(p)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return convertToK8sResource(info.OrgID, out, s.namespacer), nil |
||||
} |
||||
|
||||
func (s *legacyStorage) Update(ctx context.Context, |
||||
name 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 |
||||
} |
||||
|
||||
dto, err := s.service.GetTemplate(ctx, info.OrgID, name) |
||||
if err != nil { |
||||
return nil, false, err |
||||
} |
||||
old := convertToK8sResource(info.OrgID, dto, s.namespacer) |
||||
|
||||
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.TemplateGroup) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("expected template but got %s", obj.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
|
||||
domainModel := convertToDomainModel(p) |
||||
updated, err := s.service.UpdateTemplate(ctx, info.OrgID, domainModel) |
||||
if err != nil { |
||||
return nil, false, err |
||||
} |
||||
|
||||
r := convertToK8sResource(info.OrgID, updated, s.namespacer) |
||||
return r, false, nil |
||||
} |
||||
|
||||
// GracefulDeleter
|
||||
func (s *legacyStorage) Delete(ctx context.Context, name 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, name, nil) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
version := "" |
||||
if options.Preconditions != nil && options.Preconditions.ResourceVersion != nil { |
||||
version = *options.Preconditions.ResourceVersion |
||||
} |
||||
if deleteValidation != nil { |
||||
if err = deleteValidation(ctx, old); err != nil { |
||||
return nil, false, err |
||||
} |
||||
} |
||||
err = s.service.DeleteTemplate(ctx, info.OrgID, name, definitions.Provenance(models.ProvenanceNone), version) // TODO add support for dry-run option
|
||||
return old, false, err // false - will be deleted async
|
||||
} |
@ -0,0 +1,81 @@ |
||||
package template_group |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"k8s.io/apimachinery/pkg/fields" |
||||
"k8s.io/apimachinery/pkg/labels" |
||||
"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" |
||||
apistore "k8s.io/apiserver/pkg/storage" |
||||
|
||||
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" |
||||
) |
||||
|
||||
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 TemplateService, |
||||
namespacer request.NamespaceMapper, |
||||
scheme *runtime.Scheme, |
||||
optsGetter generic.RESTOptionsGetter, |
||||
dualWriteBuilder grafanarest.DualWriteBuilder, |
||||
) (rest.Storage, error) { |
||||
legacyStore := &legacyStorage{ |
||||
service: legacySvc, |
||||
namespacer: namespacer, |
||||
tableConverter: resourceInfo.TableConverter(), |
||||
} |
||||
if optsGetter != nil && dualWriteBuilder != nil { |
||||
strategy := grafanaregistry.NewStrategy(scheme, resourceInfo.GroupVersion()) |
||||
s := &genericregistry.Store{ |
||||
NewFunc: resourceInfo.NewFunc, |
||||
NewListFunc: resourceInfo.NewListFunc, |
||||
KeyRootFunc: grafanaregistry.KeyRootFunc(resourceInfo.GroupResource()), |
||||
KeyFunc: grafanaregistry.NamespaceKeyFunc(resourceInfo.GroupResource()), |
||||
PredicateFunc: Matcher, |
||||
DefaultQualifiedResource: resourceInfo.GroupResource(), |
||||
SingularQualifiedResource: resourceInfo.SingularGroupResource(), |
||||
TableConvertor: legacyStore.tableConverter, |
||||
CreateStrategy: strategy, |
||||
UpdateStrategy: strategy, |
||||
DeleteStrategy: strategy, |
||||
} |
||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs} |
||||
if err := s.CompleteWithOptions(options); err != nil { |
||||
return nil, err |
||||
} |
||||
return dualWriteBuilder(resourceInfo.GroupResource(), legacyStore, storage{Store: s}) |
||||
} |
||||
return legacyStore, nil |
||||
} |
||||
|
||||
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { |
||||
if s, ok := obj.(*model.TemplateGroup); ok { |
||||
return s.Labels, model.SelectableTemplateGroupFields(s), nil |
||||
} |
||||
return nil, nil, fmt.Errorf("object of type %T is not supported", obj) |
||||
} |
||||
|
||||
// Matcher returns a generic.SelectionPredicate that matches on label and field selectors.
|
||||
func Matcher(label labels.Selector, field fields.Selector) apistore.SelectionPredicate { |
||||
return apistore.SelectionPredicate{ |
||||
Label: label, |
||||
Field: field, |
||||
GetAttrs: GetAttrs, |
||||
} |
||||
} |
@ -0,0 +1,629 @@ |
||||
package templateGroup |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"fmt" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
"github.com/stretchr/testify/require" |
||||
"k8s.io/apimachinery/pkg/api/errors" |
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/types" |
||||
|
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
"github.com/grafana/grafana/pkg/generated/clientset/versioned" |
||||
"github.com/grafana/grafana/pkg/services/accesscontrol" |
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" |
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" |
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana" |
||||
"github.com/grafana/grafana/pkg/services/dashboards" |
||||
"github.com/grafana/grafana/pkg/services/featuremgmt" |
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" |
||||
"github.com/grafana/grafana/pkg/services/ngalert/store" |
||||
"github.com/grafana/grafana/pkg/services/org" |
||||
"github.com/grafana/grafana/pkg/tests/apis" |
||||
"github.com/grafana/grafana/pkg/tests/testinfra" |
||||
"github.com/grafana/grafana/pkg/tests/testsuite" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
func TestMain(m *testing.M) { |
||||
testsuite.Run(m) |
||||
} |
||||
|
||||
func getTestHelper(t *testing.T) *apis.K8sTestHelper { |
||||
return apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{ |
||||
EnableFeatureToggles: []string{ |
||||
featuremgmt.FlagAlertingApiServer, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationResourceIdentifier(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
client := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
newTemplate := &v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "templateGroup", |
||||
Content: `{{ define "test" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
|
||||
t.Run("create should fail if object name is specified", func(t *testing.T) { |
||||
template := newTemplate.DeepCopy() |
||||
template.Name = "new-templateGroup" |
||||
_, err := client.Create(ctx, template, v1.CreateOptions{}) |
||||
assert.Error(t, err) |
||||
require.Truef(t, errors.IsBadRequest(err), "Expected BadRequest but got %s", err) |
||||
}) |
||||
|
||||
var resourceID string |
||||
t.Run("create should succeed and provide resource name", func(t *testing.T) { |
||||
actual, err := client.Create(ctx, newTemplate, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
require.NotEmptyf(t, actual.Name, "Resource name should not be empty") |
||||
require.NotEmptyf(t, actual.UID, "Resource UID should not be empty") |
||||
resourceID = actual.Name |
||||
}) |
||||
|
||||
var existingTemplateGroup *v0alpha1.TemplateGroup |
||||
t.Run("resource should be available by the identifier", func(t *testing.T) { |
||||
actual, err := client.Get(ctx, resourceID, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
require.NotEmptyf(t, actual.Name, "Resource name should not be empty") |
||||
require.Equal(t, newTemplate.Spec, actual.Spec) |
||||
existingTemplateGroup = actual |
||||
}) |
||||
|
||||
t.Run("update should rename template if name in the specification changes", func(t *testing.T) { |
||||
if existingTemplateGroup == nil { |
||||
t.Skip() |
||||
} |
||||
updated := existingTemplateGroup.DeepCopy() |
||||
updated.Spec.Title = "another-templateGroup" |
||||
actual, err := client.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, updated.Spec, actual.Spec) |
||||
require.NotEqualf(t, updated.Name, actual.Name, "Update should change the resource name but it didn't") |
||||
|
||||
resource, err := client.Get(ctx, actual.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, actual, resource) |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationAccessControl(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
|
||||
org1 := helper.Org1 |
||||
|
||||
type testCase struct { |
||||
user apis.User |
||||
canRead bool |
||||
canUpdate bool |
||||
canCreate bool |
||||
canDelete bool |
||||
} |
||||
|
||||
reader := helper.CreateUser("TemplatesReader", apis.Org1, org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTemplatesRead, |
||||
}, |
||||
}, |
||||
}) |
||||
writer := helper.CreateUser("TemplatesWriter", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTemplatesRead, |
||||
accesscontrol.ActionAlertingNotificationsTemplatesWrite, |
||||
}, |
||||
}, |
||||
}) |
||||
|
||||
deleter := helper.CreateUser("TemplatesDeleter", apis.Org1, org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTemplatesRead, |
||||
accesscontrol.ActionAlertingNotificationsTemplatesDelete, |
||||
}, |
||||
}, |
||||
}) |
||||
|
||||
testCases := []testCase{ |
||||
{ |
||||
user: org1.Admin, |
||||
canRead: true, |
||||
canUpdate: true, |
||||
canCreate: true, |
||||
canDelete: true, |
||||
}, |
||||
{ |
||||
user: org1.Editor, |
||||
canRead: true, |
||||
canUpdate: true, |
||||
canCreate: true, |
||||
canDelete: true, |
||||
}, |
||||
{ |
||||
user: org1.Viewer, |
||||
canRead: true, |
||||
}, |
||||
{ |
||||
user: reader, |
||||
canRead: true, |
||||
}, |
||||
{ |
||||
user: writer, |
||||
canRead: true, |
||||
canCreate: true, |
||||
canUpdate: true, |
||||
}, |
||||
{ |
||||
user: deleter, |
||||
canRead: true, |
||||
canDelete: true, |
||||
}, |
||||
} |
||||
|
||||
admin := org1.Admin |
||||
adminK8sClient, err := versioned.NewForConfig(admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
for _, tc := range testCases { |
||||
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) { |
||||
k8sClient, err := versioned.NewForConfig(tc.user.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
client := k8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
var expected = &v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: fmt.Sprintf("template-group-1-%s", tc.user.Identity.GetLogin()), |
||||
Content: `{{ define "test" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
expected.SetProvenanceStatus("") |
||||
d, err := json.Marshal(expected) |
||||
require.NoError(t, err) |
||||
|
||||
if tc.canCreate { |
||||
t.Run("should be able to create template group", func(t *testing.T) { |
||||
actual, err := client.Create(ctx, expected, v1.CreateOptions{}) |
||||
require.NoErrorf(t, err, "Payload %s", string(d)) |
||||
require.Equal(t, expected.Spec, actual.Spec) |
||||
|
||||
t.Run("should fail if already exists", func(t *testing.T) { |
||||
_, err := client.Create(ctx, actual, v1.CreateOptions{}) |
||||
require.Truef(t, errors.IsBadRequest(err), "expected bad request but got %s", err) |
||||
}) |
||||
|
||||
expected = actual |
||||
}) |
||||
} else { |
||||
t.Run("should be forbidden to create", func(t *testing.T) { |
||||
_, err := client.Create(ctx, expected, v1.CreateOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "Payload %s", string(d)) |
||||
}) |
||||
|
||||
// create resource to proceed with other tests
|
||||
expected, err = adminClient.Create(ctx, expected, v1.CreateOptions{}) |
||||
require.NoErrorf(t, err, "Payload %s", string(d)) |
||||
require.NotNil(t, expected) |
||||
} |
||||
|
||||
if tc.canRead { |
||||
t.Run("should be able to list template groups", func(t *testing.T) { |
||||
list, err := client.List(ctx, v1.ListOptions{}) |
||||
require.NoError(t, err) |
||||
require.Len(t, list.Items, 1) |
||||
}) |
||||
|
||||
t.Run("should be able to read template group by resource identifier", func(t *testing.T) { |
||||
got, err := client.Get(ctx, expected.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, expected, got) |
||||
|
||||
t.Run("should get NotFound if resource does not exist", func(t *testing.T) { |
||||
_, err := client.Get(ctx, "Notfound", v1.GetOptions{}) |
||||
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err) |
||||
}) |
||||
}) |
||||
} else { |
||||
t.Run("should be forbidden to list template groups", func(t *testing.T) { |
||||
_, err := client.List(ctx, v1.ListOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
|
||||
t.Run("should be forbidden to read template group by name", func(t *testing.T) { |
||||
_, err := client.Get(ctx, expected.Name, v1.GetOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
|
||||
t.Run("should get forbidden even if name does not exist", func(t *testing.T) { |
||||
_, err := client.Get(ctx, "Notfound", v1.GetOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
updatedExpected := expected.DeepCopy() |
||||
updatedExpected.Spec.Content = `{{ define "another-test" }} test {{ end }}` |
||||
|
||||
d, err = json.Marshal(updatedExpected) |
||||
require.NoError(t, err) |
||||
|
||||
if tc.canUpdate { |
||||
t.Run("should be able to update template group", func(t *testing.T) { |
||||
updated, err := client.Update(ctx, updatedExpected, v1.UpdateOptions{}) |
||||
require.NoErrorf(t, err, "Payload %s", string(d)) |
||||
|
||||
expected = updated |
||||
|
||||
t.Run("should get NotFound if name does not exist", func(t *testing.T) { |
||||
up := updatedExpected.DeepCopy() |
||||
up.Name = "notFound" |
||||
_, err := client.Update(ctx, up, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err) |
||||
}) |
||||
}) |
||||
} else { |
||||
t.Run("should be forbidden to update template group", func(t *testing.T) { |
||||
_, err := client.Update(ctx, updatedExpected, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
|
||||
t.Run("should get forbidden even if resource does not exist", func(t *testing.T) { |
||||
up := updatedExpected.DeepCopy() |
||||
up.Name = "notFound" |
||||
_, err := client.Update(ctx, up, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
deleteOptions := v1.DeleteOptions{Preconditions: &v1.Preconditions{ResourceVersion: util.Pointer(expected.ResourceVersion)}} |
||||
|
||||
if tc.canDelete { |
||||
t.Run("should be able to delete template group", func(t *testing.T) { |
||||
err := client.Delete(ctx, expected.Name, deleteOptions) |
||||
require.NoError(t, err) |
||||
|
||||
t.Run("should get NotFound if name does not exist", func(t *testing.T) { |
||||
err := client.Delete(ctx, "notfound", v1.DeleteOptions{}) |
||||
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err) |
||||
}) |
||||
}) |
||||
} else { |
||||
t.Run("should be forbidden to delete template group", func(t *testing.T) { |
||||
err := client.Delete(ctx, expected.Name, deleteOptions) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
|
||||
t.Run("should be forbidden even if resource does not exist", func(t *testing.T) { |
||||
err := client.Delete(ctx, "notfound", v1.DeleteOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
}) |
||||
require.NoError(t, adminClient.Delete(ctx, expected.Name, v1.DeleteOptions{})) |
||||
} |
||||
|
||||
if tc.canRead { |
||||
t.Run("should get empty list if no mute timings", func(t *testing.T) { |
||||
list, err := client.List(ctx, v1.ListOptions{}) |
||||
require.NoError(t, err) |
||||
require.Len(t, list.Items, 0) |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestIntegrationProvisioning(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
|
||||
org := helper.Org1 |
||||
|
||||
admin := org.Admin |
||||
adminK8sClient, err := versioned.NewForConfig(admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
env := helper.GetEnv() |
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles, zanzana.NewNoopClient()) |
||||
db, err := store.ProvideDBStore(env.Cfg, env.FeatureToggles, env.SQLStore, &foldertest.FakeService{}, &dashboards.FakeDashboardService{}, ac) |
||||
require.NoError(t, err) |
||||
|
||||
created, err := adminClient.Create(ctx, &v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "template-group-1", |
||||
Content: `{{ define "test" }} test {{ end }}`, |
||||
}, |
||||
}, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "none", created.GetProvenanceStatus()) |
||||
|
||||
t.Run("should provide provenance status", func(t *testing.T) { |
||||
require.NoError(t, db.SetProvenance(ctx, &definitions.NotificationTemplate{ |
||||
Name: created.Spec.Title, |
||||
}, admin.Identity.GetOrgID(), "API")) |
||||
|
||||
got, err := adminClient.Get(ctx, created.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "API", got.GetProvenanceStatus()) |
||||
}) |
||||
t.Run("should not let update if provisioned", func(t *testing.T) { |
||||
updated := created.DeepCopy() |
||||
updated.Spec.Content = `{{ define "another-test" }} test {{ end }}` |
||||
|
||||
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
|
||||
t.Run("should not let delete if provisioned", func(t *testing.T) { |
||||
err := adminClient.Delete(ctx, created.Name, v1.DeleteOptions{}) |
||||
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationOptimisticConcurrency(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
|
||||
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
template := v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "template-group-1", |
||||
Content: `{{ define "test" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
|
||||
created, err := adminClient.Create(ctx, &template, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
require.NotNil(t, created) |
||||
require.NotEmpty(t, created.ResourceVersion) |
||||
|
||||
t.Run("should forbid if version does not match", func(t *testing.T) { |
||||
updated := created.DeepCopy() |
||||
updated.ResourceVersion = "test" |
||||
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsConflict(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
t.Run("should update if version matches", func(t *testing.T) { |
||||
updated := created.DeepCopy() |
||||
updated.Spec.Content = `{{ define "test-another" }} test {{ end }}` |
||||
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.NoError(t, err) |
||||
require.EqualValues(t, updated.Spec, actualUpdated.Spec) |
||||
require.NotEqual(t, updated.ResourceVersion, actualUpdated.ResourceVersion) |
||||
}) |
||||
t.Run("should update if version is empty", func(t *testing.T) { |
||||
updated := created.DeepCopy() |
||||
updated.ResourceVersion = "" |
||||
updated.Spec.Content = `{{ define "test-another-2" }} test {{ end }}` |
||||
|
||||
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.NoError(t, err) |
||||
require.EqualValues(t, updated.Spec, actualUpdated.Spec) |
||||
require.NotEqual(t, created.ResourceVersion, actualUpdated.ResourceVersion) |
||||
}) |
||||
t.Run("should fail to delete if version does not match", func(t *testing.T) { |
||||
actual, err := adminClient.Get(ctx, created.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
|
||||
err = adminClient.Delete(ctx, actual.Name, v1.DeleteOptions{ |
||||
Preconditions: &v1.Preconditions{ |
||||
ResourceVersion: util.Pointer("something"), |
||||
}, |
||||
}) |
||||
require.Truef(t, errors.IsConflict(err), "should get Forbidden error but got %s", err) |
||||
}) |
||||
t.Run("should succeed if version matches", func(t *testing.T) { |
||||
actual, err := adminClient.Get(ctx, created.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
|
||||
err = adminClient.Delete(ctx, actual.Name, v1.DeleteOptions{ |
||||
Preconditions: &v1.Preconditions{ |
||||
ResourceVersion: util.Pointer(actual.ResourceVersion), |
||||
}, |
||||
}) |
||||
require.NoError(t, err) |
||||
}) |
||||
t.Run("should succeed if version is empty", func(t *testing.T) { |
||||
actual, err := adminClient.Create(ctx, &template, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
|
||||
err = adminClient.Delete(ctx, actual.Name, v1.DeleteOptions{ |
||||
Preconditions: &v1.Preconditions{ |
||||
ResourceVersion: util.Pointer(actual.ResourceVersion), |
||||
}, |
||||
}) |
||||
require.NoError(t, err) |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationPatch(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
|
||||
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
template := v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "template-group", |
||||
Content: `{{ define "test" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
|
||||
current, err := adminClient.Create(ctx, &template, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
require.NotNil(t, current) |
||||
require.NotEmpty(t, current.ResourceVersion) |
||||
|
||||
t.Run("should patch with merge patch", func(t *testing.T) { |
||||
patch := `{ |
||||
"spec": { |
||||
"content" : "{{ define \"test-another\" }} test {{ end }}" |
||||
} |
||||
}` |
||||
|
||||
result, err := adminClient.Patch(ctx, current.Name, types.MergePatchType, []byte(patch), v1.PatchOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, `{{ define "test-another" }} test {{ end }}`, result.Spec.Content) |
||||
current = result |
||||
}) |
||||
|
||||
t.Run("should patch with json patch", func(t *testing.T) { |
||||
expected := `{{ define "test-json-patch" }} test {{ end }}` |
||||
|
||||
patch := []map[string]interface{}{ |
||||
{ |
||||
"op": "replace", |
||||
"path": "/spec/content", |
||||
"value": expected, |
||||
}, |
||||
} |
||||
|
||||
patchData, err := json.Marshal(patch) |
||||
require.NoError(t, err) |
||||
|
||||
result, err := adminClient.Patch(ctx, current.Name, types.JSONPatchType, patchData, v1.PatchOptions{}) |
||||
require.NoError(t, err) |
||||
expectedSpec := *current.Spec.DeepCopy() |
||||
expectedSpec.Content = expected |
||||
require.EqualValues(t, expectedSpec, result.Spec) |
||||
current = result |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationListSelector(t *testing.T) { |
||||
if testing.Short() { |
||||
t.Skip("skipping integration test") |
||||
} |
||||
|
||||
ctx := context.Background() |
||||
helper := getTestHelper(t) |
||||
|
||||
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig()) |
||||
require.NoError(t, err) |
||||
adminClient := adminK8sClient.NotificationsV0alpha1().TemplateGroups("default") |
||||
|
||||
template1 := &v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "test1", |
||||
Content: `{{ define "test1" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
template1, err = adminClient.Create(ctx, template1, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
|
||||
template2 := &v0alpha1.TemplateGroup{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TemplateGroupSpec{ |
||||
Title: "test2", |
||||
Content: `{{ define "test2" }} test {{ end }}`, |
||||
}, |
||||
} |
||||
template2, err = adminClient.Create(ctx, template2, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
env := helper.GetEnv() |
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles, zanzana.NewNoopClient()) |
||||
db, err := store.ProvideDBStore(env.Cfg, env.FeatureToggles, env.SQLStore, &foldertest.FakeService{}, &dashboards.FakeDashboardService{}, ac) |
||||
require.NoError(t, err) |
||||
require.NoError(t, db.SetProvenance(ctx, &definitions.NotificationTemplate{ |
||||
Name: template2.Spec.Title, |
||||
}, helper.Org1.Admin.Identity.GetOrgID(), "API")) |
||||
template2, err = adminClient.Get(ctx, template2.Name, v1.GetOptions{}) |
||||
|
||||
require.NoError(t, err) |
||||
|
||||
templates, err := adminClient.List(ctx, v1.ListOptions{}) |
||||
require.NoError(t, err) |
||||
require.Len(t, templates.Items, 2) |
||||
|
||||
t.Run("should filter by template name", func(t *testing.T) { |
||||
list, err := adminClient.List(ctx, v1.ListOptions{ |
||||
FieldSelector: "spec.title=" + template1.Spec.Title, |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Len(t, list.Items, 1) |
||||
require.Equal(t, template1.Name, list.Items[0].Name) |
||||
}) |
||||
|
||||
t.Run("should filter by template metadata name", func(t *testing.T) { |
||||
list, err := adminClient.List(ctx, v1.ListOptions{ |
||||
FieldSelector: "metadata.name=" + template2.Name, |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Len(t, list.Items, 1) |
||||
require.Equal(t, template2.Name, list.Items[0].Name) |
||||
}) |
||||
|
||||
t.Run("should filter by multiple filters", func(t *testing.T) { |
||||
list, err := adminClient.List(ctx, v1.ListOptions{ |
||||
FieldSelector: fmt.Sprintf("metadata.name=%s,metadata.provenance=%s", template2.Name, "API"), |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Len(t, list.Items, 1) |
||||
require.Equal(t, template2.Name, list.Items[0].Name) |
||||
}) |
||||
|
||||
t.Run("should be empty when filter does not match", func(t *testing.T) { |
||||
list, err := adminClient.List(ctx, v1.ListOptions{ |
||||
FieldSelector: fmt.Sprintf("metadata.name=%s,metadata.provenance=%s", template2.Name, "unknown"), |
||||
}) |
||||
require.NoError(t, err) |
||||
require.Empty(t, list.Items) |
||||
}) |
||||
} |
Loading…
Reference in new issue