@@ -68,10 +68,16 @@ type ProviderAdapter interface {
68
68
// ClaimHandler knows how to allocate and release IP addresses for a specific provider.
69
69
type ClaimHandler interface {
70
70
// 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.
71
74
FetchPool (ctx context.Context ) (client.Object , * ctrl.Result , error )
72
75
// 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.
73
77
EnsureAddress (ctx context.Context , address * ipamv1.IPAddress ) (* ctrl.Result , error )
74
78
// 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.
75
81
ReleaseAddress (ctx context.Context ) (* ctrl.Result , error )
76
82
}
77
83
@@ -138,24 +144,18 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
138
144
cluster , err = clusterutil .GetClusterFromMetadata (ctx , r .Client , claim .ObjectMeta )
139
145
}
140
146
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 {
153
156
log .Info ("IPAddressClaim linked to a cluster that is not found, unable to determine cluster's paused state, skipping reconciliation" )
154
157
return ctrl.Result {}, nil
155
158
}
156
-
157
- log .Error (err , "error fetching cluster linked to IPAddressClaim" )
158
- return ctrl.Result {}, err
159
159
}
160
160
if cluster != nil {
161
161
if annotations .IsPaused (cluster , cluster ) {
@@ -189,8 +189,8 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
189
189
if apierrors .IsNotFound (err ) {
190
190
err := errors .New ("pool not found" )
191
191
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 )
194
194
}
195
195
return ctrl.Result {}, nil
196
196
}
@@ -208,12 +208,8 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
208
208
return ctrl.Result {}, nil
209
209
}
210
210
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 )
217
213
}
218
214
219
215
// 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
263
259
return ctrl.Result {}, nil
264
260
}
265
261
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
+
267
267
address := & ipamv1.IPAddress {}
268
268
namespacedName := types.NamespacedName {
269
269
Namespace : claim .Namespace ,
270
270
Name : claim .Name ,
271
271
}
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" )
274
274
}
275
275
276
276
if address .Name != "" {
277
277
var err error
278
- patch := client .MergeFrom (address .DeepCopy ())
278
+ p := client .MergeFrom (address .DeepCopy ())
279
279
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" )
282
282
}
283
283
}
284
284
285
285
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
288
288
}
289
289
}
290
290
}
291
291
292
292
controllerutil .RemoveFinalizer (claim , ReleaseAddressFinalizer )
293
- return nil
293
+ return ctrl. Result {}, nil
294
294
}
295
295
296
296
func (r * ClaimReconciler ) clusterToIPClaims (_ context.Context , a client.Object ) []reconcile.Request {
0 commit comments