Skip to content

Commit e2b3cda

Browse files
committed
Use Patch instead of Update for finalizer operations
Refactor all controllers to use Patch() instead of Update() when adding or removing finalizers to improve performance, and to avoid removing non-cached fields erroneously. This is necesary because we no longer cache the last-applied-configuration annotation, so when we add/remove the finalizers, we are removing that field from the metadata. This causes issues with clients when they don't see that annotation (e.g. apply the same ClusterExtension twice). Update ClusterCatalog and ClusterExtension controllers to use Patch-based funalizer management (ClusterExtensionRevision already uses Patch()) Signed-off-by: Todd Short <[email protected]>
1 parent 0a34fc2 commit e2b3cda

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

internal/catalogd/controllers/core/clustercatalog_controller.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package core
1818

1919
import (
2020
"context" // #nosec
21+
"encoding/json"
2122
"errors"
2223
"fmt"
2324
"slices"
@@ -28,6 +29,7 @@ import (
2829
"k8s.io/apimachinery/pkg/api/equality"
2930
"k8s.io/apimachinery/pkg/api/meta"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/types"
3133
"k8s.io/apimachinery/pkg/util/sets"
3234
"k8s.io/apimachinery/pkg/util/wait"
3335
"k8s.io/utils/ptr"
@@ -115,8 +117,8 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque
115117

116118
// Save the finalizers off to the side. If we update the status, the reconciledCatsrc will be updated
117119
// to contain the new state of the ClusterCatalog, which contains the status update, but (critically)
118-
// does not contain the finalizers. After the status update, we need to re-add the finalizers to the
119-
// reconciledCatsrc before updating the object.
120+
// does not contain the finalizers. After the status update, we will use the saved finalizers in the
121+
// Patch()
120122
finalizers := reconciledCatsrc.Finalizers
121123

122124
if updateStatus {
@@ -125,10 +127,17 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque
125127
}
126128
}
127129

128-
reconciledCatsrc.Finalizers = finalizers
129-
130130
if updateFinalizers {
131-
if err := r.Update(ctx, reconciledCatsrc); err != nil {
131+
// Use patch to update finalizers on the server
132+
patch := map[string]any{
133+
"metadata": map[string]any{
134+
"finalizers": finalizers,
135+
},
136+
}
137+
patchJSON, err := json.Marshal(patch)
138+
if err != nil {
139+
reconcileErr = errors.Join(reconcileErr, fmt.Errorf("marshalling patch to ensure finalizers: %w", err))
140+
} else if err := r.Patch(ctx, reconciledCatsrc, client.RawPatch(types.MergePatchType, patchJSON)); err != nil {
132141
reconcileErr = errors.Join(reconcileErr, fmt.Errorf("error updating finalizers: %v", err))
133142
}
134143
}
@@ -415,11 +424,15 @@ func (r *ClusterCatalogReconciler) needsPoll(lastSuccessfulPoll time.Time, catal
415424
return nextPoll.Before(time.Now())
416425
}
417426

418-
// Compare resources - ignoring status & metadata.finalizers
427+
// Compare resources - annotations, labels and spec
419428
func checkForUnexpectedFieldChange(a, b ocv1.ClusterCatalog) bool {
420-
a.Status, b.Status = ocv1.ClusterCatalogStatus{}, ocv1.ClusterCatalogStatus{}
421-
a.Finalizers, b.Finalizers = []string{}, []string{}
422-
return !equality.Semantic.DeepEqual(a, b)
429+
if !equality.Semantic.DeepEqual(a.Annotations, b.Annotations) {
430+
return true
431+
}
432+
if !equality.Semantic.DeepEqual(a.Labels, b.Labels) {
433+
return true
434+
}
435+
return !equality.Semantic.DeepEqual(a.Spec, b.Spec)
423436
}
424437

425438
type finalizerFunc func(ctx context.Context, obj client.Object) (crfinalizer.Result, error)

internal/operator-controller/controllers/clusterextension_controller.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controllers
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"errors"
2223
"fmt"
2324
"io/fs"
@@ -142,18 +143,26 @@ func (r *ClusterExtensionReconciler) Reconcile(ctx context.Context, req ctrl.Req
142143

143144
// Save the finalizers off to the side. If we update the status, the reconciledExt will be updated
144145
// to contain the new state of the ClusterExtension, which contains the status update, but (critically)
145-
// does not contain the finalizers. After the status update, we need to re-add the finalizers to the
146-
// reconciledExt before updating the object.
146+
// does not contain the finalizers. After the status update, we will use the saved finalizers in the
147+
// Patch()
147148
finalizers := reconciledExt.Finalizers
148149
if updateStatus {
149150
if err := r.Client.Status().Update(ctx, reconciledExt); err != nil {
150151
reconcileErr = errors.Join(reconcileErr, fmt.Errorf("error updating status: %v", err))
151152
}
152153
}
153-
reconciledExt.Finalizers = finalizers
154154

155155
if updateFinalizers {
156-
if err := r.Update(ctx, reconciledExt); err != nil {
156+
// Use patch to update finalizers on the server
157+
patch := map[string]any{
158+
"metadata": map[string]any{
159+
"finalizers": finalizers,
160+
},
161+
}
162+
patchJSON, err := json.Marshal(patch)
163+
if err != nil {
164+
reconcileErr = errors.Join(reconcileErr, fmt.Errorf("marshalling patch to ensure finalizers: %w", err))
165+
} else if err := r.Patch(ctx, reconciledExt, client.RawPatch(types.MergePatchType, patchJSON)); err != nil {
157166
reconcileErr = errors.Join(reconcileErr, fmt.Errorf("error updating finalizers: %v", err))
158167
}
159168
}
@@ -179,11 +188,15 @@ func ensureAllConditionsWithReason(ext *ocv1.ClusterExtension, reason v1alpha1.C
179188
}
180189
}
181190

182-
// Compare resources - ignoring status & metadata.finalizers
191+
// Compare resources - annotations, labels and spec
183192
func checkForUnexpectedClusterExtensionFieldChange(a, b ocv1.ClusterExtension) bool {
184-
a.Status, b.Status = ocv1.ClusterExtensionStatus{}, ocv1.ClusterExtensionStatus{}
185-
a.Finalizers, b.Finalizers = []string{}, []string{}
186-
return !equality.Semantic.DeepEqual(a, b)
193+
if !equality.Semantic.DeepEqual(a.Annotations, b.Annotations) {
194+
return true
195+
}
196+
if !equality.Semantic.DeepEqual(a.Labels, b.Labels) {
197+
return true
198+
}
199+
return !equality.Semantic.DeepEqual(a.Spec, b.Spec)
187200
}
188201

189202
// SetDeprecationStatus will set the appropriate deprecation statuses for a ClusterExtension

0 commit comments

Comments
 (0)