mirror of https://github.com/grafana/grafana
Alerting: Time Intervals API (#88201)
* expose ngalert API to public * add delete action to time-intervals * introduce time-interval model generated by app-platform-sdk from CUE model the fields of the model are chosen to be compatible with the current model * implement api server * add feature flag alertingApiServer ---- Test Infra * update helper to support creating custom users with enterprise permissions * add generator for Interval modelpull/89522/head
parent
3228b64fe6
commit
b075926202
@ -0,0 +1 @@ |
||||
module:"notifications" |
@ -0,0 +1,37 @@ |
||||
package core |
||||
|
||||
timeInterval: { |
||||
kind: "TimeInterval" |
||||
group: "notifications" |
||||
apiResource: { |
||||
groupOverride: "notifications.alerting.grafana.app" |
||||
} |
||||
codegen: { |
||||
frontend: false |
||||
backend: true |
||||
} |
||||
pluralName: "TimeIntervals" |
||||
current: "v0alpha1" |
||||
versions: { |
||||
"v0alpha1": { |
||||
schema: { |
||||
#TimeRange: { |
||||
start_time: string |
||||
end_time: string |
||||
} |
||||
#Interval: { |
||||
times?: [...#TimeRange] |
||||
weekdays?: [...string] |
||||
days_of_month?: [...string] |
||||
months?: [...string] |
||||
years?: [...string] |
||||
location?: string |
||||
} |
||||
spec: { |
||||
name: string |
||||
time_intervals: [...#Interval] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=notifications.alerting.grafana.app
|
||||
|
||||
package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1"
|
@ -0,0 +1,53 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" |
||||
) |
||||
|
||||
func init() { |
||||
localSchemeBuilder.Register(AddKnownTypes) |
||||
} |
||||
|
||||
const ( |
||||
GROUP = "notifications.alerting.grafana.app" |
||||
VERSION = "v0alpha1" |
||||
APIVERSION = GROUP + "/" + VERSION |
||||
) |
||||
|
||||
var ( |
||||
TimeIntervalResourceInfo = common.NewResourceInfo(GROUP, VERSION, |
||||
"timeintervals", "timeinterval", "TimeIntervals", |
||||
func() runtime.Object { return &TimeInterval{} }, |
||||
func() runtime.Object { return &TimeIntervalList{} }, |
||||
) |
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION} |
||||
// SchemaBuilder is used by standard codegen
|
||||
SchemeBuilder runtime.SchemeBuilder |
||||
localSchemeBuilder = &SchemeBuilder |
||||
AddToScheme = localSchemeBuilder.AddToScheme |
||||
) |
||||
|
||||
// Adds the list of known types to the given scheme.
|
||||
func AddKnownTypes(scheme *runtime.Scheme) error { |
||||
return AddKnownTypesGroup(scheme, SchemeGroupVersion) |
||||
} |
||||
|
||||
// Adds the list of known types to the given scheme and group version.
|
||||
func AddKnownTypesGroup(scheme *runtime.Scheme, g schema.GroupVersion) error { |
||||
scheme.AddKnownTypes(g, |
||||
&TimeInterval{}, |
||||
&TimeIntervalList{}, |
||||
) |
||||
metav1.AddToGroupVersion(scheme, g) |
||||
return nil |
||||
} |
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource { |
||||
return SchemeGroupVersion.WithResource(resource).GroupResource() |
||||
} |
@ -0,0 +1,115 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/rand" |
||||
"slices" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
// +k8s:openapi-gen=false
|
||||
// +k8s:deepcopy-gen=false
|
||||
type IntervalMutator func(spec *Interval) |
||||
|
||||
// +k8s:openapi-gen=false
|
||||
// +k8s:deepcopy-gen=false
|
||||
type IntervalGenerator struct { |
||||
mutators []IntervalMutator |
||||
} |
||||
|
||||
func (t IntervalGenerator) With(mutators ...IntervalMutator) IntervalGenerator { |
||||
return IntervalGenerator{ |
||||
mutators: append(t.mutators, mutators...), |
||||
} |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateDaysOfMonth() string { |
||||
isRange := rand.Int()%2 == 0 |
||||
if !isRange { |
||||
return fmt.Sprintf("%d", rand.Intn(30)+1) |
||||
} |
||||
from := rand.Intn(15) + 1 |
||||
to := rand.Intn(31-from) + from + 1 |
||||
return fmt.Sprintf("%d:%d", from, to) |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateTimeRange() TimeRange { |
||||
from := rand.Int63n(1440 / 2) // [0, 719]
|
||||
to := from + rand.Int63n(1440/2) + 1 // from < ([0,719] + [1,720]) < 1440
|
||||
return TimeRange{ |
||||
StartTime: time.Unix(from*60, 0).UTC().Format("15:04"), |
||||
EndTime: time.Unix(to*60, 0).UTC().Format("15:04"), |
||||
} |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateWeekday() string { |
||||
day := rand.Intn(7) |
||||
return strings.ToLower(time.Weekday(day).String()) |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateYear() string { |
||||
from := 1970 + rand.Intn(100) |
||||
if rand.Int()%3 == 0 { |
||||
to := 1970 + from + rand.Intn(10) + 1 |
||||
return fmt.Sprintf("%d:%d", from, to) |
||||
} |
||||
return fmt.Sprintf("%d", from) |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateLocation() *string { |
||||
if rand.Int()%3 == 0 { |
||||
return nil |
||||
} |
||||
return util.Pointer("UTC") |
||||
} |
||||
|
||||
func (t IntervalGenerator) generateMonth() string { |
||||
return fmt.Sprintf("%d", rand.Intn(12)+1) |
||||
} |
||||
|
||||
func (t IntervalGenerator) GenerateMany(count int) []Interval { |
||||
result := make([]Interval, 0, count) |
||||
for i := 0; i < count; i++ { |
||||
result = append(result, t.Generate()) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
func (t IntervalGenerator) Generate() Interval { |
||||
i := Interval{ |
||||
DaysOfMonth: generateMany(rand.Intn(6), true, t.generateDaysOfMonth), |
||||
Location: t.generateLocation(), |
||||
Months: generateMany(rand.Intn(3), true, t.generateMonth), |
||||
Times: generateMany(rand.Intn(6), true, t.generateTimeRange), |
||||
Weekdays: generateMany(rand.Intn(3), true, t.generateWeekday), |
||||
Years: generateMany(rand.Intn(3), true, t.generateYear), |
||||
} |
||||
for _, mutator := range t.mutators { |
||||
mutator(&i) |
||||
} |
||||
return i |
||||
} |
||||
|
||||
func generateMany[T comparable](repeatTimes int, unique bool, f func() T) []T { |
||||
qty := repeatTimes + 1 |
||||
result := make([]T, 0, qty) |
||||
for i := 0; i < qty; i++ { |
||||
r := f() |
||||
if unique && slices.Contains(result, r) { |
||||
continue |
||||
} |
||||
result = append(result, f()) |
||||
} |
||||
return result |
||||
} |
||||
|
||||
func CopyWith(in Interval, mutators ...IntervalMutator) Interval { |
||||
r := *in.DeepCopy() |
||||
for _, mut := range mutators { |
||||
mut(&r) |
||||
} |
||||
return r |
||||
} |
@ -0,0 +1,38 @@ |
||||
package v0alpha1 |
||||
|
||||
// Interval defines model for Interval.
|
||||
// +k8s:openapi-gen=true
|
||||
type Interval struct { |
||||
// +listType=atomic
|
||||
DaysOfMonth []string `json:"days_of_month,omitempty"` |
||||
|
||||
// +listType=atomic
|
||||
Location *string `json:"location,omitempty"` |
||||
|
||||
// +listType=atomic
|
||||
Months []string `json:"months,omitempty"` |
||||
|
||||
// +listType=atomic
|
||||
Times []TimeRange `json:"times,omitempty"` |
||||
|
||||
// +listType=atomic
|
||||
Weekdays []string `json:"weekdays,omitempty"` |
||||
|
||||
// +listType=atomic
|
||||
Years []string `json:"years,omitempty"` |
||||
} |
||||
|
||||
// Spec defines model for Spec.
|
||||
// +k8s:openapi-gen=true
|
||||
type TimeIntervalSpec struct { |
||||
Name string `json:"name"` |
||||
// +listType=atomic
|
||||
TimeIntervals []Interval `json:"time_intervals"` |
||||
} |
||||
|
||||
// TimeRange defines model for TimeRange.
|
||||
// +k8s:openapi-gen=true
|
||||
type TimeRange struct { |
||||
EndTime string `json:"end_time"` |
||||
StartTime string `json:"start_time"` |
||||
} |
@ -0,0 +1,87 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"fmt" |
||||
"time" |
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
) |
||||
|
||||
// +genclient
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type TimeInterval struct { |
||||
metav1.TypeMeta `json:",inline"` |
||||
metav1.ObjectMeta `json:"metadata"` |
||||
Spec TimeIntervalSpec `json:"spec"` |
||||
} |
||||
|
||||
func (o *TimeInterval) GetSpec() any { |
||||
return o.Spec |
||||
} |
||||
|
||||
func (o *TimeInterval) SetSpec(spec any) error { |
||||
cast, ok := spec.(TimeIntervalSpec) |
||||
if !ok { |
||||
return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) |
||||
} |
||||
o.Spec = cast |
||||
return nil |
||||
} |
||||
|
||||
func (o *TimeInterval) GetCreatedBy() string { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/createdBy"] |
||||
} |
||||
|
||||
func (o *TimeInterval) SetCreatedBy(createdBy string) { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy |
||||
} |
||||
|
||||
func (o *TimeInterval) GetUpdateTimestamp() time.Time { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) |
||||
return parsed |
||||
} |
||||
|
||||
func (o *TimeInterval) SetUpdateTimestamp(updateTimestamp time.Time) { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) |
||||
} |
||||
|
||||
func (o *TimeInterval) GetUpdatedBy() string { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/updatedBy"] |
||||
} |
||||
|
||||
func (o *TimeInterval) SetUpdatedBy(updatedBy string) { |
||||
if o.ObjectMeta.Annotations == nil { |
||||
o.ObjectMeta.Annotations = make(map[string]string) |
||||
} |
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy |
||||
} |
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:openapi-gen=true
|
||||
type TimeIntervalList struct { |
||||
metav1.TypeMeta `json:",inline"` |
||||
metav1.ListMeta `json:"metadata"` |
||||
Items []TimeInterval `json:"items"` |
||||
} |
@ -0,0 +1,157 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
runtime "k8s.io/apimachinery/pkg/runtime" |
||||
) |
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Interval) DeepCopyInto(out *Interval) { |
||||
*out = *in |
||||
if in.DaysOfMonth != nil { |
||||
in, out := &in.DaysOfMonth, &out.DaysOfMonth |
||||
*out = make([]string, len(*in)) |
||||
copy(*out, *in) |
||||
} |
||||
if in.Location != nil { |
||||
in, out := &in.Location, &out.Location |
||||
*out = new(string) |
||||
**out = **in |
||||
} |
||||
if in.Months != nil { |
||||
in, out := &in.Months, &out.Months |
||||
*out = make([]string, len(*in)) |
||||
copy(*out, *in) |
||||
} |
||||
if in.Times != nil { |
||||
in, out := &in.Times, &out.Times |
||||
*out = make([]TimeRange, len(*in)) |
||||
copy(*out, *in) |
||||
} |
||||
if in.Weekdays != nil { |
||||
in, out := &in.Weekdays, &out.Weekdays |
||||
*out = make([]string, len(*in)) |
||||
copy(*out, *in) |
||||
} |
||||
if in.Years != nil { |
||||
in, out := &in.Years, &out.Years |
||||
*out = make([]string, len(*in)) |
||||
copy(*out, *in) |
||||
} |
||||
return |
||||
} |
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Interval.
|
||||
func (in *Interval) DeepCopy() *Interval { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
out := new(Interval) |
||||
in.DeepCopyInto(out) |
||||
return out |
||||
} |
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TimeInterval) DeepCopyInto(out *TimeInterval) { |
||||
*out = *in |
||||
out.TypeMeta = in.TypeMeta |
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) |
||||
in.Spec.DeepCopyInto(&out.Spec) |
||||
return |
||||
} |
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeInterval.
|
||||
func (in *TimeInterval) DeepCopy() *TimeInterval { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
out := new(TimeInterval) |
||||
in.DeepCopyInto(out) |
||||
return out |
||||
} |
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TimeInterval) DeepCopyObject() runtime.Object { |
||||
if c := in.DeepCopy(); c != nil { |
||||
return c |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TimeIntervalList) DeepCopyInto(out *TimeIntervalList) { |
||||
*out = *in |
||||
out.TypeMeta = in.TypeMeta |
||||
in.ListMeta.DeepCopyInto(&out.ListMeta) |
||||
if in.Items != nil { |
||||
in, out := &in.Items, &out.Items |
||||
*out = make([]TimeInterval, len(*in)) |
||||
for i := range *in { |
||||
(*in)[i].DeepCopyInto(&(*out)[i]) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeIntervalList.
|
||||
func (in *TimeIntervalList) DeepCopy() *TimeIntervalList { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
out := new(TimeIntervalList) |
||||
in.DeepCopyInto(out) |
||||
return out |
||||
} |
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TimeIntervalList) DeepCopyObject() runtime.Object { |
||||
if c := in.DeepCopy(); c != nil { |
||||
return c |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TimeIntervalSpec) DeepCopyInto(out *TimeIntervalSpec) { |
||||
*out = *in |
||||
if in.TimeIntervals != nil { |
||||
in, out := &in.TimeIntervals, &out.TimeIntervals |
||||
*out = make([]Interval, len(*in)) |
||||
for i := range *in { |
||||
(*in)[i].DeepCopyInto(&(*out)[i]) |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeIntervalSpec.
|
||||
func (in *TimeIntervalSpec) DeepCopy() *TimeIntervalSpec { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
out := new(TimeIntervalSpec) |
||||
in.DeepCopyInto(out) |
||||
return out |
||||
} |
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TimeRange) DeepCopyInto(out *TimeRange) { |
||||
*out = *in |
||||
return |
||||
} |
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeRange.
|
||||
func (in *TimeRange) DeepCopy() *TimeRange { |
||||
if in == nil { |
||||
return nil |
||||
} |
||||
out := new(TimeRange) |
||||
in.DeepCopyInto(out) |
||||
return out |
||||
} |
@ -0,0 +1,19 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
runtime "k8s.io/apimachinery/pkg/runtime" |
||||
) |
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error { |
||||
return nil |
||||
} |
@ -0,0 +1,303 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
common "k8s.io/kube-openapi/pkg/common" |
||||
spec "k8s.io/kube-openapi/pkg/validation/spec" |
||||
) |
||||
|
||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { |
||||
return map[string]common.OpenAPIDefinition{ |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.Interval": schema_pkg_apis_alerting_notifications_v0alpha1_Interval(ref), |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeInterval": schema_pkg_apis_alerting_notifications_v0alpha1_TimeInterval(ref), |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeIntervalList": schema_pkg_apis_alerting_notifications_v0alpha1_TimeIntervalList(ref), |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeIntervalSpec": schema_pkg_apis_alerting_notifications_v0alpha1_TimeIntervalSpec(ref), |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeRange": schema_pkg_apis_alerting_notifications_v0alpha1_TimeRange(ref), |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_alerting_notifications_v0alpha1_Interval(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Interval defines model for Interval.", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"days_of_month": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
"location": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"months": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
"times": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeRange"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
"weekdays": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
"years": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeRange"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_alerting_notifications_v0alpha1_TimeInterval(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"metadata": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), |
||||
}, |
||||
}, |
||||
"spec": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeIntervalSpec"), |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"metadata", "spec"}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeIntervalSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_alerting_notifications_v0alpha1_TimeIntervalList(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"metadata": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), |
||||
}, |
||||
}, |
||||
"items": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeInterval"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"metadata", "items"}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.TimeInterval", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_alerting_notifications_v0alpha1_TimeIntervalSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Spec defines model for Spec.", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"name": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"time_intervals": { |
||||
VendorExtensible: spec.VendorExtensible{ |
||||
Extensions: spec.Extensions{ |
||||
"x-kubernetes-list-type": "atomic", |
||||
}, |
||||
}, |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.Interval"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"name", "time_intervals"}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1.Interval"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_alerting_notifications_v0alpha1_TimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "TimeRange defines model for TimeRange.", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"end_time": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"start_time": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: "", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
Required: []string{"end_time", "start_time"}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
@ -0,0 +1,4 @@ |
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1,Interval,DaysOfMonth |
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1,TimeIntervalSpec,TimeIntervals |
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1,TimeRange,EndTime |
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1,TimeRange,StartTime |
@ -0,0 +1,83 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// IntervalApplyConfiguration represents an declarative configuration of the Interval type for use
|
||||
// with apply.
|
||||
type IntervalApplyConfiguration struct { |
||||
DaysOfMonth []string `json:"days_of_month,omitempty"` |
||||
Location *string `json:"location,omitempty"` |
||||
Months []string `json:"months,omitempty"` |
||||
Times []TimeRangeApplyConfiguration `json:"times,omitempty"` |
||||
Weekdays []string `json:"weekdays,omitempty"` |
||||
Years []string `json:"years,omitempty"` |
||||
} |
||||
|
||||
// IntervalApplyConfiguration constructs an declarative configuration of the Interval type for use with
|
||||
// apply.
|
||||
func Interval() *IntervalApplyConfiguration { |
||||
return &IntervalApplyConfiguration{} |
||||
} |
||||
|
||||
// WithDaysOfMonth adds the given value to the DaysOfMonth 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 DaysOfMonth field.
|
||||
func (b *IntervalApplyConfiguration) WithDaysOfMonth(values ...string) *IntervalApplyConfiguration { |
||||
for i := range values { |
||||
b.DaysOfMonth = append(b.DaysOfMonth, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithLocation sets the Location 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 Location field is set to the value of the last call.
|
||||
func (b *IntervalApplyConfiguration) WithLocation(value string) *IntervalApplyConfiguration { |
||||
b.Location = &value |
||||
return b |
||||
} |
||||
|
||||
// WithMonths adds the given value to the Months 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 Months field.
|
||||
func (b *IntervalApplyConfiguration) WithMonths(values ...string) *IntervalApplyConfiguration { |
||||
for i := range values { |
||||
b.Months = append(b.Months, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithTimes adds the given value to the Times 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 Times field.
|
||||
func (b *IntervalApplyConfiguration) WithTimes(values ...*TimeRangeApplyConfiguration) *IntervalApplyConfiguration { |
||||
for i := range values { |
||||
if values[i] == nil { |
||||
panic("nil value passed to WithTimes") |
||||
} |
||||
b.Times = append(b.Times, *values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithWeekdays adds the given value to the Weekdays 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 Weekdays field.
|
||||
func (b *IntervalApplyConfiguration) WithWeekdays(values ...string) *IntervalApplyConfiguration { |
||||
for i := range values { |
||||
b.Weekdays = append(b.Weekdays, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
// WithYears adds the given value to the Years 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 Years field.
|
||||
func (b *IntervalApplyConfiguration) WithYears(values ...string) *IntervalApplyConfiguration { |
||||
for i := range values { |
||||
b.Years = append(b.Years, values[i]) |
||||
} |
||||
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" |
||||
) |
||||
|
||||
// TimeIntervalApplyConfiguration represents an declarative configuration of the TimeInterval type for use
|
||||
// with apply.
|
||||
type TimeIntervalApplyConfiguration struct { |
||||
v1.TypeMetaApplyConfiguration `json:",inline"` |
||||
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` |
||||
Spec *TimeIntervalSpecApplyConfiguration `json:"spec,omitempty"` |
||||
} |
||||
|
||||
// TimeInterval constructs an declarative configuration of the TimeInterval type for use with
|
||||
// apply.
|
||||
func TimeInterval(name, namespace string) *TimeIntervalApplyConfiguration { |
||||
b := &TimeIntervalApplyConfiguration{} |
||||
b.WithName(name) |
||||
b.WithNamespace(namespace) |
||||
b.WithKind("TimeInterval") |
||||
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 *TimeIntervalApplyConfiguration) WithKind(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithAPIVersion(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithName(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithGenerateName(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithNamespace(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithUID(value types.UID) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithResourceVersion(value string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithGeneration(value int64) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithLabels(entries map[string]string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithAnnotations(entries map[string]string) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TimeIntervalApplyConfiguration { |
||||
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 *TimeIntervalApplyConfiguration) WithFinalizers(values ...string) *TimeIntervalApplyConfiguration { |
||||
b.ensureObjectMetaApplyConfigurationExists() |
||||
for i := range values { |
||||
b.Finalizers = append(b.Finalizers, values[i]) |
||||
} |
||||
return b |
||||
} |
||||
|
||||
func (b *TimeIntervalApplyConfiguration) 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 *TimeIntervalApplyConfiguration) WithSpec(value *TimeIntervalSpecApplyConfiguration) *TimeIntervalApplyConfiguration { |
||||
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 |
||||
|
||||
// TimeIntervalSpecApplyConfiguration represents an declarative configuration of the TimeIntervalSpec type for use
|
||||
// with apply.
|
||||
type TimeIntervalSpecApplyConfiguration struct { |
||||
Name *string `json:"name,omitempty"` |
||||
TimeIntervals []IntervalApplyConfiguration `json:"time_intervals,omitempty"` |
||||
} |
||||
|
||||
// TimeIntervalSpecApplyConfiguration constructs an declarative configuration of the TimeIntervalSpec type for use with
|
||||
// apply.
|
||||
func TimeIntervalSpec() *TimeIntervalSpecApplyConfiguration { |
||||
return &TimeIntervalSpecApplyConfiguration{} |
||||
} |
||||
|
||||
// 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 *TimeIntervalSpecApplyConfiguration) WithName(value string) *TimeIntervalSpecApplyConfiguration { |
||||
b.Name = &value |
||||
return b |
||||
} |
||||
|
||||
// WithTimeIntervals adds the given value to the TimeIntervals 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 TimeIntervals field.
|
||||
func (b *TimeIntervalSpecApplyConfiguration) WithTimeIntervals(values ...*IntervalApplyConfiguration) *TimeIntervalSpecApplyConfiguration { |
||||
for i := range values { |
||||
if values[i] == nil { |
||||
panic("nil value passed to WithTimeIntervals") |
||||
} |
||||
b.TimeIntervals = append(b.TimeIntervals, *values[i]) |
||||
} |
||||
return b |
||||
} |
@ -0,0 +1,34 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by applyconfiguration-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// TimeRangeApplyConfiguration represents an declarative configuration of the TimeRange type for use
|
||||
// with apply.
|
||||
type TimeRangeApplyConfiguration struct { |
||||
EndTime *string `json:"end_time,omitempty"` |
||||
StartTime *string `json:"start_time,omitempty"` |
||||
} |
||||
|
||||
// TimeRangeApplyConfiguration constructs an declarative configuration of the TimeRange type for use with
|
||||
// apply.
|
||||
func TimeRange() *TimeRangeApplyConfiguration { |
||||
return &TimeRangeApplyConfiguration{} |
||||
} |
||||
|
||||
// WithEndTime sets the EndTime 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 EndTime field is set to the value of the last call.
|
||||
func (b *TimeRangeApplyConfiguration) WithEndTime(value string) *TimeRangeApplyConfiguration { |
||||
b.EndTime = &value |
||||
return b |
||||
} |
||||
|
||||
// WithStartTime sets the StartTime 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 StartTime field is set to the value of the last call.
|
||||
func (b *TimeRangeApplyConfiguration) WithStartTime(value string) *TimeRangeApplyConfiguration { |
||||
b.StartTime = &value |
||||
return b |
||||
} |
@ -0,0 +1,93 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
"net/http" |
||||
|
||||
v0alpha1 "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
"github.com/grafana/grafana/pkg/generated/clientset/versioned/scheme" |
||||
rest "k8s.io/client-go/rest" |
||||
) |
||||
|
||||
type NotificationsV0alpha1Interface interface { |
||||
RESTClient() rest.Interface |
||||
TimeIntervalsGetter |
||||
} |
||||
|
||||
// NotificationsV0alpha1Client is used to interact with features provided by the notifications.alerting.grafana.app group.
|
||||
type NotificationsV0alpha1Client struct { |
||||
restClient rest.Interface |
||||
} |
||||
|
||||
func (c *NotificationsV0alpha1Client) TimeIntervals(namespace string) TimeIntervalInterface { |
||||
return newTimeIntervals(c, namespace) |
||||
} |
||||
|
||||
// NewForConfig creates a new NotificationsV0alpha1Client for the given config.
|
||||
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
|
||||
// where httpClient was generated with rest.HTTPClientFor(c).
|
||||
func NewForConfig(c *rest.Config) (*NotificationsV0alpha1Client, error) { |
||||
config := *c |
||||
if err := setConfigDefaults(&config); err != nil { |
||||
return nil, err |
||||
} |
||||
httpClient, err := rest.HTTPClientFor(&config) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return NewForConfigAndClient(&config, httpClient) |
||||
} |
||||
|
||||
// NewForConfigAndClient creates a new NotificationsV0alpha1Client for the given config and http client.
|
||||
// Note the http client provided takes precedence over the configured transport values.
|
||||
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*NotificationsV0alpha1Client, error) { |
||||
config := *c |
||||
if err := setConfigDefaults(&config); err != nil { |
||||
return nil, err |
||||
} |
||||
client, err := rest.RESTClientForConfigAndClient(&config, h) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &NotificationsV0alpha1Client{client}, nil |
||||
} |
||||
|
||||
// NewForConfigOrDie creates a new NotificationsV0alpha1Client for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewForConfigOrDie(c *rest.Config) *NotificationsV0alpha1Client { |
||||
client, err := NewForConfig(c) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
return client |
||||
} |
||||
|
||||
// New creates a new NotificationsV0alpha1Client for the given RESTClient.
|
||||
func New(c rest.Interface) *NotificationsV0alpha1Client { |
||||
return &NotificationsV0alpha1Client{c} |
||||
} |
||||
|
||||
func setConfigDefaults(config *rest.Config) error { |
||||
gv := v0alpha1.SchemeGroupVersion |
||||
config.GroupVersion = &gv |
||||
config.APIPath = "/apis" |
||||
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() |
||||
|
||||
if config.UserAgent == "" { |
||||
config.UserAgent = rest.DefaultKubernetesUserAgent() |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *NotificationsV0alpha1Client) RESTClient() rest.Interface { |
||||
if c == nil { |
||||
return nil |
||||
} |
||||
return c.restClient |
||||
} |
@ -0,0 +1,6 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// This package has the automatically generated typed clients.
|
||||
package v0alpha1 |
@ -0,0 +1,6 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake |
@ -0,0 +1,26 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake |
||||
|
||||
import ( |
||||
v0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/alerting_notifications/v0alpha1" |
||||
rest "k8s.io/client-go/rest" |
||||
testing "k8s.io/client-go/testing" |
||||
) |
||||
|
||||
type FakeNotificationsV0alpha1 struct { |
||||
*testing.Fake |
||||
} |
||||
|
||||
func (c *FakeNotificationsV0alpha1) TimeIntervals(namespace string) v0alpha1.TimeIntervalInterface { |
||||
return &FakeTimeIntervals{c, namespace} |
||||
} |
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeNotificationsV0alpha1) RESTClient() rest.Interface { |
||||
var ret *rest.RESTClient |
||||
return ret |
||||
} |
@ -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" |
||||
) |
||||
|
||||
// FakeTimeIntervals implements TimeIntervalInterface
|
||||
type FakeTimeIntervals struct { |
||||
Fake *FakeNotificationsV0alpha1 |
||||
ns string |
||||
} |
||||
|
||||
var timeintervalsResource = v0alpha1.SchemeGroupVersion.WithResource("timeintervals") |
||||
|
||||
var timeintervalsKind = v0alpha1.SchemeGroupVersion.WithKind("TimeInterval") |
||||
|
||||
// Get takes name of the timeInterval, and returns the corresponding timeInterval object, and an error if there is any.
|
||||
func (c *FakeTimeIntervals) Get(ctx context.Context, name string, options v1.GetOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewGetAction(timeintervalsResource, c.ns, name), &v0alpha1.TimeInterval{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), err |
||||
} |
||||
|
||||
// List takes label and field selectors, and returns the list of TimeIntervals that match those selectors.
|
||||
func (c *FakeTimeIntervals) List(ctx context.Context, opts v1.ListOptions) (result *v0alpha1.TimeIntervalList, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewListAction(timeintervalsResource, timeintervalsKind, c.ns, opts), &v0alpha1.TimeIntervalList{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts) |
||||
if label == nil { |
||||
label = labels.Everything() |
||||
} |
||||
list := &v0alpha1.TimeIntervalList{ListMeta: obj.(*v0alpha1.TimeIntervalList).ListMeta} |
||||
for _, item := range obj.(*v0alpha1.TimeIntervalList).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 timeIntervals.
|
||||
func (c *FakeTimeIntervals) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { |
||||
return c.Fake. |
||||
InvokesWatch(testing.NewWatchAction(timeintervalsResource, c.ns, opts)) |
||||
|
||||
} |
||||
|
||||
// Create takes the representation of a timeInterval and creates it. Returns the server's representation of the timeInterval, and an error, if there is any.
|
||||
func (c *FakeTimeIntervals) Create(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.CreateOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewCreateAction(timeintervalsResource, c.ns, timeInterval), &v0alpha1.TimeInterval{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), err |
||||
} |
||||
|
||||
// Update takes the representation of a timeInterval and updates it. Returns the server's representation of the timeInterval, and an error, if there is any.
|
||||
func (c *FakeTimeIntervals) Update(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.UpdateOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewUpdateAction(timeintervalsResource, c.ns, timeInterval), &v0alpha1.TimeInterval{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), err |
||||
} |
||||
|
||||
// Delete takes name of the timeInterval and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeTimeIntervals) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { |
||||
_, err := c.Fake. |
||||
Invokes(testing.NewDeleteActionWithOptions(timeintervalsResource, c.ns, name, opts), &v0alpha1.TimeInterval{}) |
||||
|
||||
return err |
||||
} |
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeTimeIntervals) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { |
||||
action := testing.NewDeleteCollectionAction(timeintervalsResource, c.ns, listOpts) |
||||
|
||||
_, err := c.Fake.Invokes(action, &v0alpha1.TimeIntervalList{}) |
||||
return err |
||||
} |
||||
|
||||
// Patch applies the patch and returns the patched timeInterval.
|
||||
func (c *FakeTimeIntervals) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.TimeInterval, err error) { |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceAction(timeintervalsResource, c.ns, name, pt, data, subresources...), &v0alpha1.TimeInterval{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), err |
||||
} |
||||
|
||||
// Apply takes the given apply declarative configuration, applies it and returns the applied timeInterval.
|
||||
func (c *FakeTimeIntervals) Apply(ctx context.Context, timeInterval *alertingnotificationsv0alpha1.TimeIntervalApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
if timeInterval == nil { |
||||
return nil, fmt.Errorf("timeInterval provided to Apply must not be nil") |
||||
} |
||||
data, err := json.Marshal(timeInterval) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
name := timeInterval.Name |
||||
if name == nil { |
||||
return nil, fmt.Errorf("timeInterval.Name must be provided to Apply") |
||||
} |
||||
obj, err := c.Fake. |
||||
Invokes(testing.NewPatchSubresourceAction(timeintervalsResource, c.ns, *name, types.ApplyPatchType, data), &v0alpha1.TimeInterval{}) |
||||
|
||||
if obj == nil { |
||||
return nil, err |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), err |
||||
} |
@ -0,0 +1,7 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
type TimeIntervalExpansion interface{} |
@ -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" |
||||
) |
||||
|
||||
// TimeIntervalsGetter has a method to return a TimeIntervalInterface.
|
||||
// A group's client should implement this interface.
|
||||
type TimeIntervalsGetter interface { |
||||
TimeIntervals(namespace string) TimeIntervalInterface |
||||
} |
||||
|
||||
// TimeIntervalInterface has methods to work with TimeInterval resources.
|
||||
type TimeIntervalInterface interface { |
||||
Create(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.CreateOptions) (*v0alpha1.TimeInterval, error) |
||||
Update(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.UpdateOptions) (*v0alpha1.TimeInterval, 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.TimeInterval, error) |
||||
List(ctx context.Context, opts v1.ListOptions) (*v0alpha1.TimeIntervalList, 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.TimeInterval, err error) |
||||
Apply(ctx context.Context, timeInterval *alertingnotificationsv0alpha1.TimeIntervalApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.TimeInterval, err error) |
||||
TimeIntervalExpansion |
||||
} |
||||
|
||||
// timeIntervals implements TimeIntervalInterface
|
||||
type timeIntervals struct { |
||||
client rest.Interface |
||||
ns string |
||||
} |
||||
|
||||
// newTimeIntervals returns a TimeIntervals
|
||||
func newTimeIntervals(c *NotificationsV0alpha1Client, namespace string) *timeIntervals { |
||||
return &timeIntervals{ |
||||
client: c.RESTClient(), |
||||
ns: namespace, |
||||
} |
||||
} |
||||
|
||||
// Get takes name of the timeInterval, and returns the corresponding timeInterval object, and an error if there is any.
|
||||
func (c *timeIntervals) Get(ctx context.Context, name string, options v1.GetOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
result = &v0alpha1.TimeInterval{} |
||||
err = c.client.Get(). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
Name(name). |
||||
VersionedParams(&options, scheme.ParameterCodec). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// List takes label and field selectors, and returns the list of TimeIntervals that match those selectors.
|
||||
func (c *timeIntervals) List(ctx context.Context, opts v1.ListOptions) (result *v0alpha1.TimeIntervalList, err error) { |
||||
var timeout time.Duration |
||||
if opts.TimeoutSeconds != nil { |
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second |
||||
} |
||||
result = &v0alpha1.TimeIntervalList{} |
||||
err = c.client.Get(). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Watch returns a watch.Interface that watches the requested timeIntervals.
|
||||
func (c *timeIntervals) 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("timeintervals"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Watch(ctx) |
||||
} |
||||
|
||||
// Create takes the representation of a timeInterval and creates it. Returns the server's representation of the timeInterval, and an error, if there is any.
|
||||
func (c *timeIntervals) Create(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.CreateOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
result = &v0alpha1.TimeInterval{} |
||||
err = c.client.Post(). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Body(timeInterval). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Update takes the representation of a timeInterval and updates it. Returns the server's representation of the timeInterval, and an error, if there is any.
|
||||
func (c *timeIntervals) Update(ctx context.Context, timeInterval *v0alpha1.TimeInterval, opts v1.UpdateOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
result = &v0alpha1.TimeInterval{} |
||||
err = c.client.Put(). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
Name(timeInterval.Name). |
||||
VersionedParams(&opts, scheme.ParameterCodec). |
||||
Body(timeInterval). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
||||
|
||||
// Delete takes name of the timeInterval and deletes it. Returns an error if one occurs.
|
||||
func (c *timeIntervals) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { |
||||
return c.client.Delete(). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
Name(name). |
||||
Body(&opts). |
||||
Do(ctx). |
||||
Error() |
||||
} |
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *timeIntervals) 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("timeintervals"). |
||||
VersionedParams(&listOpts, scheme.ParameterCodec). |
||||
Timeout(timeout). |
||||
Body(&opts). |
||||
Do(ctx). |
||||
Error() |
||||
} |
||||
|
||||
// Patch applies the patch and returns the patched timeInterval.
|
||||
func (c *timeIntervals) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v0alpha1.TimeInterval, err error) { |
||||
result = &v0alpha1.TimeInterval{} |
||||
err = c.client.Patch(pt). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
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 timeInterval.
|
||||
func (c *timeIntervals) Apply(ctx context.Context, timeInterval *alertingnotificationsv0alpha1.TimeIntervalApplyConfiguration, opts v1.ApplyOptions) (result *v0alpha1.TimeInterval, err error) { |
||||
if timeInterval == nil { |
||||
return nil, fmt.Errorf("timeInterval provided to Apply must not be nil") |
||||
} |
||||
patchOpts := opts.ToPatchOptions() |
||||
data, err := json.Marshal(timeInterval) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
name := timeInterval.Name |
||||
if name == nil { |
||||
return nil, fmt.Errorf("timeInterval.Name must be provided to Apply") |
||||
} |
||||
result = &v0alpha1.TimeInterval{} |
||||
err = c.client.Patch(types.ApplyPatchType). |
||||
Namespace(c.ns). |
||||
Resource("timeintervals"). |
||||
Name(*name). |
||||
VersionedParams(&patchOpts, scheme.ParameterCodec). |
||||
Body(data). |
||||
Do(ctx). |
||||
Into(result) |
||||
return |
||||
} |
@ -0,0 +1,32 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package alerting_notifications |
||||
|
||||
import ( |
||||
v0alpha1 "github.com/grafana/grafana/pkg/generated/informers/externalversions/alerting_notifications/v0alpha1" |
||||
internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" |
||||
) |
||||
|
||||
// Interface provides access to each of this group's versions.
|
||||
type Interface interface { |
||||
// V0alpha1 provides access to shared informers for resources in V0alpha1.
|
||||
V0alpha1() v0alpha1.Interface |
||||
} |
||||
|
||||
type group struct { |
||||
factory internalinterfaces.SharedInformerFactory |
||||
namespace string |
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc |
||||
} |
||||
|
||||
// New returns a new Interface.
|
||||
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { |
||||
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} |
||||
} |
||||
|
||||
// V0alpha1 returns a new v0alpha1.Interface.
|
||||
func (g *group) V0alpha1() v0alpha1.Interface { |
||||
return v0alpha1.New(g.factory, g.namespace, g.tweakListOptions) |
||||
} |
@ -0,0 +1,31 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" |
||||
) |
||||
|
||||
// Interface provides access to all the informers in this group version.
|
||||
type Interface interface { |
||||
// TimeIntervals returns a TimeIntervalInformer.
|
||||
TimeIntervals() TimeIntervalInformer |
||||
} |
||||
|
||||
type version struct { |
||||
factory internalinterfaces.SharedInformerFactory |
||||
namespace string |
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc |
||||
} |
||||
|
||||
// New returns a new Interface.
|
||||
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { |
||||
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} |
||||
} |
||||
|
||||
// TimeIntervals returns a TimeIntervalInformer.
|
||||
func (v *version) TimeIntervals() TimeIntervalInformer { |
||||
return &timeIntervalInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} |
||||
} |
@ -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" |
||||
) |
||||
|
||||
// TimeIntervalInformer provides access to a shared informer and lister for
|
||||
// TimeIntervals.
|
||||
type TimeIntervalInformer interface { |
||||
Informer() cache.SharedIndexInformer |
||||
Lister() v0alpha1.TimeIntervalLister |
||||
} |
||||
|
||||
type timeIntervalInformer struct { |
||||
factory internalinterfaces.SharedInformerFactory |
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc |
||||
namespace string |
||||
} |
||||
|
||||
// NewTimeIntervalInformer constructs a new informer for TimeInterval 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 NewTimeIntervalInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { |
||||
return NewFilteredTimeIntervalInformer(client, namespace, resyncPeriod, indexers, nil) |
||||
} |
||||
|
||||
// NewFilteredTimeIntervalInformer constructs a new informer for TimeInterval 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 NewFilteredTimeIntervalInformer(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().TimeIntervals(namespace).List(context.TODO(), options) |
||||
}, |
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { |
||||
if tweakListOptions != nil { |
||||
tweakListOptions(&options) |
||||
} |
||||
return client.NotificationsV0alpha1().TimeIntervals(namespace).Watch(context.TODO(), options) |
||||
}, |
||||
}, |
||||
&alertingnotificationsv0alpha1.TimeInterval{}, |
||||
resyncPeriod, |
||||
indexers, |
||||
) |
||||
} |
||||
|
||||
func (f *timeIntervalInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { |
||||
return NewFilteredTimeIntervalInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) |
||||
} |
||||
|
||||
func (f *timeIntervalInformer) Informer() cache.SharedIndexInformer { |
||||
return f.factory.InformerFor(&alertingnotificationsv0alpha1.TimeInterval{}, f.defaultInformer) |
||||
} |
||||
|
||||
func (f *timeIntervalInformer) Lister() v0alpha1.TimeIntervalLister { |
||||
return v0alpha1.NewTimeIntervalLister(f.Informer().GetIndexer()) |
||||
} |
@ -0,0 +1,13 @@ |
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
// TimeIntervalListerExpansion allows custom methods to be added to
|
||||
// TimeIntervalLister.
|
||||
type TimeIntervalListerExpansion interface{} |
||||
|
||||
// TimeIntervalNamespaceListerExpansion allows custom methods to be added to
|
||||
// TimeIntervalNamespaceLister.
|
||||
type TimeIntervalNamespaceListerExpansion interface{} |
@ -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" |
||||
) |
||||
|
||||
// TimeIntervalLister helps list TimeIntervals.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type TimeIntervalLister interface { |
||||
// List lists all TimeIntervals in the indexer.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.TimeInterval, err error) |
||||
// TimeIntervals returns an object that can list and get TimeIntervals.
|
||||
TimeIntervals(namespace string) TimeIntervalNamespaceLister |
||||
TimeIntervalListerExpansion |
||||
} |
||||
|
||||
// timeIntervalLister implements the TimeIntervalLister interface.
|
||||
type timeIntervalLister struct { |
||||
indexer cache.Indexer |
||||
} |
||||
|
||||
// NewTimeIntervalLister returns a new TimeIntervalLister.
|
||||
func NewTimeIntervalLister(indexer cache.Indexer) TimeIntervalLister { |
||||
return &timeIntervalLister{indexer: indexer} |
||||
} |
||||
|
||||
// List lists all TimeIntervals in the indexer.
|
||||
func (s *timeIntervalLister) List(selector labels.Selector) (ret []*v0alpha1.TimeInterval, err error) { |
||||
err = cache.ListAll(s.indexer, selector, func(m interface{}) { |
||||
ret = append(ret, m.(*v0alpha1.TimeInterval)) |
||||
}) |
||||
return ret, err |
||||
} |
||||
|
||||
// TimeIntervals returns an object that can list and get TimeIntervals.
|
||||
func (s *timeIntervalLister) TimeIntervals(namespace string) TimeIntervalNamespaceLister { |
||||
return timeIntervalNamespaceLister{indexer: s.indexer, namespace: namespace} |
||||
} |
||||
|
||||
// TimeIntervalNamespaceLister helps list and get TimeIntervals.
|
||||
// All objects returned here must be treated as read-only.
|
||||
type TimeIntervalNamespaceLister interface { |
||||
// List lists all TimeIntervals in the indexer for a given namespace.
|
||||
// Objects returned here must be treated as read-only.
|
||||
List(selector labels.Selector) (ret []*v0alpha1.TimeInterval, err error) |
||||
// Get retrieves the TimeInterval from the indexer for a given namespace and name.
|
||||
// Objects returned here must be treated as read-only.
|
||||
Get(name string) (*v0alpha1.TimeInterval, error) |
||||
TimeIntervalNamespaceListerExpansion |
||||
} |
||||
|
||||
// timeIntervalNamespaceLister implements the TimeIntervalNamespaceLister
|
||||
// interface.
|
||||
type timeIntervalNamespaceLister struct { |
||||
indexer cache.Indexer |
||||
namespace string |
||||
} |
||||
|
||||
// List lists all TimeIntervals in the indexer for a given namespace.
|
||||
func (s timeIntervalNamespaceLister) List(selector labels.Selector) (ret []*v0alpha1.TimeInterval, err error) { |
||||
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { |
||||
ret = append(ret, m.(*v0alpha1.TimeInterval)) |
||||
}) |
||||
return ret, err |
||||
} |
||||
|
||||
// Get retrieves the TimeInterval from the indexer for a given namespace and name.
|
||||
func (s timeIntervalNamespaceLister) Get(name string) (*v0alpha1.TimeInterval, error) { |
||||
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
if !exists { |
||||
return nil, errors.NewNotFound(v0alpha1.Resource("timeinterval"), name) |
||||
} |
||||
return obj.(*v0alpha1.TimeInterval), nil |
||||
} |
@ -0,0 +1,112 @@ |
||||
package notifications |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
|
||||
"github.com/prometheus/client_golang/prometheus" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
"k8s.io/apimachinery/pkg/runtime/serializer" |
||||
"k8s.io/apiserver/pkg/authorization/authorizer" |
||||
"k8s.io/apiserver/pkg/registry/generic" |
||||
"k8s.io/apiserver/pkg/registry/rest" |
||||
genericapiserver "k8s.io/apiserver/pkg/server" |
||||
"k8s.io/kube-openapi/pkg/common" |
||||
|
||||
notificationsModels "github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1" |
||||
"github.com/grafana/grafana/pkg/apiserver/builder" |
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" |
||||
timeInterval "github.com/grafana/grafana/pkg/registry/apis/alerting/notifications/timeinterval" |
||||
"github.com/grafana/grafana/pkg/services/accesscontrol" |
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" |
||||
"github.com/grafana/grafana/pkg/services/featuremgmt" |
||||
"github.com/grafana/grafana/pkg/services/ngalert" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
var _ builder.APIGroupBuilder = (*NotificationsAPIBuilder)(nil) |
||||
|
||||
// This is used just so wire has something unique to return
|
||||
type NotificationsAPIBuilder struct { |
||||
authz accesscontrol.AccessControl |
||||
ng *ngalert.AlertNG |
||||
namespacer request.NamespaceMapper |
||||
gv schema.GroupVersion |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetDesiredDualWriterMode(dualWrite bool, toMode map[string]grafanarest.DualWriterMode) grafanarest.DualWriterMode { |
||||
// Add required configuration support in order to enable other modes. For an example, see pkg/registry/apis/playlist/register.go
|
||||
return grafanarest.Mode0 |
||||
} |
||||
|
||||
func RegisterAPIService( |
||||
features featuremgmt.FeatureToggles, |
||||
apiregistration builder.APIRegistrar, |
||||
cfg *setting.Cfg, |
||||
ng *ngalert.AlertNG, |
||||
) *NotificationsAPIBuilder { |
||||
if ng.IsDisabled() || !features.IsEnabledGlobally(featuremgmt.FlagAlertingApiServer) { |
||||
return nil |
||||
} |
||||
builder := &NotificationsAPIBuilder{ |
||||
ng: ng, |
||||
namespacer: request.GetNamespaceMapper(cfg), |
||||
gv: notificationsModels.SchemeGroupVersion, |
||||
authz: ng.Api.AccessControl, |
||||
} |
||||
apiregistration.RegisterAPI(builder) |
||||
return builder |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetGroupVersion() schema.GroupVersion { |
||||
return t.gv |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { |
||||
err := notificationsModels.AddToScheme(scheme) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return scheme.SetVersionPriority(notificationsModels.SchemeGroupVersion) |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetAPIGroupInfo( |
||||
scheme *runtime.Scheme, |
||||
codecs serializer.CodecFactory, |
||||
optsGetter generic.RESTOptionsGetter, |
||||
desiredMode grafanarest.DualWriterMode, |
||||
reg prometheus.Registerer, |
||||
) (*genericapiserver.APIGroupInfo, error) { |
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(notificationsModels.GROUP, scheme, metav1.ParameterCodec, codecs) |
||||
|
||||
intervals, err := timeInterval.NewStorage(t.ng.Api.MuteTimings, t.namespacer, scheme, desiredMode, optsGetter, reg) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("failed to initialize time-interval storage: %w", err) |
||||
} |
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[notificationsModels.VERSION] = map[string]rest.Storage{ |
||||
notificationsModels.TimeIntervalResourceInfo.StoragePath(): intervals, |
||||
} |
||||
return &apiGroupInfo, nil |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { |
||||
return notificationsModels.GetOpenAPIDefinitions |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetAPIRoutes() *builder.APIRoutes { |
||||
return nil |
||||
} |
||||
|
||||
func (t NotificationsAPIBuilder) GetAuthorizer() authorizer.Authorizer { |
||||
return authorizer.AuthorizerFunc( |
||||
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { |
||||
switch a.GetResource() { |
||||
case notificationsModels.TimeIntervalResourceInfo.GroupResource().Resource: |
||||
return timeInterval.Authorize(ctx, t.authz, a) |
||||
} |
||||
return authorizer.DecisionNoOpinion, "", nil |
||||
}) |
||||
} |
@ -0,0 +1,54 @@ |
||||
package timeinterval |
||||
|
||||
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 |
||||
case "update": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTimeIntervalsWrite), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), |
||||
) |
||||
case "deletecollection": |
||||
fallthrough |
||||
case "delete": |
||||
action = accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTimeIntervalsDelete), |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsWrite), |
||||
) |
||||
} |
||||
|
||||
eval := accesscontrol.EvalAny( |
||||
accesscontrol.EvalPermission(accesscontrol.ActionAlertingNotificationsTimeIntervalsRead), |
||||
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,97 @@ |
||||
package timeinterval |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"hash/fnv" |
||||
|
||||
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" |
||||
) |
||||
|
||||
func getIntervalUID(t definitions.MuteTimeInterval) string { |
||||
sum := fnv.New64() |
||||
_, _ = sum.Write([]byte(t.Name)) |
||||
return fmt.Sprintf("%016x", sum.Sum64()) |
||||
} |
||||
|
||||
func convertToK8sResources(orgID int64, intervals []definitions.MuteTimeInterval, namespacer request.NamespaceMapper) (*model.TimeIntervalList, error) { |
||||
data, err := json.Marshal(intervals) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
var specs []model.TimeIntervalSpec |
||||
err = json.Unmarshal(data, &specs) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
result := &model.TimeIntervalList{} |
||||
for idx := range specs { |
||||
interval := intervals[idx] |
||||
spec := specs[idx] |
||||
uid := getIntervalUID(interval) // TODO replace to stable UID when we switch to normal storage
|
||||
result.Items = append(result.Items, model.TimeInterval{ |
||||
TypeMeta: resourceInfo.TypeMeta(), |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
UID: types.UID(uid), // TODO 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(interval.Provenance), |
||||
}, |
||||
ResourceVersion: interval.Version, |
||||
}, |
||||
Spec: spec, |
||||
}) |
||||
} |
||||
return result, nil |
||||
} |
||||
|
||||
func convertToK8sResource(orgID int64, interval definitions.MuteTimeInterval, namespacer request.NamespaceMapper) (*model.TimeInterval, error) { |
||||
data, err := json.Marshal(interval) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
spec := model.TimeIntervalSpec{} |
||||
err = json.Unmarshal(data, &spec) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
uid := getIntervalUID(interval) // TODO replace to stable UID when we switch to normal storage
|
||||
return &model.TimeInterval{ |
||||
TypeMeta: resourceInfo.TypeMeta(), |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
UID: types.UID(uid), // TODO 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(interval.Provenance), |
||||
}, |
||||
ResourceVersion: interval.Version, |
||||
}, |
||||
Spec: spec, |
||||
}, nil |
||||
} |
||||
|
||||
func convertToDomainModel(interval *model.TimeInterval) (definitions.MuteTimeInterval, error) { |
||||
b, err := json.Marshal(interval.Spec) |
||||
if err != nil { |
||||
return definitions.MuteTimeInterval{}, err |
||||
} |
||||
result := definitions.MuteTimeInterval{} |
||||
err = json.Unmarshal(b, &result) |
||||
if err != nil { |
||||
return definitions.MuteTimeInterval{}, err |
||||
} |
||||
result.Version = interval.ResourceVersion |
||||
err = result.Validate() |
||||
if err != nil { |
||||
return definitions.MuteTimeInterval{}, err |
||||
} |
||||
return result, nil |
||||
} |
@ -0,0 +1,205 @@ |
||||
package timeinterval |
||||
|
||||
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) |
||||
) |
||||
|
||||
var resourceInfo = notifications.TimeIntervalResourceInfo |
||||
|
||||
type TimeIntervalService interface { |
||||
GetMuteTimings(ctx context.Context, orgID int64) ([]definitions.MuteTimeInterval, error) |
||||
GetMuteTiming(ctx context.Context, name string, orgID int64) (definitions.MuteTimeInterval, error) |
||||
CreateMuteTiming(ctx context.Context, mt definitions.MuteTimeInterval, orgID int64) (definitions.MuteTimeInterval, error) |
||||
UpdateMuteTiming(ctx context.Context, mt definitions.MuteTimeInterval, orgID int64) (definitions.MuteTimeInterval, error) |
||||
DeleteMuteTiming(ctx context.Context, name string, orgID int64, provenance definitions.Provenance, version string) error |
||||
} |
||||
|
||||
type legacyStorage struct { |
||||
service TimeIntervalService |
||||
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 |
||||
} |
||||
|
||||
res, err := s.service.GetMuteTimings(ctx, orgId) |
||||
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 |
||||
} |
||||
|
||||
timings, err := s.service.GetMuteTimings(ctx, info.OrgID) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for _, mt := range timings { |
||||
if getIntervalUID(mt) == uid { |
||||
return convertToK8sResource(info.OrgID, mt, s.namespacer) |
||||
} |
||||
} |
||||
return nil, errors.NewNotFound(resourceInfo.GroupResource(), uid) |
||||
} |
||||
|
||||
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.TimeInterval) |
||||
if !ok { |
||||
return nil, fmt.Errorf("expected time-interval 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.CreateMuteTiming(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.TimeInterval) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("expected time-interval but got %s", obj.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
interval, err := convertToDomainModel(p) |
||||
if err != nil { |
||||
return old, false, err |
||||
} |
||||
|
||||
if p.ObjectMeta.Name != getIntervalUID(interval) { |
||||
return nil, false, errors.NewBadRequest("title of cannot be changed. Consider creating a new resource.") |
||||
} |
||||
|
||||
updated, err := s.service.UpdateMuteTiming(ctx, interval, 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.TimeInterval) |
||||
if !ok { |
||||
return nil, false, fmt.Errorf("expected time-interval but got %s", old.GetObjectKind().GroupVersionKind()) |
||||
} |
||||
|
||||
err = s.service.DeleteMuteTiming(ctx, p.Spec.Name, 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,79 @@ |
||||
package timeinterval |
||||
|
||||
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 TimeIntervalService, |
||||
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: "Intervals", Type: "string", Format: "string", Description: "The display name"},
|
||||
}, |
||||
func(obj any) ([]interface{}, error) { |
||||
r, ok := obj.(*model.TimeInterval) |
||||
if ok { |
||||
return []interface{}{ |
||||
r.Name, |
||||
// 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 |
||||
} |
|
@ -0,0 +1,541 @@ |
||||
package timeinterval |
||||
|
||||
import ( |
||||
"context" |
||||
"encoding/json" |
||||
"fmt" |
||||
"testing" |
||||
|
||||
"github.com/prometheus/alertmanager/config" |
||||
"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/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().TimeIntervals("default") |
||||
|
||||
newInterval := &v0alpha1.TimeInterval{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TimeIntervalSpec{ |
||||
Name: "time-newInterval", |
||||
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2), |
||||
}, |
||||
} |
||||
|
||||
t.Run("create should fail if object name is specified", func(t *testing.T) { |
||||
interval := newInterval.DeepCopy() |
||||
interval.Name = "time-newInterval" |
||||
_, err := client.Create(ctx, interval, v1.CreateOptions{}) |
||||
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, newInterval, 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 existingInterval *v0alpha1.TimeInterval |
||||
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, newInterval.Spec, actual.Spec) |
||||
existingInterval = actual |
||||
}) |
||||
|
||||
t.Run("update should fail if name in the specification changes", func(t *testing.T) { |
||||
if existingInterval == nil { |
||||
t.Skip() |
||||
} |
||||
updated := existingInterval.DeepCopy() |
||||
updated.Spec.Name = "another-newInterval" |
||||
_, err := client.Update(ctx, updated, v1.UpdateOptions{}) |
||||
require.Truef(t, errors.IsBadRequest(err), "Expected BadRequest but got %s", err) |
||||
}) |
||||
} |
||||
|
||||
func TestIntegrationTimeIntervalAccessControl(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("IntervalsReader", apis.Org1, org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTimeIntervalsRead, |
||||
}, |
||||
}, |
||||
}) |
||||
writer := helper.CreateUser("IntervalsWriter", "Org1", org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTimeIntervalsRead, |
||||
accesscontrol.ActionAlertingNotificationsTimeIntervalsWrite, |
||||
}, |
||||
}, |
||||
}) |
||||
|
||||
deleter := helper.CreateUser("IntervalsDeleter", apis.Org1, org.RoleNone, []resourcepermissions.SetResourcePermissionCommand{ |
||||
{ |
||||
Actions: []string{ |
||||
accesscontrol.ActionAlertingNotificationsTimeIntervalsRead, |
||||
accesscontrol.ActionAlertingNotificationsTimeIntervalsDelete, |
||||
}, |
||||
}, |
||||
}) |
||||
|
||||
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().TimeIntervals("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().TimeIntervals("default") |
||||
|
||||
var expected = &v0alpha1.TimeInterval{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
Annotations: map[string]string{ |
||||
"grafana.com/provenance": "", |
||||
}, |
||||
}, |
||||
Spec: v0alpha1.TimeIntervalSpec{ |
||||
Name: fmt.Sprintf("time-interval-1-%s", tc.user.Identity.GetLogin()), |
||||
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2), |
||||
}, |
||||
} |
||||
d, err := json.Marshal(expected) |
||||
require.NoError(t, err) |
||||
|
||||
if tc.canCreate { |
||||
t.Run("should be able to create time interval", 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 time intervals", 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 time interval 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 time intervals", 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 time interval 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.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2) |
||||
|
||||
d, err = json.Marshal(updatedExpected) |
||||
require.NoError(t, err) |
||||
|
||||
if tc.canUpdate { |
||||
t.Run("should be able to update time interval", 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 time interval", 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 time interval", 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 time interval", 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 TestIntegrationTimeIntervalProvisioning(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().TimeIntervals("default") |
||||
|
||||
env := helper.GetEnv() |
||||
ac := acimpl.ProvideAccessControl(env.FeatureToggles) |
||||
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.TimeInterval{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TimeIntervalSpec{ |
||||
Name: "time-interval-1", |
||||
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2), |
||||
}, |
||||
}, v1.CreateOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "", created.Annotations["grafana.com/provenance"]) |
||||
|
||||
t.Run("should provide provenance status", func(t *testing.T) { |
||||
require.NoError(t, db.SetProvenance(ctx, &definitions.MuteTimeInterval{ |
||||
MuteTimeInterval: config.MuteTimeInterval{ |
||||
Name: created.Spec.Name, |
||||
}, |
||||
}, admin.Identity.GetOrgID(), "API")) |
||||
|
||||
got, err := adminClient.Get(ctx, created.Name, v1.GetOptions{}) |
||||
require.NoError(t, err) |
||||
require.Equal(t, "API", got.Annotations["grafana.com/provenance"]) |
||||
}) |
||||
t.Run("should not let update if provisioned", func(t *testing.T) { |
||||
updated := created.DeepCopy() |
||||
updated.Spec.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2) |
||||
|
||||
_, 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 TestIntegrationTimeIntervalOptimisticConcurrency(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().TimeIntervals("default") |
||||
|
||||
interval := v0alpha1.TimeInterval{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TimeIntervalSpec{ |
||||
Name: "time-interval", |
||||
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2), |
||||
}, |
||||
} |
||||
|
||||
created, err := adminClient.Create(ctx, &interval, 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.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2) |
||||
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.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2) |
||||
|
||||
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, &interval, 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 TestIntegrationTimeIntervalPatch(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().TimeIntervals("default") |
||||
|
||||
interval := v0alpha1.TimeInterval{ |
||||
ObjectMeta: v1.ObjectMeta{ |
||||
Namespace: "default", |
||||
}, |
||||
Spec: v0alpha1.TimeIntervalSpec{ |
||||
Name: "time-interval", |
||||
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2), |
||||
}, |
||||
} |
||||
|
||||
current, err := adminClient.Create(ctx, &interval, 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": { |
||||
"time_intervals" : [] |
||||
} |
||||
}` |
||||
|
||||
result, err := adminClient.Patch(ctx, current.Name, types.MergePatchType, []byte(patch), v1.PatchOptions{}) |
||||
require.NoError(t, err) |
||||
require.Empty(t, result.Spec.TimeIntervals) |
||||
current = result |
||||
}) |
||||
|
||||
t.Run("should patch with json patch", func(t *testing.T) { |
||||
expected := v0alpha1.IntervalGenerator{}.Generate() |
||||
|
||||
patch := []map[string]interface{}{ |
||||
{ |
||||
"op": "add", |
||||
"path": "/spec/time_intervals/-", |
||||
"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.TimeIntervals = []v0alpha1.Interval{ |
||||
expected, |
||||
} |
||||
require.EqualValues(t, expectedSpec, result.Spec) |
||||
current = result |
||||
}) |
||||
} |
Loading…
Reference in new issue