Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: use annotation to prevent policy conflicts
Browse files Browse the repository at this point in the history
theSuess committed Jul 17, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 78f0175 commit 43bde21
Showing 6 changed files with 37 additions and 4 deletions.
6 changes: 6 additions & 0 deletions api/v1beta1/grafananotificationpolicy_types.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@ limitations under the License.
package v1beta1

import (
"fmt"

"github.com/grafana/grafana-openapi-client-go/models"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -144,6 +146,10 @@ type GrafanaNotificationPolicy struct {
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)
}

//+kubebuilder:object:root=true

// GrafanaNotificationPolicyList contains a list of GrafanaNotificationPolicy
Original file line number Diff line number Diff line change
@@ -96,6 +96,7 @@ spec:
pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
type: string
route:
description: Routes for alerts to match against
properties:
continue:
description: continue
6 changes: 3 additions & 3 deletions config/samples/grafana_v1beta1_grafananotificationpolicy.yaml
Original file line number Diff line number Diff line change
@@ -7,18 +7,18 @@ spec:
matchLabels:
dashboards: "grafana"
route:
receiver: grafana cloud oncall
receiver: Grafana Cloud OnCall
group_by:
- grafana_folder
- alertname
routes:
- receiver: some-other-receiver
- receiver: grafana-default-email
object_matchers:
- - foo
- =
- bar
routes:
- receiver: a_third_receiver
- receiver: Grafana Cloud OnCall
object_matchers:
- - severity
- =
2 changes: 2 additions & 0 deletions controllers/controller_shared.go
Original file line number Diff line number Diff line change
@@ -27,6 +27,8 @@ const (
conditionInvalidSpec = "InvalidSpec"
)

const annotationAppliedNotificationPolicy = "operator.grafana.com/applied-notificationpolicy"

//+kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete

func GetMatchingInstances(ctx context.Context, k8sClient client.Client, labelSelector *metav1.LabelSelector) (v1beta1.GrafanaList, error) {
25 changes: 24 additions & 1 deletion controllers/notificationpolicy_controller.go
Original file line number Diff line number Diff line change
@@ -121,9 +121,17 @@ func (r *GrafanaNotificationPolicyReconciler) Reconcile(ctx context.Context, req
removeNoMatchingInstance(&notificationPolicy.Status.Conditions)

applyErrors := make(map[string]string)
appliedCount := 0
for _, grafana := range instances.Items {
// can be removed in go 1.22+
grafana := grafana
appliedPolicy := grafana.Annotations[annotationAppliedNotificationPolicy]
if appliedPolicy != "" && appliedPolicy != notificationPolicy.NamespacedResource() {
controllerLog.Info("instance already has a different notification policy applied - skipping", "grafana", grafana.Name)
continue
}
appliedCount++

if grafana.Status.Stage != grafanav1beta1.OperatorStageComplete || grafana.Status.StageStatus != grafanav1beta1.OperatorStageResultSuccess {
controllerLog.Info("grafana instance not ready", "grafana", grafana.Name)
continue
@@ -134,7 +142,7 @@ func (r *GrafanaNotificationPolicyReconciler) Reconcile(ctx context.Context, req
applyErrors[fmt.Sprintf("%s/%s", grafana.Namespace, grafana.Name)] = err.Error()
}
}
condition := buildSynchronizedCondition("Notification Policy", conditionFolderSynchronized, notificationPolicy.Generation, applyErrors, len(instances.Items))
condition := buildSynchronizedCondition("Notification Policy", conditionNotificationPolicySynchronized, notificationPolicy.Generation, applyErrors, appliedCount)
meta.SetStatusCondition(&notificationPolicy.Status.Conditions, condition)

return ctrl.Result{RequeueAfter: notificationPolicy.Spec.ResyncPeriod.Duration}, nil
@@ -151,6 +159,10 @@ func (r *GrafanaNotificationPolicyReconciler) reconcileWithInstance(ctx context.
if _, err := cl.Provisioning.PutPolicyTree(params); err != nil { //nolint:errcheck
return fmt.Errorf("applying notification policy: %w", err)
}
instance.Annotations[annotationAppliedNotificationPolicy] = notificationPolicy.NamespacedResource()
if err := r.Client.Update(ctx, instance); err != nil {
return fmt.Errorf("saving applied policy to instance CR: %w", err)
}
return nil
}

@@ -162,6 +174,11 @@ func (r *GrafanaNotificationPolicyReconciler) resetInstance(ctx context.Context,
if _, err := cl.Provisioning.ResetPolicyTree(); err != nil { //nolint:errcheck
return fmt.Errorf("resetting policy tree")
}
delete(instance.Annotations, annotationAppliedNotificationPolicy)
if err := r.Client.Update(ctx, instance); err != nil {
return fmt.Errorf("removing applied policy from instance CR: %w", err)
}

return nil
}

@@ -174,6 +191,12 @@ func (r *GrafanaNotificationPolicyReconciler) finalize(ctx context.Context, noti
}
for _, i := range instances.Items {
instance := i
appliedPolicy := i.Annotations[annotationAppliedNotificationPolicy]
if appliedPolicy != "" && appliedPolicy != notificationPolicy.NamespacedResource() {
r.Log.Info("instance already has a different notification policy applied - skipping", "grafana", instance.Name)
continue
}

if err := r.resetInstance(ctx, &instance); err != nil {
return fmt.Errorf("resetting instance notification policy: %w", err)
}
Original file line number Diff line number Diff line change
@@ -96,6 +96,7 @@ spec:
pattern: ^([0-9]+(\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$
type: string
route:
description: Routes for alerts to match against
properties:
continue:
description: continue

0 comments on commit 43bde21

Please sign in to comment.