Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions api/v1beta1/grafananotificationpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,15 @@ type GrafanaNotificationPolicySpec struct {
GrafanaCommonSpec `json:",inline"`

// Routes for alerts to match against
Route *Route `json:"route"`
Route *PartialRoute `json:"route"`

// Whether to enable or disable editing of the notification policy in Grafana UI
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
// +optional
Editable *bool `json:"editable,omitempty"`
}

type Route struct {
// continue
Continue bool `json:"continue,omitempty"`

type PartialRoute struct {
// group by
GroupBy []string `json:"group_by,omitempty"`

Expand All @@ -50,23 +47,6 @@ type Route struct {
// group wait
GroupWait string `json:"group_wait,omitempty"`

// match re
MatchRe models.MatchRegexps `json:"match_re,omitempty"`

// matchers
Matchers Matchers `json:"matchers,omitempty"`

// mute time intervals
MuteTimeIntervals []string `json:"mute_time_intervals,omitempty"`

ActiveTimeIntervals []string `json:"active_time_intervals,omitempty"`

// object matchers
ObjectMatchers models.ObjectMatchers `json:"object_matchers,omitempty"`

// provenance
Provenance models.Provenance `json:"provenance,omitempty"`

// receiver
// +kubebuilder:validation:MinLength=1
Receiver string `json:"receiver"`
Expand All @@ -82,6 +62,31 @@ type Route struct {
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
Routes []*Route `json:"routes,omitempty"`

// Deprecated: Does nothing
Provenance models.Provenance `json:"provenance,omitempty"`
}

type Route struct {
PartialRoute `json:",inline"`

// continue
Continue bool `json:"continue,omitempty"`

// match re
MatchRe models.MatchRegexps `json:"match_re,omitempty"`

// matchers
Matchers Matchers `json:"matchers,omitempty"`

// object matchers
ObjectMatchers models.ObjectMatchers `json:"object_matchers,omitempty"`

// mute time intervals
MuteTimeIntervals []string `json:"mute_time_intervals,omitempty"`

// active time intervals
ActiveTimeIntervals []string `json:"active_time_intervals,omitempty"`
}

type Matcher struct {
Expand Down Expand Up @@ -124,7 +129,6 @@ func (r *Route) ToModelRoute() *models.Route {
MuteTimeIntervals: r.MuteTimeIntervals,
ActiveTimeIntervals: r.ActiveTimeIntervals,
ObjectMatchers: r.ObjectMatchers,
Provenance: r.Provenance,
Receiver: r.Receiver,
RepeatInterval: r.RepeatInterval,
Routes: make([]*models.Route, len(r.Routes)),
Expand All @@ -136,15 +140,31 @@ func (r *Route) ToModelRoute() *models.Route {
return out
}

func (r *PartialRoute) ToModelRoute() *models.Route {
out := &models.Route{
GroupBy: r.GroupBy,
GroupInterval: r.GroupInterval,
GroupWait: r.GroupWait,
Receiver: r.Receiver,
RepeatInterval: r.RepeatInterval,
Routes: make([]*models.Route, len(r.Routes)),
}
for i, v := range r.Routes {
out.Routes[i] = v.ToModelRoute()
}

return out
}

// selectorMutuallyExclusive checks if a single route satisfies the mutual exclusivity constraint
// for checking the entire route including nested routes, use IsRouteSelectorMutuallyExclusive
func (r *Route) selectorMutuallyExclusive() bool {
func (r *PartialRoute) selectorMutuallyExclusive() bool {
return !(r.RouteSelector != nil && len(r.Routes) > 0) // nolint:staticcheck
}

// IsRouteSelectorMutuallyExclusive returns true when the route and all its sub-routes
// satisfy the constraint of routes and routeSelector being mutually exclusive
func (r *Route) IsRouteSelectorMutuallyExclusive() bool {
func (r *PartialRoute) IsRouteSelectorMutuallyExclusive() bool {
if !r.selectorMutuallyExclusive() {
return false
}
Expand All @@ -160,7 +180,7 @@ func (r *Route) IsRouteSelectorMutuallyExclusive() bool {
}

// HasRouteSelector checks if the given Route or any of its nested Routes has a RouteSelector
func (r *Route) HasRouteSelector() bool {
func (r *PartialRoute) HasRouteSelector() bool {
if r.RouteSelector != nil {
return true
}
Expand Down
43 changes: 23 additions & 20 deletions api/v1beta1/grafananotificationpolicy_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,10 @@ func newNotificationPolicy(name string, editable *bool) *GrafanaNotificationPoli
},
},
},
Route: &Route{
Continue: false,
Receiver: "grafana-default-email",
GroupBy: []string{"group_name", "alert_name"},
MuteTimeIntervals: []string{},
ActiveTimeIntervals: []string{},
Routes: []*Route{},
Route: &PartialRoute{
Receiver: "grafana-default-email",
GroupBy: []string{"group_name", "alert_name"},
Routes: []*Route{},
},
},
}
Expand Down Expand Up @@ -91,24 +88,24 @@ var _ = Describe("NotificationPolicy type", func() {
func TestIsRouteSelectorMutuallyExclusive(t *testing.T) {
tests := []struct {
name string
route *Route
route *PartialRoute
expected bool
}{
{
name: "Empty route",
route: &Route{},
route: &PartialRoute{},
expected: true,
},
{
name: "Route with only RouteSelector",
route: &Route{
route: &PartialRoute{
RouteSelector: &metav1.LabelSelector{},
},
expected: true,
},
{
name: "Route with only sub-routes",
route: &Route{
route: &PartialRoute{
Routes: []*Route{
{},
{},
Expand All @@ -118,7 +115,7 @@ func TestIsRouteSelectorMutuallyExclusive(t *testing.T) {
},
{
name: "Route with both RouteSelector and sub-routes",
route: &Route{
route: &PartialRoute{
RouteSelector: &metav1.LabelSelector{},
Routes: []*Route{
{},
Expand All @@ -128,14 +125,18 @@ func TestIsRouteSelectorMutuallyExclusive(t *testing.T) {
},
{
name: "Nested routes with mutual exclusivity",
route: &Route{
route: &PartialRoute{
Routes: []*Route{
{
RouteSelector: &metav1.LabelSelector{},
PartialRoute: PartialRoute{
RouteSelector: &metav1.LabelSelector{},
},
},
{
Routes: []*Route{
{},
PartialRoute: PartialRoute{
Routes: []*Route{
{},
},
},
},
},
Expand All @@ -144,12 +145,14 @@ func TestIsRouteSelectorMutuallyExclusive(t *testing.T) {
},
{
name: "Nested routes without mutual exclusivity",
route: &Route{
route: &PartialRoute{
Routes: []*Route{
{
RouteSelector: &metav1.LabelSelector{},
Routes: []*Route{
{},
PartialRoute: PartialRoute{
RouteSelector: &metav1.LabelSelector{},
Routes: []*Route{
{},
},
},
},
},
Expand Down
76 changes: 46 additions & 30 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading