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 model
This commit is contained in:
Yuri Tseretyan
2024-06-20 16:52:03 -04:00
committed by GitHub
parent 3228b64fe6
commit b075926202
53 changed files with 3149 additions and 54 deletions

View File

@@ -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"

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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"},
},
},
}
}

View File

@@ -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