Skip to content

Commit ac99c99

Browse files
committed
fix ReleaseAddress not getting called during deletion
There were two cases where deletion of a claim was reconciled without calling ClaimHandler.ReleaseAddress, which causes issues for providers that manage addresses in external systems.
1 parent c2105ae commit ac99c99

File tree

1 file changed

+32
-32
lines changed

1 file changed

+32
-32
lines changed

pkg/ipamutil/reconciler.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,16 @@ type ProviderAdapter interface {
6868
// ClaimHandler knows how to allocate and release IP addresses for a specific provider.
6969
type ClaimHandler interface {
7070
// FetchPool is called to fetch the pool referenced by the claim. The pool needs to be stored by the handler.
71+
// This method is called before EnsureAddress and ReleaseAddress.
72+
// Note that the ClaimReconciler will call the ReleaseAddress method regardless whether fetching the pool was
73+
// successful or not.
7174
FetchPool(ctx context.Context) (client.Object, *ctrl.Result, error)
7275
// EnsureAddress is called to make sure that the IPAddress.Spec is correct and the address is allocated.
76+
// It will only be called when FetchPool was successful.
7377
EnsureAddress(ctx context.Context, address *ipamv1.IPAddress) (*ctrl.Result, error)
7478
// ReleaseAddress is called to release the ip address that was allocated for the claim.
79+
// It will be called even if FetchPool was not successful when reconciling a deleted claim, so there is no guarantee
80+
// that the claim is available in the handler.
7581
ReleaseAddress(ctx context.Context) (*ctrl.Result, error)
7682
}
7783

@@ -138,24 +144,18 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
138144
cluster, err = clusterutil.GetClusterFromMetadata(ctx, r.Client, claim.ObjectMeta)
139145
}
140146
if err != nil {
141-
if apierrors.IsNotFound(err) {
142-
if !claim.DeletionTimestamp.IsZero() {
143-
patch := client.MergeFrom(claim.DeepCopy())
144-
if err := r.reconcileDelete(ctx, claim); err != nil {
145-
return ctrl.Result{}, fmt.Errorf("reconcile delete: %w", err)
146-
}
147-
// we'll need to explicitly patch the claim here since we haven't set up a patch helper yet.
148-
if err := r.Patch(ctx, claim, patch); err != nil {
149-
return ctrl.Result{}, fmt.Errorf("patch after reconciling delete: %w", err)
150-
}
151-
return ctrl.Result{}, nil
152-
}
147+
if !apierrors.IsNotFound(err) {
148+
log.Error(err, "error fetching cluster linked to IPAddressClaim")
149+
return ctrl.Result{}, err
150+
}
151+
if !claim.ObjectMeta.DeletionTimestamp.IsZero() {
152+
// In case the claim is deleted, we'll process it even when we can't find the cluster. During cluster deletion it
153+
// can happen that the Cluster object is released before all claims are cleaned up, which would block the claims indefinitely.
154+
log.Info("IPAddressClaim linked to a cluster that is not found, unable to determine cluster's paused state, proceeding with deletion")
155+
} else {
153156
log.Info("IPAddressClaim linked to a cluster that is not found, unable to determine cluster's paused state, skipping reconciliation")
154157
return ctrl.Result{}, nil
155158
}
156-
157-
log.Error(err, "error fetching cluster linked to IPAddressClaim")
158-
return ctrl.Result{}, err
159159
}
160160
if cluster != nil {
161161
if annotations.IsPaused(cluster, cluster) {
@@ -189,8 +189,8 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
189189
if apierrors.IsNotFound(err) {
190190
err := errors.New("pool not found")
191191
log.Error(err, "the referenced pool could not be found")
192-
if !claim.DeletionTimestamp.IsZero() {
193-
return ctrl.Result{}, r.reconcileDelete(ctx, claim)
192+
if !claim.ObjectMeta.DeletionTimestamp.IsZero() {
193+
return r.reconcileDelete(ctx, claim, handler)
194194
}
195195
return ctrl.Result{}, nil
196196
}
@@ -208,12 +208,8 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
208208
return ctrl.Result{}, nil
209209
}
210210

211-
// If the claim is marked for deletion, release the address.
212-
if !claim.DeletionTimestamp.IsZero() {
213-
if res, err := handler.ReleaseAddress(ctx); err != nil {
214-
return unwrapResult(res), err
215-
}
216-
return ctrl.Result{}, r.reconcileDelete(ctx, claim)
211+
if !claim.ObjectMeta.DeletionTimestamp.IsZero() {
212+
return r.reconcileDelete(ctx, claim, handler)
217213
}
218214

219215
// We always ensure there is a valid address object passed to the handler.
@@ -263,34 +259,38 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
263259
return ctrl.Result{}, nil
264260
}
265261

266-
func (r *ClaimReconciler) reconcileDelete(ctx context.Context, claim *ipamv1.IPAddressClaim) error {
262+
func (r *ClaimReconciler) reconcileDelete(ctx context.Context, claim *ipamv1.IPAddressClaim, handler ClaimHandler) (ctrl.Result, error) {
263+
if res, err := handler.ReleaseAddress(ctx); err != nil {
264+
return unwrapResult(res), fmt.Errorf("release address: %w", err)
265+
}
266+
267267
address := &ipamv1.IPAddress{}
268268
namespacedName := types.NamespacedName{
269269
Namespace: claim.Namespace,
270270
Name: claim.Name,
271271
}
272-
if err := r.Get(ctx, namespacedName, address); err != nil && !apierrors.IsNotFound(err) {
273-
return errors.Wrap(err, "failed to fetch address")
272+
if err := r.Client.Get(ctx, namespacedName, address); err != nil && !apierrors.IsNotFound(err) {
273+
return ctrl.Result{}, errors.Wrap(err, "failed to fetch address")
274274
}
275275

276276
if address.Name != "" {
277277
var err error
278-
patch := client.MergeFrom(address.DeepCopy())
278+
p := client.MergeFrom(address.DeepCopy())
279279
if controllerutil.RemoveFinalizer(address, ProtectAddressFinalizer) {
280-
if err = r.Patch(ctx, address, patch); err != nil && !apierrors.IsNotFound(err) {
281-
return errors.Wrap(err, "failed to remove address finalizer")
280+
if err = r.Client.Patch(ctx, address, p); err != nil && !apierrors.IsNotFound(err) {
281+
return ctrl.Result{}, errors.Wrap(err, "failed to remove address finalizer")
282282
}
283283
}
284284

285285
if err == nil {
286-
if err := r.Delete(ctx, address); err != nil && !apierrors.IsNotFound(err) {
287-
return err
286+
if err := r.Client.Delete(ctx, address); err != nil && !apierrors.IsNotFound(err) {
287+
return ctrl.Result{}, err
288288
}
289289
}
290290
}
291291

292292
controllerutil.RemoveFinalizer(claim, ReleaseAddressFinalizer)
293-
return nil
293+
return ctrl.Result{}, nil
294294
}
295295

296296
func (r *ClaimReconciler) clusterToIPClaims(_ context.Context, a client.Object) []reconcile.Request {

0 commit comments

Comments
 (0)