@@ -24,6 +24,7 @@ import (
2424 "time"
2525
2626 "github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
27+ "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
2728 "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
2829 corev1 "k8s.io/api/core/v1"
2930 apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -123,30 +124,6 @@ func (r *OpenStackClusterReconciler) Reconcile(ctx context.Context, req ctrl.Req
123124 }
124125 scope := scope .NewWithLogger (clientScope , log )
125126
126- // Resolve and store referenced & dependent resources for the bastion
127- if openStackCluster .Spec .Bastion != nil && openStackCluster .Spec .Bastion .Enabled {
128- if openStackCluster .Status .Bastion == nil {
129- openStackCluster .Status .Bastion = & infrav1.BastionStatus {}
130- }
131- changed , err := compute .ResolveReferencedMachineResources (scope , openStackCluster , & openStackCluster .Spec .Bastion .Instance , & openStackCluster .Status .Bastion .ReferencedResources )
132- if err != nil {
133- return reconcile.Result {}, err
134- }
135- if changed {
136- // If the referenced resources have changed, we need to update the OpenStackCluster status now.
137- return reconcile.Result {}, nil
138- }
139-
140- changed , err = compute .ResolveDependentBastionResources (scope , openStackCluster , bastionName (cluster .Name ))
141- if err != nil {
142- return reconcile.Result {}, err
143- }
144- if changed {
145- // If the dependent resources have changed, we need to update the OpenStackCluster status now.
146- return reconcile.Result {}, nil
147- }
148- }
149-
150127 // Handle deleted clusters
151128 if ! openStackCluster .DeletionTimestamp .IsZero () {
152129 return r .reconcileDelete (ctx , scope , cluster , openStackCluster )
@@ -173,8 +150,17 @@ func (r *OpenStackClusterReconciler) reconcileDelete(ctx context.Context, scope
173150 return ctrl.Result {RequeueAfter : 5 * time .Second }, nil
174151 }
175152
176- if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
177- return reconcile.Result {}, err
153+ // A bastion may have been created if cluster initialisation previously reached populating the network status
154+ // We attempt to delete it even if no status was written, just in case
155+ if openStackCluster .Status .Network != nil {
156+ // Attempt to resolve bastion resources before delete. We don't need to worry about starting if the resources have changed on update.
157+ if _ , err := resolveBastionResources (scope , openStackCluster ); err != nil {
158+ return reconcile.Result {}, err
159+ }
160+
161+ if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
162+ return reconcile.Result {}, err
163+ }
178164 }
179165
180166 networkingService , err := networking .NewService (scope )
@@ -234,6 +220,32 @@ func contains(arr []string, target string) bool {
234220 return false
235221}
236222
223+ func resolveBastionResources (scope * scope.WithLogger , openStackCluster * infrav1.OpenStackCluster ) (bool , error ) {
224+ if openStackCluster .Spec .Bastion != nil && openStackCluster .Spec .Bastion .Enabled {
225+ if openStackCluster .Status .Bastion == nil {
226+ openStackCluster .Status .Bastion = & infrav1.BastionStatus {}
227+ }
228+ changed , err := compute .ResolveReferencedMachineResources (scope , openStackCluster , & openStackCluster .Spec .Bastion .Instance , & openStackCluster .Status .Bastion .ReferencedResources )
229+ if err != nil {
230+ return false , err
231+ }
232+ if changed {
233+ // If the referenced resources have changed, we need to update the OpenStackCluster status now.
234+ return true , nil
235+ }
236+
237+ changed , err = compute .ResolveDependentBastionResources (scope , openStackCluster , bastionName (openStackCluster .Name ))
238+ if err != nil {
239+ return false , err
240+ }
241+ if changed {
242+ // If the dependent resources have changed, we need to update the OpenStackCluster status now.
243+ return true , nil
244+ }
245+ }
246+ return false , nil
247+ }
248+
237249func deleteBastion (scope * scope.WithLogger , cluster * clusterv1.Cluster , openStackCluster * infrav1.OpenStackCluster ) error {
238250 scope .Logger ().Info ("Deleting Bastion" )
239251
@@ -307,7 +319,7 @@ func deleteBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openStac
307319 openStackCluster .Status .Bastion .DependentResources .Ports = nil
308320 }
309321
310- scope .Logger ().Info ("Deleted Bastion for cluster %s" , cluster . Name )
322+ scope .Logger ().Info ("Deleted Bastion" )
311323
312324 openStackCluster .Status .Bastion = nil
313325 delete (openStackCluster .ObjectMeta .Annotations , BastionInstanceHashAnnotation )
@@ -335,8 +347,11 @@ func reconcileNormal(scope *scope.WithLogger, cluster *clusterv1.Cluster, openSt
335347 }
336348
337349 result , err := reconcileBastion (scope , cluster , openStackCluster )
338- if err != nil || ! reflect .DeepEqual (result , reconcile.Result {}) {
339- return result , err
350+ if err != nil {
351+ return reconcile.Result {}, err
352+ }
353+ if result != nil {
354+ return * result , nil
340355 }
341356
342357 availabilityZones , err := computeService .GetAvailabilityZones ()
@@ -366,102 +381,126 @@ func reconcileNormal(scope *scope.WithLogger, cluster *clusterv1.Cluster, openSt
366381 return reconcile.Result {}, nil
367382}
368383
369- func reconcileBastion (scope * scope.WithLogger , cluster * clusterv1.Cluster , openStackCluster * infrav1.OpenStackCluster ) (ctrl.Result , error ) {
370- scope .Logger ().Info ("Reconciling Bastion" )
384+ func reconcileBastion (scope * scope.WithLogger , cluster * clusterv1.Cluster , openStackCluster * infrav1.OpenStackCluster ) (* ctrl.Result , error ) {
385+ scope .Logger ().V ( 4 ). Info ("Reconciling Bastion" )
371386
372- if openStackCluster .Spec .Bastion == nil || ! openStackCluster .Spec .Bastion .Enabled {
373- return reconcile.Result {}, deleteBastion (scope , cluster , openStackCluster )
387+ changed , err := resolveBastionResources (scope , openStackCluster )
388+ if err != nil {
389+ return nil , err
390+ }
391+ if changed {
392+ return & reconcile.Result {}, nil
374393 }
375394
376- // If ports options aren't in the status, we'll re-trigger the reconcile to get them
377- // via adopting the referenced resources.
378- if len (openStackCluster .Status .Bastion .ReferencedResources .Ports ) == 0 {
379- return reconcile.Result {}, nil
395+ // No Bastion defined
396+ if openStackCluster .Spec .Bastion == nil || ! openStackCluster .Spec .Bastion .Enabled {
397+ // Delete any existing bastion
398+ if openStackCluster .Status .Bastion != nil {
399+ if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
400+ return nil , err
401+ }
402+ // Reconcile again before continuing
403+ return & reconcile.Result {}, nil
404+ }
405+
406+ // Otherwise nothing to do
407+ return nil , nil
380408 }
381409
382410 computeService , err := compute .NewService (scope )
383411 if err != nil {
384- return reconcile. Result {} , err
412+ return nil , err
385413 }
386414
387415 networkingService , err := networking .NewService (scope )
388416 if err != nil {
389- return reconcile. Result {} , err
417+ return nil , err
390418 }
391419
392420 instanceSpec , err := bastionToInstanceSpec (openStackCluster , cluster )
393421 if err != nil {
394- return reconcile. Result {} , err
422+ return nil , err
395423 }
396424 clusterName := fmt .Sprintf ("%s-%s" , cluster .Namespace , cluster .Name )
425+
397426 bastionHash , err := compute .HashInstanceSpec (instanceSpec )
398427 if err != nil {
399- return reconcile. Result {} , fmt .Errorf ("failed computing bastion hash from instance spec: %w" , err )
428+ return nil , fmt .Errorf ("failed computing bastion hash from instance spec: %w" , err )
400429 }
401430 if bastionHashHasChanged (bastionHash , openStackCluster .ObjectMeta .Annotations ) {
402431 if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
403- return ctrl. Result {} , err
432+ return nil , err
404433 }
434+
435+ // Add the new annotation and reconcile again before continuing
436+ annotations .AddAnnotations (openStackCluster , map [string ]string {BastionInstanceHashAnnotation : bastionHash })
437+ return & reconcile.Result {}, nil
405438 }
406439
407440 err = getOrCreateBastionPorts (openStackCluster , networkingService , cluster .Name )
408441 if err != nil {
409442 handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create ports for bastion: %w" , err ))
410- return ctrl. Result {} , fmt .Errorf ("failed to get or create ports for bastion: %w" , err )
443+ return nil , fmt .Errorf ("failed to get or create ports for bastion: %w" , err )
411444 }
412445 bastionPortIDs := GetPortIDs (openStackCluster .Status .Bastion .DependentResources .Ports )
413446
414447 var instanceStatus * compute.InstanceStatus
415448 if openStackCluster .Status .Bastion != nil && openStackCluster .Status .Bastion .ID != "" {
416449 if instanceStatus , err = computeService .GetInstanceStatus (openStackCluster .Status .Bastion .ID ); err != nil {
417- return reconcile. Result {} , err
450+ return nil , err
418451 }
419452 }
420453 if instanceStatus == nil {
421454 // Check if there is an existing instance with bastion name, in case where bastion ID would not have been properly stored in cluster status
422455 if instanceStatus , err = computeService .GetInstanceStatusByName (openStackCluster , instanceSpec .Name ); err != nil {
423- return reconcile. Result {} , err
456+ return nil , err
424457 }
425458 }
426459 if instanceStatus == nil {
427460 instanceStatus , err = computeService .CreateInstance (openStackCluster , instanceSpec , bastionPortIDs )
428461 if err != nil {
429- return reconcile. Result {} , fmt .Errorf ("failed to create bastion: %w" , err )
462+ return nil , fmt .Errorf ("failed to create bastion: %w" , err )
430463 }
431464 }
432465
433466 // Save hash & status as soon as we know we have an instance
434467 instanceStatus .UpdateBastionStatus (openStackCluster )
435- annotations .AddAnnotations (openStackCluster , map [string ]string {BastionInstanceHashAnnotation : bastionHash })
436468
437469 // Make sure that bastion instance has a valid state
438470 switch instanceStatus .State () {
439471 case infrav1 .InstanceStateError :
440- return ctrl. Result {} , fmt .Errorf ("failed to reconcile bastion, instance state is ERROR" )
472+ return nil , fmt .Errorf ("failed to reconcile bastion, instance state is ERROR" )
441473 case infrav1 .InstanceStateBuild , infrav1 .InstanceStateUndefined :
442474 scope .Logger ().Info ("Waiting for bastion instance to become ACTIVE" , "id" , instanceStatus .ID (), "status" , instanceStatus .State ())
443- return ctrl .Result {RequeueAfter : waitForBuildingInstanceToReconcile }, nil
475+ return & reconcile .Result {RequeueAfter : waitForBuildingInstanceToReconcile }, nil
444476 case infrav1 .InstanceStateDeleted :
445- // This should normally be handled by deleteBastion
446- openStackCluster .Status .Bastion = nil
447- return ctrl.Result {}, nil
477+ // Not clear why this would happen, so try to clean everything up before reconciling again
478+ if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
479+ return nil , err
480+ }
481+ return & reconcile.Result {}, nil
448482 }
449483
450484 port , err := computeService .GetManagementPort (openStackCluster , instanceStatus )
451485 if err != nil {
452486 err = fmt .Errorf ("getting management port for bastion: %w" , err )
453487 handleUpdateOSCError (openStackCluster , err )
454- return ctrl. Result {} , err
488+ return nil , err
455489 }
490+
491+ return bastionAddFloatingIP (openStackCluster , clusterName , port , networkingService )
492+ }
493+
494+ func bastionAddFloatingIP (openStackCluster * infrav1.OpenStackCluster , clusterName string , port * ports.Port , networkingService * networking.Service ) (* reconcile.Result , error ) {
456495 fp , err := networkingService .GetFloatingIPByPortID (port .ID )
457496 if err != nil {
458497 handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err ))
459- return ctrl. Result {} , fmt .Errorf ("failed to get floating IP for bastion port: %w" , err )
498+ return nil , fmt .Errorf ("failed to get floating IP for bastion port: %w" , err )
460499 }
461500 if fp != nil {
462501 // Floating IP is already attached to bastion, no need to proceed
463502 openStackCluster .Status .Bastion .FloatingIP = fp .FloatingIP
464- return ctrl. Result {} , nil
503+ return nil , nil
465504 }
466505
467506 var floatingIP * string
@@ -477,17 +516,17 @@ func reconcileBastion(scope *scope.WithLogger, cluster *clusterv1.Cluster, openS
477516 fp , err = networkingService .GetOrCreateFloatingIP (openStackCluster , openStackCluster , clusterName , floatingIP )
478517 if err != nil {
479518 handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err ))
480- return ctrl. Result {} , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err )
519+ return nil , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err )
481520 }
482521 openStackCluster .Status .Bastion .FloatingIP = fp .FloatingIP
483522
484523 err = networkingService .AssociateFloatingIP (openStackCluster , fp , port .ID )
485524 if err != nil {
486525 handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to associate floating IP with bastion: %w" , err ))
487- return ctrl. Result {} , fmt .Errorf ("failed to associate floating IP with bastion: %w" , err )
526+ return nil , fmt .Errorf ("failed to associate floating IP with bastion: %w" , err )
488527 }
489528
490- return ctrl. Result {} , nil
529+ return nil , nil
491530}
492531
493532func bastionToInstanceSpec (openStackCluster * infrav1.OpenStackCluster , cluster * clusterv1.Cluster ) (* compute.InstanceSpec , error ) {
0 commit comments