Skip to content

Commit 6ccf996

Browse files
EmilienMk8s-infra-cherrypick-robot
authored andcommitted
Better conditions for creating Floating IPs
When a floating is created, we need to make sure that `OpenStackCluster.Spec.DisableExternalNetwork` is not set to `True`. Otherwise, we'll have a nil pointer error. * Add a check in `reconcileBastion` to check that external network is not disabled before creating the floating IP for the bastion. * Add a check in `reconcileControlPlaneEndpoint` and `reconcileAPIServerLoadBalancer` to check that external network is not disabled (alongside the DisableAPIServerFloatingIP check) before creating the floating IP for the API server endpoint. * Add a safeguard in `GetOrCreateFloatingIP` to return a proper error (instead of a nil pointer error) when `openStackCluster.Status.ExternalNetwork` is nil. * Add API CEL to check that when DisableExternalNetwork is set and true, the bastion (if defined) doesn't have a floating IP defined and also that disableAPIServerFloatingIP (when set) is not False.
1 parent da57204 commit 6ccf996

File tree

8 files changed

+62
-4
lines changed

8 files changed

+62
-4
lines changed

api/v1beta1/openstackcluster_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const (
3131
)
3232

3333
// OpenStackClusterSpec defines the desired state of OpenStackCluster.
34+
// +kubebuilder:validation:XValidation:rule="has(self.disableExternalNetwork) && self.disableExternalNetwork ? !has(self.bastion) || !has(self.bastion.floatingIP) : true",message="bastion floating IP cannot be set when disableExternalNetwork is true"
35+
// +kubebuilder:validation:XValidation:rule="has(self.disableExternalNetwork) && self.disableExternalNetwork ? has(self.disableAPIServerFloatingIP) && self.disableAPIServerFloatingIP : true",message="disableAPIServerFloatingIP cannot be false when disableExternalNetwork is true"
3436
type OpenStackClusterSpec struct {
3537
// ManagedSubnets describe OpenStack Subnets to be created. Cluster actuator will create a network,
3638
// subnets with the defined CIDR, and a router connected to these subnets. Currently only one IPv4

config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclustertemplates.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/openstackcluster_controller.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,11 @@ func (r *OpenStackClusterReconciler) reconcileBastion(ctx context.Context, scope
395395
return nil, err
396396
}
397397

398-
return bastionAddFloatingIP(openStackCluster, clusterResourceName, port, networkingService)
398+
if !ptr.Deref(openStackCluster.Spec.DisableExternalNetwork, false) {
399+
return bastionAddFloatingIP(openStackCluster, clusterResourceName, port, networkingService)
400+
}
401+
402+
return nil, nil
399403
}
400404

401405
func bastionAddFloatingIP(openStackCluster *infrav1.OpenStackCluster, clusterResourceName string, port *ports.Port, networkingService *networking.Service) (*reconcile.Result, error) {
@@ -798,9 +802,9 @@ func reconcileControlPlaneEndpoint(scope *scope.WithLogger, networkingService *n
798802
case openStackCluster.Spec.ControlPlaneEndpoint != nil && openStackCluster.Spec.ControlPlaneEndpoint.IsValid():
799803
host = openStackCluster.Spec.ControlPlaneEndpoint.Host
800804

801-
// API server load balancer is disabled, but floating IP is not. Create
805+
// API server load balancer is disabled, but external netowork and floating IP are not. Create
802806
// a floating IP to be attached directly to a control plane host.
803-
case !ptr.Deref(openStackCluster.Spec.DisableAPIServerFloatingIP, false):
807+
case !ptr.Deref(openStackCluster.Spec.DisableAPIServerFloatingIP, false) && !ptr.Deref(openStackCluster.Spec.DisableExternalNetwork, false):
804808
fp, err := networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterResourceName, openStackCluster.Spec.APIServerFloatingIP)
805809
if err != nil {
806810
handleUpdateOSCError(openStackCluster, fmt.Errorf("floating IP cannot be got or created: %w", err), false)

controllers/openstackmachine_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ func (r *OpenStackMachineReconciler) reconcileAPIServerLoadBalancer(scope *scope
628628
conditions.MarkFalse(openStackMachine, infrav1.APIServerIngressReadyCondition, infrav1.LoadBalancerMemberErrorReason, clusterv1.ConditionSeverityError, "Reconciling load balancer member failed: %v", err)
629629
return fmt.Errorf("reconcile load balancer member: %w", err)
630630
}
631-
} else if !ptr.Deref(openStackCluster.Spec.DisableAPIServerFloatingIP, false) {
631+
} else if !ptr.Deref(openStackCluster.Spec.DisableAPIServerFloatingIP, false) && !ptr.Deref(openStackCluster.Spec.DisableExternalNetwork, false) {
632632
var floatingIPAddress *string
633633
switch {
634634
case openStackCluster.Spec.ControlPlaneEndpoint != nil && openStackCluster.Spec.ControlPlaneEndpoint.IsValid():

docs/book/src/clusteropenstack/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ associated to the first controller node and the other controller nodes have no f
270270
to any other controller node. So we recommend to only set one controller node when floating IP is needed,
271271
or please consider using load balancer instead, see [issue #1265](https://github.com/kubernetes-sigs/cluster-api-provider-openstack/issues/1265) for further information.
272272

273+
Note: `spec.disableExternalNetwork` must be unset or set to `false` to allow the API server to have a floating IP.
274+
273275
### Disabling the API server floating IP
274276

275277
It is possible to provision a cluster without a floating IP for the API server by setting
@@ -717,6 +719,8 @@ spec:
717719
floatingIP: <Floating IP address>
718720
```
719721

722+
Note: A floating IP can only be added if `OpenStackCluster.Spec.DisableExternalNetwork` is not set or set to `false`.
723+
720724
If `managedSecurityGroups` is set to a non-nil value (e.g. `{}`), security group rule opening 22/tcp is added to security groups for bastion, controller, and worker nodes respectively. Otherwise, you have to add `securityGroups` to the `bastion` in `OpenStackCluster` spec and `OpenStackMachineTemplate` spec template respectively.
721725

722726
### Making changes to the bastion host

pkg/cloud/services/networking/floatingip.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func (s *Service) GetOrCreateFloatingIP(eventObject runtime.Object, openStackClu
3838
var err error
3939
var fpCreateOpts floatingips.CreateOpts
4040

41+
// This is a safeguard, we shouldn't reach it and if we do, it's something to fix in the caller of the function.
42+
if openStackCluster.Status.ExternalNetwork == nil {
43+
return nil, fmt.Errorf("external network not found")
44+
}
45+
4146
if ptr.Deref(ip, "") != "" {
4247
fp, err = s.GetFloatingIP(*ip)
4348
if err != nil {

test/e2e/suites/apivalidations/openstackcluster_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,29 @@ var _ = Describe("OpenStackCluster API validations", func() {
178178
Expect(createObj(cluster)).NotTo(Succeed(), "OpenStackCluster creation should not succeed")
179179
})
180180

181+
It("should not allow a bastion floating IP with DisableExternalNetwork set to true", func() {
182+
cluster.Spec.Bastion = &infrav1.Bastion{
183+
Enabled: ptr.To(true),
184+
Spec: &infrav1.OpenStackMachineSpec{
185+
Flavor: ptr.To("flavor-name"),
186+
Image: infrav1.ImageParam{
187+
Filter: &infrav1.ImageFilter{
188+
Name: ptr.To("fake-image"),
189+
},
190+
},
191+
},
192+
FloatingIP: ptr.To("10.0.0.0"),
193+
}
194+
cluster.Spec.DisableExternalNetwork = ptr.To(true)
195+
Expect(createObj(cluster)).ToNot(Succeed(), "OpenStackCluster creation should not succeed")
196+
})
197+
198+
It("should not allow DisableAPIServerFloatingIP to be false with DisableExternalNetwork set to true", func() {
199+
cluster.Spec.DisableAPIServerFloatingIP = ptr.To(false)
200+
cluster.Spec.DisableExternalNetwork = ptr.To(true)
201+
Expect(createObj(cluster)).ToNot(Succeed(), "OpenStackCluster creation should not succeed")
202+
})
203+
181204
// We must use unstructured to set values which can't be marshalled by the Go type
182205
unstructuredClusterWithAPIPort := func(apiServerPort any) *unstructured.Unstructured {
183206
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(cluster)

0 commit comments

Comments
 (0)