Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Notification Policy Routes #1800

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
871e2db
feat: run operator-sdk create api command
msvechla Dec 13, 2024
ed9d2fa
feat: add new fields to GrafanaNotificationPolicyRoute spec
msvechla Dec 13, 2024
0bedeb5
chore: run make manifests && make generate
msvechla Dec 13, 2024
cdcdde8
feat: add DiscoveredRoutes to GrafanaNotificationPolicy status
msvechla Dec 17, 2024
b7ddf8d
refactor: switch to RouteSelector instead of InstanceSelector
msvechla Dec 17, 2024
10b3ca7
chore: run make
msvechla Dec 17, 2024
775a1a2
feat: implement merging of NotificationPolicyRoutes
msvechla Dec 17, 2024
051688f
test: add test for mergeNotificationPolicyRoutesWithRouteList
msvechla Dec 17, 2024
7c8458e
chore: add samples for local GrafanaNotificationPolicyRoute testing
msvechla Dec 17, 2024
068bf86
feat: setup ownerReferences for GrafanaNotificationPolicyRoutes
msvechla Dec 18, 2024
5a70f84
feat: replace owner references with watches to support cross-namespace
msvechla Dec 18, 2024
3ed60f5
docs: add docs and example for new NotificationPolicyRoute
msvechla Dec 20, 2024
f7854d8
refactor: move routeSelector to route object, make it mutually exclusive
msvechla Jan 20, 2025
72bda03
refactor: implement new assembleNotificationPolicyRoutes logic
msvechla Jan 20, 2025
d0a7de6
refactor: implement new dynamic route assembly and add tests
msvechla Jan 21, 2025
95a1fd8
refactor: remove priority
msvechla Jan 21, 2025
6b59bb5
Merge branch 'master' into notification_policy_routes_impl
msvechla Jan 21, 2025
9787fe5
chore: re-run make manifest and generate, fix go mods
msvechla Jan 21, 2025
d4238b2
feat: add status condition for mutual exclusivity check
msvechla Jan 22, 2025
c7a9b8d
docs: update examples and docs related to NotificationPolicies
msvechla Jan 22, 2025
01ab6b1
refactor: convert to member func and rename to selectorMutuallyExclusive
msvechla Jan 24, 2025
03dd5a9
chore: mention mutual exclusivity in comments
msvechla Jan 24, 2025
9cb305d
test: fix test setup order and kind args
msvechla Jan 24, 2025
ebdf7ed
refactor: inline Route object
msvechla Jan 24, 2025
d97de63
refactor: remove controller for GrafanaNotificationPolicyRoute
msvechla Jan 24, 2025
c8b41c9
refactor: implement NamespacedResource for NotificationPolicyRoute
msvechla Jan 24, 2025
6f6e696
docs: switch to example image of rendered routes
msvechla Jan 24, 2025
9fe3524
Update docs/docs/alerting/notification-policies.md
msvechla Jan 24, 2025
85c4e4d
Update docs/docs/alerting/notification-policies.md
msvechla Jan 24, 2025
3fe566d
fix
msvechla Jan 24, 2025
f924763
refactor: always sync applyErrors status condition on defer
msvechla Jan 24, 2025
942a0b5
refactor: always sync all GrafanaNotificationPolicy on route changes
msvechla Jan 24, 2025
feeee41
Update docs/docs/alerting/notification-policies.md
msvechla Jan 24, 2025
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
11 changes: 10 additions & 1 deletion PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ layout:
plugins:
manifests.sdk.operatorframework.io/v2: {}
projectName: grafana-operator
repo: github.com/grafana/grafana-operator
repo: github.com/grafana/grafana-operator/v5
resources:
- api:
crdVersion: v1
Expand Down Expand Up @@ -64,4 +64,13 @@ resources:
kind: GrafanaContactPoint
path: github.com/grafana/grafana-operator/api/v1beta1
version: v1beta1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: integreatly.org
group: grafana
kind: GrafanaNotificationPolicyRoute
path: github.com/grafana/grafana-operator/v5/api/v1beta1
version: v1beta1
version: "3"
40 changes: 38 additions & 2 deletions api/v1beta1/grafananotificationpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type Route struct {
// repeat interval
RepeatInterval string `json:"repeat_interval,omitempty"`

// Selects GrafanaNotificationPolicyRoutes to merge in when specified
msvechla marked this conversation as resolved.
Show resolved Hide resolved
RouteSelector *metav1.LabelSelector `json:"routeSelector,omitempty"`

// routes
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
Expand Down Expand Up @@ -126,6 +129,34 @@ func (r *Route) ToModelRoute() *models.Route {
return out
}

// isMutuallyExclusive checks if a single route satisfies the mutual exclusivity constraint
func isMutuallyExclusive(r *Route) bool {
msvechla marked this conversation as resolved.
Show resolved Hide resolved
return !(r.RouteSelector != nil && len(r.Routes) > 0)
}

// 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 {
if !isMutuallyExclusive(r) {
return false
}

// Recursively check all child routes
for _, childRoute := range r.Routes {
if !childRoute.IsRouteSelectorMutuallyExclusive() {
return false
}
}
return true
}

// GrafanaNotificationPolicyStatus defines the observed state of GrafanaNotificationPolicy
type GrafanaNotificationPolicyStatus struct {
GrafanaCommonStatus `json:",inline"`

DiscoveredRoutes *[]string `json:"discoveredRoutes,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

Expand All @@ -137,14 +168,19 @@ type GrafanaNotificationPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec GrafanaNotificationPolicySpec `json:"spec,omitempty"`
Status GrafanaCommonStatus `json:"status,omitempty"`
Spec GrafanaNotificationPolicySpec `json:"spec,omitempty"`
Status GrafanaNotificationPolicyStatus `json:"status,omitempty"`
}

func (np *GrafanaNotificationPolicy) NamespacedResource() string {
return fmt.Sprintf("%v/%v/%v", np.ObjectMeta.Namespace, np.ObjectMeta.Name, np.ObjectMeta.UID)
}

// IsCrossNamespaceImportAllowed returns true when cross namespace imports are allowed
func (np *GrafanaNotificationPolicy) IsCrossNamespaceImportAllowed() bool {
return np.Spec.AllowCrossNamespaceImport
}

Comment on lines +181 to +185
Copy link
Contributor

@Baarsgaard Baarsgaard Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate of AllowCrossNamespace ~20 lines down

//+kubebuilder:object:root=true

// GrafanaNotificationPolicyList contains a list of GrafanaNotificationPolicy
Expand Down
82 changes: 82 additions & 0 deletions api/v1beta1/grafananotificationpolicy_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package v1beta1

import (
"context"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -74,3 +76,83 @@ var _ = Describe("NotificationPolicy type", func() {
})
})
})

func TestIsRouteSelectorMutuallyExclusive(t *testing.T) {
tests := []struct {
name string
route *Route
expected bool
}{
{
name: "Empty route",
route: &Route{},
expected: true,
},
{
name: "Route with only RouteSelector",
route: &Route{
RouteSelector: &metav1.LabelSelector{},
},
expected: true,
},
{
name: "Route with only sub-routes",
route: &Route{
Routes: []*Route{
{},
{},
},
},
expected: true,
},
{
name: "Route with both RouteSelector and sub-routes",
route: &Route{
RouteSelector: &metav1.LabelSelector{},
Routes: []*Route{
{},
},
},
expected: false,
},
{
name: "Nested routes with mutual exclusivity",
route: &Route{
Routes: []*Route{
{
RouteSelector: &metav1.LabelSelector{},
},
{
Routes: []*Route{
{},
},
},
},
},
expected: true,
},
{
name: "Nested routes without mutual exclusivity",
route: &Route{
Routes: []*Route{
{
RouteSelector: &metav1.LabelSelector{},
Routes: []*Route{
{},
},
},
},
},
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.route.IsRouteSelectorMutuallyExclusive()
if result != tt.expected {
t.Errorf("IsRouteSelectorMutuallyExclusive() = %v, want %v", result, tt.expected)
}
})
}
}
58 changes: 58 additions & 0 deletions api/v1beta1/grafananotificationpolicyroute_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright 2022.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1beta1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// GrafanaNotificationPolicyRouteSpec defines the desired state of GrafanaNotificationPolicyRoute
type GrafanaNotificationPolicyRouteSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Route for alerts to match against
Route *Route `json:"route"`
msvechla marked this conversation as resolved.
Show resolved Hide resolved
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// GrafanaNotificationPolicyRoute is the Schema for the grafananotificationpolicyroutes API
type GrafanaNotificationPolicyRoute struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec GrafanaNotificationPolicyRouteSpec `json:"spec,omitempty"`
Status GrafanaCommonStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// GrafanaNotificationPolicyRouteList contains a list of GrafanaNotificationPolicyRoute
type GrafanaNotificationPolicyRouteList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []GrafanaNotificationPolicyRoute `json:"items"`
}

func init() {
SchemeBuilder.Register(&GrafanaNotificationPolicyRoute{}, &GrafanaNotificationPolicyRouteList{})
}
109 changes: 109 additions & 0 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