Skip to content

Commit 404c2e7

Browse files
author
Ricardo Pchevuzinske Katz
committed
Validate the cache contains the created address before moving on
IPAM controller relies on controller runtime client, which is cached, to list the existing address before allocating a new IP. When the cache is too slow to reflect the changes, we may end up with an IPAddressClaim reconciliation happening before the previous reconciliation address acknowledge by the cache, which may end with duplicated IP address allocations. This change enforces that the Reconcile function waits for the cached client to be aware of the IPAddress before allowing the reconciliation to move on to the next reconciliation request
1 parent 01f9a1e commit 404c2e7

File tree

1 file changed

+19
-0
lines changed

1 file changed

+19
-0
lines changed

pkg/ipamutil/reconciler.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package ipamutil
33
import (
44
"context"
55
"fmt"
6+
"time"
67

78
"github.com/pkg/errors"
89
corev1 "k8s.io/api/core/v1"
910
apierrors "k8s.io/apimachinery/pkg/api/errors"
1011
"k8s.io/apimachinery/pkg/runtime"
1112
"k8s.io/apimachinery/pkg/types"
1213
kerrors "k8s.io/apimachinery/pkg/util/errors"
14+
"k8s.io/apimachinery/pkg/util/wait"
1315
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1416
ipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
1517
clusterutil "sigs.k8s.io/cluster-api/util"
@@ -245,6 +247,23 @@ func (r *ClaimReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ct
245247
return unwrapResult(res), err
246248
}
247249

250+
// We need to ensure the address is properly watched by controller-runtime client (as it is cached) before moving
251+
// to the next reconciliation request.
252+
// This is required, otherwise if the next "EnsureAddress" is called too fast, or the controller-runtime cached client informer
253+
// is too slow, we may end up not seeing that the IP Address is being used and can provide duplicated IPs.
254+
err = wait.PollUntilContextTimeout(ctx, 5*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
255+
key := client.ObjectKeyFromObject(&address)
256+
if err := r.Client.Get(ctx, key, &ipamv1.IPAddress{}); err != nil {
257+
return false, client.IgnoreNotFound(err)
258+
}
259+
return true, nil
260+
})
261+
262+
if err != nil {
263+
log.Error(err, "failed waiting for IPAddress to be visible in the cache after create", "namespace", address.GetNamespace(), "name", address.GetName())
264+
return ctrl.Result{}, errors.Wrapf(err, "failed waiting for IPAddress %s/%s to be visible in the cache after create", address.GetNamespace(), address.GetName())
265+
}
266+
248267
log.Info(fmt.Sprintf("IPAddress %s/%s (%s) has been %s", address.Namespace, address.Name, address.Spec.Address, operationResult),
249268
"IPAddressClaim", fmt.Sprintf("%s/%s", claim.Namespace, claim.Name))
250269

0 commit comments

Comments
 (0)