diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index 8c112737d..15ad6430f 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -19,7 +19,9 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" ) @@ -366,3 +368,35 @@ func getReferencedValue(ctx context.Context, cl client.Client, cr metav1.ObjectM } } } + +// Add finalizer through a MergePatch +// Avoids updating the entire object and only changes the finalizers +func addFinalizer(ctx context.Context, cl client.Client, cr client.Object) error { + // Only update when changed + if controllerutil.AddFinalizer(cr, grafanaFinalizer) { + return patchFinalizers(ctx, cl, cr) + } + return nil +} + +// Remove finalizer through a MergePatch +// Avoids updating the entire object and only changes the finalizers +func removeFinalizer(ctx context.Context, cl client.Client, cr client.Object) error { + // Only update when changed + if controllerutil.RemoveFinalizer(cr, grafanaFinalizer) { + return patchFinalizers(ctx, cl, cr) + } + return nil +} + +// Helper func for add/remove, avoid using directly +func patchFinalizers(ctx context.Context, cl client.Client, cr client.Object) error { + crFinalizers := cr.GetFinalizers() + + // Create patch using slice + patch, err := json.Marshal(map[string]interface{}{"metadata": map[string]interface{}{"finalizers": crFinalizers}}) + if err != nil { + return err + } + return cl.Patch(ctx, cr, client.RawPatch(types.MergePatchType, patch)) +} diff --git a/controllers/grafanaalertrulegroup_controller.go b/controllers/grafanaalertrulegroup_controller.go index 72a39a074..ab730b489 100644 --- a/controllers/grafanaalertrulegroup_controller.go +++ b/controllers/grafanaalertrulegroup_controller.go @@ -76,16 +76,13 @@ func (r *GrafanaAlertRuleGroupReconciler) Reconcile(ctx context.Context, req ctr } if group.GetDeletionTimestamp() != nil { + // Check if resource needs clean up if controllerutil.ContainsFinalizer(group, grafanaFinalizer) { - // still need to clean up - err := r.finalize(ctx, group) - if err != nil { - return ctrl.Result{}, fmt.Errorf("cleaning up alert rule group: %w", err) + if err := r.finalize(ctx, group); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to finalize GrafanaAlertRuleGroup: %w", err) } - controllerutil.RemoveFinalizer(group, grafanaFinalizer) - if err := r.Update(ctx, group); err != nil { - r.Log.Error(err, "failed to remove finalizer") - return ctrl.Result{}, err + if err := removeFinalizer(ctx, r.Client, group); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) } } return ctrl.Result{}, nil @@ -96,12 +93,13 @@ func (r *GrafanaAlertRuleGroupReconciler) Reconcile(ctx context.Context, req ctr r.Log.Error(err, "updating status") } if meta.IsStatusConditionTrue(group.Status.Conditions, conditionNoMatchingInstance) { - controllerutil.RemoveFinalizer(group, grafanaFinalizer) + if err := removeFinalizer(ctx, r.Client, group); err != nil { + r.Log.Error(err, "failed to remove finalizer") + } } else { - controllerutil.AddFinalizer(group, grafanaFinalizer) - } - if err := r.Update(ctx, group); err != nil { - r.Log.Error(err, "failed to set finalizer") + if err := addFinalizer(ctx, r.Client, group); err != nil { + r.Log.Error(err, "failed to set finalizer") + } } }() diff --git a/controllers/grafanacontactpoint_controller.go b/controllers/grafanacontactpoint_controller.go index a62456824..1340b447b 100644 --- a/controllers/grafanacontactpoint_controller.go +++ b/controllers/grafanacontactpoint_controller.go @@ -82,15 +82,13 @@ func (r *GrafanaContactPointReconciler) Reconcile(ctx context.Context, req ctrl. } if contactPoint.GetDeletionTimestamp() != nil { + // Check if resource needs clean up if controllerutil.ContainsFinalizer(contactPoint, grafanaFinalizer) { - err := r.finalize(ctx, contactPoint) - if err != nil { - return ctrl.Result{RequeueAfter: RequeueDelay}, fmt.Errorf("failed to finalize GrafanaContactPoint: %w", err) + if err := r.finalize(ctx, contactPoint); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to finalize GrafanaContactPoint: %w", err) } - controllerutil.RemoveFinalizer(contactPoint, grafanaFinalizer) - if err := r.Update(ctx, contactPoint); err != nil { - r.Log.Error(err, "failed to remove finalizer") - return ctrl.Result{RequeueAfter: RequeueDelay}, fmt.Errorf("failed to update GrafanaContactPoint: %w", err) + if err := removeFinalizer(ctx, r.Client, contactPoint); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) } } return ctrl.Result{}, nil @@ -101,12 +99,13 @@ func (r *GrafanaContactPointReconciler) Reconcile(ctx context.Context, req ctrl. r.Log.Error(err, "updating status") } if meta.IsStatusConditionTrue(contactPoint.Status.Conditions, conditionNoMatchingInstance) { - controllerutil.RemoveFinalizer(contactPoint, grafanaFinalizer) + if err := removeFinalizer(ctx, r.Client, contactPoint); err != nil { + r.Log.Error(err, "failed to remove finalizer") + } } else { - controllerutil.AddFinalizer(contactPoint, grafanaFinalizer) - } - if err := r.Update(ctx, contactPoint); err != nil { - r.Log.Error(err, "failed to set finalizer") + if err := addFinalizer(ctx, r.Client, contactPoint); err != nil { + r.Log.Error(err, "failed to set finalizer") + } } }() diff --git a/controllers/notificationpolicy_controller.go b/controllers/notificationpolicy_controller.go index 7fa34cf21..0f8188691 100644 --- a/controllers/notificationpolicy_controller.go +++ b/controllers/notificationpolicy_controller.go @@ -80,15 +80,13 @@ func (r *GrafanaNotificationPolicyReconciler) Reconcile(ctx context.Context, req } if notificationPolicy.GetDeletionTimestamp() != nil { + // Check if resource needs clean up if controllerutil.ContainsFinalizer(notificationPolicy, grafanaFinalizer) { - err := r.finalize(ctx, notificationPolicy) - if err != nil { - return ctrl.Result{RequeueAfter: RequeueDelay}, fmt.Errorf("failed to finalize GrafanaNotificationPolicy: %w", err) + if err := r.finalize(ctx, notificationPolicy); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to finalize GrafanaNotificationPolicy: %w", err) } - controllerutil.RemoveFinalizer(notificationPolicy, grafanaFinalizer) - if err := r.Update(ctx, notificationPolicy); err != nil { - r.Log.Error(err, "failed to remove finalizer") - return ctrl.Result{RequeueAfter: RequeueDelay}, fmt.Errorf("failed to update GrafanaNotificationPolicy: %w", err) + if err := removeFinalizer(ctx, r.Client, notificationPolicy); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) } } return ctrl.Result{}, nil @@ -99,12 +97,13 @@ func (r *GrafanaNotificationPolicyReconciler) Reconcile(ctx context.Context, req r.Log.Error(err, "updating status") } if meta.IsStatusConditionTrue(notificationPolicy.Status.Conditions, conditionNoMatchingInstance) { - controllerutil.RemoveFinalizer(notificationPolicy, grafanaFinalizer) + if err := removeFinalizer(ctx, r.Client, notificationPolicy); err != nil { + r.Log.Error(err, "failed to remove finalizer") + } } else { - controllerutil.AddFinalizer(notificationPolicy, grafanaFinalizer) - } - if err := r.Update(ctx, notificationPolicy); err != nil { - r.Log.Error(err, "failed to set finalizer") + if err := addFinalizer(ctx, r.Client, notificationPolicy); err != nil { + r.Log.Error(err, "failed to set finalizer") + } } }()