diff --git a/api/v1alpha1/conditions_consts.go b/api/v1alpha1/conditions_consts.go index 32d80d51d7..bc1627604a 100644 --- a/api/v1alpha1/conditions_consts.go +++ b/api/v1alpha1/conditions_consts.go @@ -19,4 +19,7 @@ package v1alpha1 const ( // OpenstackFloatingIPPoolReadyCondition reports on the current status of the floating ip pool. Ready indicates that the pool is ready to be used. OpenstackFloatingIPPoolReadyCondition = "OpenstackFloatingIPPoolReadyCondition" + + // MaxIPsReachedReason is set when the maximum number of floating IPs has been reached. + MaxIPsReachedReason = "MaxIPsReached" ) diff --git a/api/v1alpha1/openstackfloatingippool_types.go b/api/v1alpha1/openstackfloatingippool_types.go index 0f6c4e129f..fe3316e4af 100644 --- a/api/v1alpha1/openstackfloatingippool_types.go +++ b/api/v1alpha1/openstackfloatingippool_types.go @@ -56,6 +56,11 @@ type OpenStackFloatingIPPoolSpec struct { // These are used before allocating new ones and are not deleted from OpenStack when the pool is deleted. PreAllocatedFloatingIPs []string `json:"preAllocatedFloatingIPs,omitempty"` + // MaxIPs is the maximum number of floating ips that can be allocated from this pool, if nil there is no limit. + // If set, the pool will stop allocating floating ips when it reaches this number of ClaimedIPs. + // +optional + MaxIPs *int `json:"maxIPs,omitempty"` + // IdentityRef is a reference to a identity to be used when reconciling this pool. // +optional IdentityRef *infrav1alpha7.OpenStackIdentityReference `json:"identityRef,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 0eef1f41d3..353d53b3b7 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -93,6 +93,11 @@ func (in *OpenStackFloatingIPPoolSpec) DeepCopyInto(out *OpenStackFloatingIPPool *out = make([]string, len(*in)) copy(*out, *in) } + if in.MaxIPs != nil { + in, out := &in.MaxIPs, &out.MaxIPs + *out = new(int) + **out = **in + } if in.IdentityRef != nil { in, out := &in.IdentityRef, &out.IdentityRef *out = new(v1alpha7.OpenStackIdentityReference) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml index a38c86c0cc..e7677735be 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackfloatingippools.yaml @@ -86,6 +86,11 @@ spec: - kind - name type: object + maxIPs: + description: |- + MaxIPs is the maximum number of floating ips that can be allocated from this pool, if nil there is no limit. + If set, the pool will stop allocating floating ips when it reaches this number of ClaimedIPs. + type: integer preAllocatedFloatingIPs: description: |- PreAllocatedFloatingIPs is a list of floating IPs precreated in OpenStack that should be used by this pool. diff --git a/controllers/openstackfloatingippool_controller.go b/controllers/openstackfloatingippool_controller.go index a5887e88df..c01a5603dd 100644 --- a/controllers/openstackfloatingippool_controller.go +++ b/controllers/openstackfloatingippool_controller.go @@ -51,6 +51,8 @@ const ( openStackFloatingIPPool = "OpenStackFloatingIPPool" ) +var errMaxIPsReached = errors.New("maximum number of IPs reached") + var backoff = wait.Backoff{ Steps: 4, Duration: 10 * time.Millisecond, @@ -139,6 +141,10 @@ func (r *OpenStackFloatingIPPoolReconciler) Reconcile(ctx context.Context, req c if apierrors.IsNotFound(err) { ip, err := r.getIP(ctx, scope, pool) if err != nil { + if errors.Is(err, errMaxIPsReached) { + log.Info("Maximum number of IPs reached, will not allocate more IPs.") + return ctrl.Result{}, nil + } return ctrl.Result{}, err } @@ -193,6 +199,7 @@ func (r *OpenStackFloatingIPPoolReconciler) Reconcile(ctx context.Context, req c scope.Logger().Info("Claimed IP", "ip", ipAddress.Spec.Address) } } + conditions.MarkTrue(pool, infrav1alpha1.OpenstackFloatingIPPoolReadyCondition) return ctrl.Result{}, r.Client.Status().Update(ctx, pool) } @@ -341,10 +348,18 @@ func (r *OpenStackFloatingIPPoolReconciler) getIP(ctx context.Context, scope sco return "", fmt.Errorf("get floating IP: %w", err) } if fp != nil { + pool.Status.ClaimedIPs = append(pool.Status.ClaimedIPs, fp.FloatingIP) return fp.FloatingIP, nil } pool.Status.FailedIPs = append(pool.Status.FailedIPs, ip) } + maxIPs := pointer.IntDeref(pool.Spec.MaxIPs, -1) + // If we have reached the maximum number of IPs, we should not create more IPs + if maxIPs != -1 && len(pool.Status.ClaimedIPs) >= maxIPs { + scope.Logger().Info("MaxIPs reached", "pool", pool.Name) + conditions.MarkFalse(pool, infrav1alpha1.OpenstackFloatingIPPoolReadyCondition, infrav1alpha1.MaxIPsReachedReason, clusterv1.ConditionSeverityError, "Maximum number of IPs reached, we will not allocate more IPs for this pool") + return "", errMaxIPsReached + } fp, err := networkingService.CreateFloatingIPForPool(pool) if err != nil { @@ -368,7 +383,6 @@ func (r *OpenStackFloatingIPPoolReconciler) getIP(ctx context.Context, scope sco }() conditions.MarkTrue(pool, infrav1alpha1.OpenstackFloatingIPPoolReadyCondition) - ip = fp.FloatingIP pool.Status.ClaimedIPs = append(pool.Status.ClaimedIPs, ip) return ip, nil