@@ -215,10 +215,24 @@ func deleteBastion(scope scope.Scope, cluster *clusterv1.Cluster, openStackClust
215
215
return err
216
216
}
217
217
218
- instanceName := fmt .Sprintf ("%s-bastion" , cluster .Name )
219
- instanceStatus , err := computeService .GetInstanceStatusByName (openStackCluster , instanceName )
220
- if err != nil {
221
- return err
218
+ if openStackCluster .Status .Bastion != nil && openStackCluster .Status .Bastion .FloatingIP != "" {
219
+ if err = networkingService .DeleteFloatingIP (openStackCluster , openStackCluster .Status .Bastion .FloatingIP ); err != nil {
220
+ handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to delete floating IP: %w" , err ))
221
+ return fmt .Errorf ("failed to delete floating IP: %w" , err )
222
+ }
223
+ }
224
+
225
+ var instanceStatus * compute.InstanceStatus
226
+ if openStackCluster .Status .Bastion != nil && openStackCluster .Status .Bastion .ID != "" {
227
+ instanceStatus , err = computeService .GetInstanceStatus (openStackCluster .Status .Bastion .ID )
228
+ if err != nil {
229
+ return err
230
+ }
231
+ } else {
232
+ instanceStatus , err = computeService .GetInstanceStatusByName (openStackCluster , bastionName (cluster .Name ))
233
+ if err != nil {
234
+ return err
235
+ }
222
236
}
223
237
224
238
if instanceStatus != nil {
@@ -230,6 +244,7 @@ func deleteBastion(scope scope.Scope, cluster *clusterv1.Cluster, openStackClust
230
244
231
245
for _ , address := range addresses {
232
246
if address .Type == corev1 .NodeExternalIP {
247
+ // Floating IP may not have properly saved in bastion status (thus not deleted above), delete any remaining floating IP
233
248
if err = networkingService .DeleteFloatingIP (openStackCluster , address .Address ); err != nil {
234
249
handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to delete floating IP: %w" , err ))
235
250
return fmt .Errorf ("failed to delete floating IP: %w" , err )
@@ -277,8 +292,9 @@ func reconcileNormal(scope scope.Scope, cluster *clusterv1.Cluster, openStackClu
277
292
return reconcile.Result {}, err
278
293
}
279
294
280
- if err = reconcileBastion (scope , cluster , openStackCluster ); err != nil {
281
- return reconcile.Result {}, err
295
+ result , err := reconcileBastion (scope , cluster , openStackCluster )
296
+ if err != nil || ! reflect .DeepEqual (result , reconcile.Result {}) {
297
+ return result , err
282
298
}
283
299
284
300
availabilityZones , err := computeService .GetAvailabilityZones ()
@@ -308,88 +324,112 @@ func reconcileNormal(scope scope.Scope, cluster *clusterv1.Cluster, openStackClu
308
324
return reconcile.Result {}, nil
309
325
}
310
326
311
- func reconcileBastion (scope scope.Scope , cluster * clusterv1.Cluster , openStackCluster * infrav1.OpenStackCluster ) error {
327
+ func reconcileBastion (scope scope.Scope , cluster * clusterv1.Cluster , openStackCluster * infrav1.OpenStackCluster ) (ctrl. Result , error ) {
312
328
scope .Logger ().Info ("Reconciling Bastion" )
313
329
314
330
if openStackCluster .Spec .Bastion == nil || ! openStackCluster .Spec .Bastion .Enabled {
315
- return deleteBastion (scope , cluster , openStackCluster )
331
+ return reconcile. Result {}, deleteBastion (scope , cluster , openStackCluster )
316
332
}
317
333
318
334
computeService , err := compute .NewService (scope )
319
335
if err != nil {
320
- return err
336
+ return reconcile. Result {}, err
321
337
}
322
338
323
339
instanceSpec := bastionToInstanceSpec (openStackCluster , cluster .Name )
324
340
bastionHash , err := compute .HashInstanceSpec (instanceSpec )
325
341
if err != nil {
326
- return fmt .Errorf ("failed computing bastion hash from instance spec: %w" , err )
342
+ return reconcile.Result {}, fmt .Errorf ("failed computing bastion hash from instance spec: %w" , err )
343
+ }
344
+ if bastionHashHasChanged (bastionHash , openStackCluster .ObjectMeta .Annotations ) {
345
+ if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
346
+ return ctrl.Result {}, err
347
+ }
327
348
}
328
349
329
- instanceStatus , err := computeService .GetInstanceStatusByName (openStackCluster , fmt .Sprintf ("%s-bastion" , cluster .Name ))
330
- if err != nil {
331
- return err
350
+ var instanceStatus * compute.InstanceStatus
351
+ if openStackCluster .Status .Bastion != nil && openStackCluster .Status .Bastion .ID != "" {
352
+ if instanceStatus , err = computeService .GetInstanceStatus (openStackCluster .Status .Bastion .ID ); err != nil {
353
+ return reconcile.Result {}, err
354
+ }
332
355
}
333
- if instanceStatus != nil {
334
- if ! bastionHashHasChanged (bastionHash , openStackCluster .ObjectMeta .Annotations ) {
335
- bastion , err := instanceStatus .BastionStatus (openStackCluster )
336
- if err != nil {
337
- return err
338
- }
339
- // Add the current hash if no annotation is set.
340
- if _ , ok := openStackCluster .ObjectMeta .Annotations [BastionInstanceHashAnnotation ]; ! ok {
341
- annotations .AddAnnotations (openStackCluster , map [string ]string {BastionInstanceHashAnnotation : bastionHash })
342
- }
343
- openStackCluster .Status .Bastion = bastion
344
- return nil
356
+ if instanceStatus == nil {
357
+ // Check if there is an existing instance with bastion name, in case where bastion ID would not have been properly stored in cluster status
358
+ if instanceStatus , err = computeService .GetInstanceStatusByName (openStackCluster , instanceSpec .Name ); err != nil {
359
+ return reconcile.Result {}, err
345
360
}
346
-
347
- if err := deleteBastion (scope , cluster , openStackCluster ); err != nil {
348
- return err
361
+ }
362
+ if instanceStatus == nil {
363
+ instanceStatus , err = computeService .CreateInstance (openStackCluster , openStackCluster , instanceSpec , cluster .Name )
364
+ if err != nil {
365
+ return reconcile.Result {}, fmt .Errorf ("failed to create bastion: %w" , err )
349
366
}
350
367
}
351
368
352
- instanceStatus , err = computeService .CreateInstance (openStackCluster , openStackCluster , instanceSpec , cluster .Name )
353
- if err != nil {
354
- return fmt .Errorf ("failed to reconcile bastion: %w" , err )
369
+ // Save hash & status as soon as we know we have an instance
370
+ instanceStatus .UpdateBastionStatus (openStackCluster )
371
+ annotations .AddAnnotations (openStackCluster , map [string ]string {BastionInstanceHashAnnotation : bastionHash })
372
+
373
+ // Make sure that bastion instance has a valid state
374
+ switch instanceStatus .State () {
375
+ case infrav1 .InstanceStateError :
376
+ return ctrl.Result {}, fmt .Errorf ("failed to reconcile bastion, instance state is ERROR" )
377
+ case infrav1 .InstanceStateBuild , infrav1 .InstanceStateUndefined :
378
+ scope .Logger ().Info ("Waiting for bastion instance to become ACTIVE" , "id" , instanceStatus .ID (), "status" , instanceStatus .State ())
379
+ return ctrl.Result {RequeueAfter : waitForBuildingInstanceToReconcile }, nil
380
+ case infrav1 .InstanceStateDeleted :
381
+ // This should normally be handled by deleteBastion
382
+ openStackCluster .Status .Bastion = nil
383
+ return ctrl.Result {}, nil
355
384
}
356
385
357
386
networkingService , err := networking .NewService (scope )
358
387
if err != nil {
359
- return err
360
- }
361
- clusterName := fmt .Sprintf ("%s-%s" , cluster .Namespace , cluster .Name )
362
- fp , err := networkingService .GetOrCreateFloatingIP (openStackCluster , openStackCluster , clusterName , openStackCluster .Spec .Bastion .Instance .FloatingIP )
363
- if err != nil {
364
- handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err ))
365
- return fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err )
388
+ return ctrl.Result {}, err
366
389
}
367
390
port , err := computeService .GetManagementPort (openStackCluster , instanceStatus )
368
391
if err != nil {
369
392
err = fmt .Errorf ("getting management port for bastion: %w" , err )
370
393
handleUpdateOSCError (openStackCluster , err )
371
- return err
394
+ return ctrl. Result {}, err
372
395
}
373
- err = networkingService .AssociateFloatingIP ( openStackCluster , fp , port .ID )
396
+ fp , err : = networkingService .GetFloatingIPByPortID ( port .ID )
374
397
if err != nil {
375
- handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to associate floating IP with bastion: %w" , err ))
376
- return fmt .Errorf ("failed to associate floating IP with bastion: %w" , err )
398
+ handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err ))
399
+ return ctrl.Result {}, fmt .Errorf ("failed to get floating IP for bastion port: %w" , err )
400
+ }
401
+ if fp != nil {
402
+ // Floating IP is already attached to bastion, no need to proceed
403
+ openStackCluster .Status .Bastion .FloatingIP = fp .FloatingIP
404
+ return ctrl.Result {}, nil
377
405
}
378
406
379
- bastion , err := instanceStatus .BastionStatus (openStackCluster )
407
+ clusterName := fmt .Sprintf ("%s-%s" , cluster .Namespace , cluster .Name )
408
+ floatingIP := openStackCluster .Spec .Bastion .Instance .FloatingIP
409
+ if openStackCluster .Status .Bastion .FloatingIP != "" {
410
+ // Some floating IP has already been created for this bastion, make sure we re-use it
411
+ floatingIP = openStackCluster .Status .Bastion .FloatingIP
412
+ }
413
+ // Check if there is an existing floating IP attached to bastion, in case where FloatingIP would not yet have been stored in cluster status
414
+ fp , err = networkingService .GetOrCreateFloatingIP (openStackCluster , openStackCluster , clusterName , floatingIP )
380
415
if err != nil {
381
- return err
416
+ handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err ))
417
+ return ctrl.Result {}, fmt .Errorf ("failed to get or create floating IP for bastion: %w" , err )
382
418
}
383
- bastion .FloatingIP = fp .FloatingIP
384
- openStackCluster .Status .Bastion = bastion
385
- annotations .AddAnnotations (openStackCluster , map [string ]string {BastionInstanceHashAnnotation : bastionHash })
386
- return nil
419
+ openStackCluster .Status .Bastion .FloatingIP = fp .FloatingIP
420
+
421
+ err = networkingService .AssociateFloatingIP (openStackCluster , fp , port .ID )
422
+ if err != nil {
423
+ handleUpdateOSCError (openStackCluster , fmt .Errorf ("failed to associate floating IP with bastion: %w" , err ))
424
+ return ctrl.Result {}, fmt .Errorf ("failed to associate floating IP with bastion: %w" , err )
425
+ }
426
+
427
+ return ctrl.Result {}, nil
387
428
}
388
429
389
430
func bastionToInstanceSpec (openStackCluster * infrav1.OpenStackCluster , clusterName string ) * compute.InstanceSpec {
390
- name := fmt .Sprintf ("%s-bastion" , clusterName )
391
431
instanceSpec := & compute.InstanceSpec {
392
- Name : name ,
432
+ Name : bastionName ( clusterName ) ,
393
433
Flavor : openStackCluster .Spec .Bastion .Instance .Flavor ,
394
434
SSHKeyName : openStackCluster .Spec .Bastion .Instance .SSHKeyName ,
395
435
Image : openStackCluster .Spec .Bastion .Instance .Image ,
@@ -412,6 +452,10 @@ func bastionToInstanceSpec(openStackCluster *infrav1.OpenStackCluster, clusterNa
412
452
return instanceSpec
413
453
}
414
454
455
+ func bastionName (clusterName string ) string {
456
+ return fmt .Sprintf ("%s-bastion" , clusterName )
457
+ }
458
+
415
459
// bastionHashHasChanged returns a boolean whether if the latest bastion hash, built from the instance spec, has changed or not.
416
460
func bastionHashHasChanged (computeHash string , clusterAnnotations map [string ]string ) bool {
417
461
latestHash , ok := clusterAnnotations [BastionInstanceHashAnnotation ]
0 commit comments