Skip to content

Commit 0ba70a8

Browse files
Merge pull request #1785 from stuggi/backup_restore_pause
Add staged deployment support for control plane
2 parents dd4c370 + cc8589c commit 0ba70a8

18 files changed

Lines changed: 305 additions & 8 deletions

api/core/v1beta1/conditions.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ const (
161161

162162
// OpenStackControlPlaneExposeWatcherReadyCondition Status=True condition which indicates if Watcher is exposed via a route
163163
OpenStackControlPlaneExposeWatcherReadyCondition condition.Type = "OpenStackControlPlaneExposeWatcherReady"
164+
165+
// OpenStackControlPlaneInfrastructureReadyCondition Status=True condition which indicates if infrastructure components are ready
166+
// Infrastructure includes: CAs, DNSMasq, RabbitMQ, Galera (MariaDB), Memcached, and OVN databases
167+
// This condition is set to True when deployment-stage annotation is "infrastructure-only" and all infrastructure is ready
168+
OpenStackControlPlaneInfrastructureReadyCondition condition.Type = "OpenStackControlPlaneInfrastructureReady"
164169
)
165170

166171
// Common Messages used by API objects.
@@ -507,6 +512,24 @@ const (
507512

508513
// OpenStackControlPlaneWatcherReadyErrorMessage
509514
OpenStackControlPlaneWatcherReadyErrorMessage = "OpenStackControlPlane Watcher error occured %s"
515+
516+
// OpenStackControlPlaneInfrastructureReadyInitMessage
517+
OpenStackControlPlaneInfrastructureReadyInitMessage = "OpenStackControlPlane Infrastructure not started"
518+
519+
// OpenStackControlPlaneInfrastructureReadyMessage
520+
OpenStackControlPlaneInfrastructureReadyMessage = "OpenStackControlPlane Infrastructure ready"
521+
522+
// OpenStackControlPlaneInfrastructureReadyRunningMessage
523+
OpenStackControlPlaneInfrastructureReadyRunningMessage = "OpenStackControlPlane Infrastructure in progress"
524+
525+
// OpenStackControlPlaneInfrastructureReadyWaitingMessage
526+
OpenStackControlPlaneInfrastructureReadyWaitingMessage = "OpenStackControlPlane Infrastructure in progress - waiting for: %s"
527+
528+
// OpenStackControlPlaneInfrastructureReadyPausedMessage
529+
OpenStackControlPlaneInfrastructureReadyPausedMessage = "OpenStackControlPlane Infrastructure ready - deployment paused. Remove annotation to resume reconcile of OpenStack services"
530+
531+
// OpenStackControlPlaneInfrastructureReadyErrorMessage
532+
OpenStackControlPlaneInfrastructureReadyErrorMessage = "OpenStackControlPlane Infrastructure error occured %s"
510533
)
511534

512535
// Version Conditions used by to drive minor updates

api/core/v1beta1/openstackcontrolplane_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ const (
6666
GlanceName = "glance"
6767
// CinderName - Default Cinder name
6868
CinderName = "cinder"
69+
70+
// DeploymentStageAnnotation - Annotation key for controlling deployment stages
71+
DeploymentStageAnnotation = "core.openstack.org/deployment-stage"
72+
// DeploymentStageInfrastructureOnly - Annotation value to pause after infrastructure deployment
73+
DeploymentStageInfrastructureOnly = "infrastructure-only"
6974
)
7075

7176
// OpenStackControlPlaneSpec defines the desired state of OpenStackControlPlane
@@ -951,6 +956,7 @@ func (instance *OpenStackControlPlane) InitConditions() {
951956
condition.UnknownCondition(OpenStackControlPlaneCAReadyCondition, condition.InitReason, OpenStackControlPlaneCAReadyInitMessage),
952957
condition.UnknownCondition(OpenStackControlPlaneOpenStackVersionInitializationReadyCondition, condition.InitReason, OpenStackControlPlaneOpenStackVersionInitializationReadyInitMessage),
953958
condition.UnknownCondition(OpenStackControlPlaneWatcherReadyCondition, condition.InitReason, OpenStackControlPlaneWatcherReadyInitMessage),
959+
condition.UnknownCondition(OpenStackControlPlaneInfrastructureReadyCondition, condition.InitReason, OpenStackControlPlaneInfrastructureReadyInitMessage),
954960

955961
// Also add the overall status condition as Unknown
956962
condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage),

internal/controller/core/openstackcontrolplane_controller.go

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package core
2020
import (
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+
389434
func (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

test/kuttl/common/assert-sample-deployment.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ status:
254254
reason: Ready
255255
status: "True"
256256
type: OpenStackControlPlaneGlanceReady
257+
- message: OpenStackControlPlane Infrastructure ready
258+
reason: Ready
259+
status: "True"
260+
type: OpenStackControlPlaneInfrastructureReady
257261
- message: OpenStackControlPlane InstanceHa CM is available
258262
reason: Ready
259263
status: "True"

test/kuttl/tests/ctlplane-basic-deployment/03-assert-deploy-custom-cacert.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ status:
6363
reason: Ready
6464
status: "True"
6565
type: OpenStackControlPlaneGlanceReady
66+
- message: OpenStackControlPlane Infrastructure ready
67+
reason: Ready
68+
status: "True"
69+
type: OpenStackControlPlaneInfrastructureReady
6670
- message: OpenStackControlPlane InstanceHa CM is available
6771
reason: Ready
6872
status: "True"

test/kuttl/tests/ctlplane-collapsed/01-assert-collapsed-cell.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ status:
231231
reason: Ready
232232
status: "True"
233233
type: OpenStackControlPlaneGlanceReady
234+
- message: OpenStackControlPlane Infrastructure ready
235+
reason: Ready
236+
status: "True"
237+
type: OpenStackControlPlaneInfrastructureReady
234238
- message: OpenStackControlPlane InstanceHa CM is available
235239
reason: Ready
236240
status: "True"

test/kuttl/tests/ctlplane-galera-3replicas/01-assert-galera-3replicas.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ status:
219219
reason: Ready
220220
status: "True"
221221
type: OpenStackControlPlaneGlanceReady
222+
- message: OpenStackControlPlane Infrastructure ready
223+
reason: Ready
224+
status: "True"
225+
type: OpenStackControlPlaneInfrastructureReady
222226
- message: OpenStackControlPlane InstanceHa CM is available
223227
reason: Ready
224228
status: "True"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Assert that infrastructure is ready but deployment is paused
2+
# OpenStackControlPlaneInfrastructureReady should be True with pause message
3+
# Infrastructure components (CAs, RabbitMQ, MariaDB, Memcached, OVN) should be Ready
4+
# Service components should still be in Init/Unknown state
5+
# Overall Ready should be False
6+
apiVersion: core.openstack.org/v1beta1
7+
kind: OpenStackControlPlane
8+
metadata:
9+
name: openstack
10+
annotations:
11+
core.openstack.org/deployment-stage: infrastructure-only
12+
status:
13+
conditions:
14+
# Overall deployment should not be ready yet (paused after infrastructure)
15+
- message: OpenStackControlPlane Infrastructure ready - deployment paused. Remove annotation to resume reconcile of OpenStack services
16+
reason: Requested
17+
status: "False"
18+
type: Ready
19+
# Following conditions are alphabetically sorted by type
20+
- reason: Init
21+
status: Unknown
22+
type: OpenStackControlPlaneBarbicanReady
23+
- message: OpenStackControlPlane CAs completed
24+
reason: Ready
25+
status: "True"
26+
type: OpenStackControlPlaneCAReadyCondition
27+
- reason: Init
28+
status: Unknown
29+
type: OpenStackControlPlaneCinderReady
30+
- reason: Init
31+
status: Unknown
32+
type: OpenStackControlPlaneClientReady
33+
- reason: Init
34+
status: Unknown
35+
type: OpenStackControlPlaneDesignateReady
36+
- reason: Init
37+
status: Unknown
38+
type: OpenStackControlPlaneGlanceReady
39+
- reason: Init
40+
status: Unknown
41+
type: OpenStackControlPlaneHeatReady
42+
- reason: Init
43+
status: Unknown
44+
type: OpenStackControlPlaneHorizonReady
45+
- message: OpenStackControlPlane Infrastructure ready - deployment paused. Remove annotation to resume reconcile of OpenStack services
46+
reason: Ready
47+
status: "True"
48+
type: OpenStackControlPlaneInfrastructureReady
49+
- reason: Init
50+
status: Unknown
51+
type: OpenStackControlPlaneIronicReady
52+
- reason: Init
53+
status: Unknown
54+
type: OpenStackControlPlaneKeystoneAPIReady
55+
- reason: Init
56+
status: Unknown
57+
type: OpenStackControlPlaneManilaReady
58+
- message: OpenStackControlPlane MariaDB completed
59+
reason: Ready
60+
status: "True"
61+
type: OpenStackControlPlaneMariaDBReady
62+
- message: OpenStackControlPlane Memcached completed
63+
reason: Ready
64+
status: "True"
65+
type: OpenStackControlPlaneMemcachedReady
66+
- reason: Init
67+
status: Unknown
68+
type: OpenStackControlPlaneNeutronReady
69+
- reason: Init
70+
status: Unknown
71+
type: OpenStackControlPlaneNovaReady
72+
- message: OpenStackControlPlane OVN completed
73+
reason: Ready
74+
status: "True"
75+
type: OpenStackControlPlaneOVNReady
76+
- reason: Init
77+
status: Unknown
78+
type: OpenStackControlPlaneOctaviaReady
79+
- message: OpenStackControlPlane OpenStackVersion initialized
80+
reason: Ready
81+
status: "True"
82+
type: OpenStackControlPlaneOpenStackVersionInitializationReadyCondition
83+
- reason: Init
84+
status: Unknown
85+
type: OpenStackControlPlanePlacementAPIReady
86+
- message: OpenStackControlPlane RabbitMQ completed
87+
reason: Ready
88+
status: "True"
89+
type: OpenStackControlPlaneRabbitMQReady
90+
- reason: Init
91+
status: Unknown
92+
type: OpenStackControlPlaneRedisReady
93+
- reason: Init
94+
status: Unknown
95+
type: OpenStackControlPlaneSwiftReady
96+
- reason: Init
97+
status: Unknown
98+
type: OpenStackControlPlaneTelemetryReady
99+
- reason: Init
100+
status: Unknown
101+
type: OpenStackControlPlaneWatcherReady
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Deploy OpenStackControlPlane with deployment-stage annotation set to infrastructure-only
2+
# This should pause deployment after infrastructure (CAs, RabbitMQ, Galera, Memcached, OVN) is ready
3+
apiVersion: kuttl.dev/v1beta1
4+
kind: TestStep
5+
commands:
6+
- script: |
7+
oc kustomize ../../../../config/samples/base/openstackcontrolplane | \
8+
oc annotate -f - --local=true --dry-run=none \
9+
core.openstack.org/deployment-stage=infrastructure-only -o yaml | \
10+
oc apply -n $NAMESPACE -f -
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../common/assert-sample-deployment.yaml

0 commit comments

Comments
 (0)