@@ -20,6 +20,7 @@ package core
2020import (
2121 "context"
2222 "fmt"
23+ "strings"
2324
2425 certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
2526 routev1 "github.com/openshift/api/route/v1"
@@ -186,9 +187,23 @@ func (r *OpenStackControlPlaneReconciler) Reconcile(ctx context.Context, req ctr
186187 // something is not ready so reset the Ready condition
187188 instance .Status .Conditions .MarkUnknown (
188189 condition .ReadyCondition , condition .InitReason , condition .ReadyInitMessage )
189- // and recalculate it based on the state of the rest of the conditions
190- instance .Status .Conditions .Set (
191- instance .Status .Conditions .Mirror (condition .ReadyCondition ))
190+
191+ // In infrastructure-only mode with infrastructure ready, set Ready to False with pause message
192+ // This prevents service conditions (which are still Unknown/Init) from being mirrored to Ready
193+ if stage , ok := instance .Annotations [corev1beta1 .DeploymentStageAnnotation ]; ok &&
194+ stage == corev1beta1 .DeploymentStageInfrastructureOnly &&
195+ instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneInfrastructureReadyCondition ) {
196+ // Set Ready to False with the infrastructure pause message
197+ instance .Status .Conditions .Set (condition .FalseCondition (
198+ condition .ReadyCondition ,
199+ condition .RequestedReason ,
200+ condition .SeverityInfo ,
201+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyPausedMessage ))
202+ } else {
203+ // Normal mode or infrastructure not ready yet: use default mirror behavior
204+ instance .Status .Conditions .Set (
205+ instance .Status .Conditions .Mirror (condition .ReadyCondition ))
206+ }
192207 }
193208
194209 condition .RestoreLastTransitionTimes (& instance .Status .Conditions , savedConditions )
@@ -386,6 +401,36 @@ func (r *OpenStackControlPlaneReconciler) reconcileOVNControllers(ctx context.Co
386401 return ctrl.Result {}, nil
387402}
388403
404+ // isInfrastructureReady checks if all enabled infrastructure components are ready
405+ // Returns true if ready, and a list of components that are not ready (empty if all ready)
406+ func isInfrastructureReady (instance * corev1beta1.OpenStackControlPlane ) (bool , []string ) {
407+ notReady := []string {}
408+
409+ // CAs are always deployed (no enabled flag)
410+ if ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneCAReadyCondition ) {
411+ notReady = append (notReady , "CAs" )
412+ }
413+
414+ // Only check each infrastructure component if it's enabled
415+ if instance .Spec .DNS .Enabled && ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneDNSReadyCondition ) {
416+ notReady = append (notReady , "DNS" )
417+ }
418+ if instance .Spec .Rabbitmq .Enabled && ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneRabbitMQReadyCondition ) {
419+ notReady = append (notReady , "RabbitMQs" )
420+ }
421+ if instance .Spec .Galera .Enabled && ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneMariaDBReadyCondition ) {
422+ notReady = append (notReady , "Galeras" )
423+ }
424+ if instance .Spec .Memcached .Enabled && ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneMemcachedReadyCondition ) {
425+ notReady = append (notReady , "Memcached" )
426+ }
427+ if instance .Spec .Ovn .Enabled && ! instance .Status .Conditions .IsTrue (corev1beta1 .OpenStackControlPlaneOVNReadyCondition ) {
428+ notReady = append (notReady , "OVN" )
429+ }
430+
431+ return len (notReady ) == 0 , notReady
432+ }
433+
389434func (r * OpenStackControlPlaneReconciler ) reconcileNormal (ctx context.Context , instance * corev1beta1.OpenStackControlPlane , version * corev1beta1.OpenStackVersion , helper * common_helper.Helper ) (ctrl.Result , error ) {
390435 if instance .Spec .TopologyRef != nil {
391436 if err := r .checkTopologyRef (ctx , helper ,
@@ -402,6 +447,11 @@ func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, i
402447 instance .Status .Conditions .MarkTrue (condition .TopologyReadyCondition , condition .TopologyReadyMessage )
403448 }
404449
450+ // Check for deployment-stage annotation
451+ deploymentStage := instance .Annotations [corev1beta1 .DeploymentStageAnnotation ]
452+ infrastructureOnly := deploymentStage == corev1beta1 .DeploymentStageInfrastructureOnly
453+
454+ // Reconcile infrastructure components (always run)
405455 ctrlResult , err := openstack .ReconcileCAs (ctx , instance , helper )
406456 if err != nil {
407457 return ctrl.Result {}, err
@@ -437,41 +487,87 @@ func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, i
437487 return ctrlResult , nil
438488 }
439489
440- ctrlResult , err = openstack .ReconcileKeystoneAPI (ctx , instance , version , helper )
490+ // OVN databases are part of infrastructure
491+ ctrlResult , err = openstack .ReconcileOVN (ctx , instance , version , helper )
441492 if err != nil {
442493 return ctrl.Result {}, err
443494 } else if (ctrlResult != ctrl.Result {}) {
444495 return ctrlResult , nil
445496 }
446497
447- ctrlResult , err = openstack .ReconcilePlacementAPI (ctx , instance , version , helper )
498+ // Update InfrastructureReady condition based on infrastructure component readiness
499+ // This is useful for observability regardless of staged deployment mode
500+ infrastructureReady , notReadyComponents := isInfrastructureReady (instance )
501+
502+ if infrastructureReady {
503+ // Set different messages based on whether deployment is paused
504+ if infrastructureOnly {
505+ // Infrastructure-only mode: indicate deployment is paused
506+ instance .Status .Conditions .MarkTrue (
507+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyCondition ,
508+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyPausedMessage )
509+ r .GetLogger (ctx ).Info ("Infrastructure components ready - deployment paused at infrastructure-only stage" )
510+ } else {
511+ // Normal mode: infrastructure is ready
512+ instance .Status .Conditions .MarkTrue (
513+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyCondition ,
514+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyMessage )
515+ }
516+ } else {
517+ // Build a descriptive message showing which components are not ready
518+ if len (notReadyComponents ) > 0 {
519+ instance .Status .Conditions .Set (condition .FalseCondition (
520+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyCondition ,
521+ condition .RequestedReason ,
522+ condition .SeverityInfo ,
523+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyWaitingMessage ,
524+ strings .Join (notReadyComponents , ", " )))
525+ } else {
526+ instance .Status .Conditions .Set (condition .FalseCondition (
527+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyCondition ,
528+ condition .RequestedReason ,
529+ condition .SeverityInfo ,
530+ corev1beta1 .OpenStackControlPlaneInfrastructureReadyRunningMessage ))
531+ }
532+ }
533+
534+ // Check if we're in infrastructure-only mode and should pause deployment
535+ if infrastructureOnly {
536+ // Stop here - do not reconcile OpenStack services
537+ return ctrl.Result {}, nil
538+ }
539+
540+ // Continue with OpenStack service reconciliation
541+ ctrlResult , err = openstack .ReconcileKeystoneAPI (ctx , instance , version , helper )
448542 if err != nil {
449543 return ctrl.Result {}, err
450544 } else if (ctrlResult != ctrl.Result {}) {
451545 return ctrlResult , nil
452546 }
453547
454- ctrlResult , err = openstack .ReconcileGlance (ctx , instance , version , helper )
548+ ctrlResult , err = openstack .ReconcilePlacementAPI (ctx , instance , version , helper )
455549 if err != nil {
456550 return ctrl.Result {}, err
457551 } else if (ctrlResult != ctrl.Result {}) {
458552 return ctrlResult , nil
459553 }
460554
461- ctrlResult , err = openstack .ReconcileCinder (ctx , instance , version , helper )
555+ ctrlResult , err = openstack .ReconcileGlance (ctx , instance , version , helper )
462556 if err != nil {
463557 return ctrl.Result {}, err
464558 } else if (ctrlResult != ctrl.Result {}) {
465559 return ctrlResult , nil
466560 }
467561
468- ctrlResult , err = openstack .ReconcileOVN (ctx , instance , version , helper )
562+ ctrlResult , err = openstack .ReconcileCinder (ctx , instance , version , helper )
469563 if err != nil {
470564 return ctrl.Result {}, err
471565 } else if (ctrlResult != ctrl.Result {}) {
472566 return ctrlResult , nil
473567 }
474568
569+ // OVN already reconciled in infrastructure section above
570+
475571 ctrlResult , err = openstack .ReconcileNeutron (ctx , instance , version , helper )
476572 if err != nil {
477573 return ctrl.Result {}, err
0 commit comments