Skip to content

Commit 9be78b8

Browse files
committed
use annotations instead of spec.version
1 parent 669db52 commit 9be78b8

10 files changed

Lines changed: 190 additions & 171 deletions

File tree

apis/bases/rabbitmq.openstack.org_rabbitmqs.yaml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,14 +1892,6 @@ spec:
18921892
current project
18931893
type: string
18941894
type: object
1895-
version:
1896-
description: |-
1897-
Version - Desired RabbitMQ version (e.g., "3.9", "4.0").
1898-
Changing this value triggers a version upgrade/downgrade process which requires storage wipe
1899-
for major/minor version changes. Defaults to "4.0" for new instances and "3.9" for existing
1900-
instances (backwards compatibility). This field should be set by openstack-operator to control
1901-
the RabbitMQ version.
1902-
type: string
19031895
required:
19041896
- containerImage
19051897
type: object
@@ -1952,7 +1944,7 @@ spec:
19521944
description: |-
19531945
CurrentVersion - the currently deployed RabbitMQ version (e.g., "3.9", "4.0")
19541946
This is controller-managed and reflects the actual running version.
1955-
Users should use spec.version to request version changes.
1947+
openstack-operator should use the "rabbitmq.openstack.org/target-version" annotation to request version changes.
19561948
type: string
19571949
lastAppliedTopology:
19581950
description: LastAppliedTopology - the last applied Topology

apis/rabbitmq/v1beta1/rabbitmq_types.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ const (
5151
QueueTypeQuorum = "Quorum"
5252
// QueueTypeNone - no special queue type
5353
QueueTypeNone = "None"
54+
55+
// Annotations
56+
// AnnotationTargetVersion - annotation key for target RabbitMQ version (set by openstack-operator)
57+
AnnotationTargetVersion = "rabbitmq.openstack.org/target-version"
5458
)
5559

5660
// PodOverride defines per-pod service configurations
@@ -89,14 +93,6 @@ type RabbitMqSpecCore struct {
8993
QueueType *string `json:"queueType,omitempty"`
9094
// +kubebuilder:validation:Optional
9195
// +operator-sdk:csv:customresourcedefinitions:type=spec
92-
// Version - Desired RabbitMQ version (e.g., "3.9", "4.0").
93-
// Changing this value triggers a version upgrade/downgrade process which requires storage wipe
94-
// for major/minor version changes. Defaults to "4.0" for new instances and "3.9" for existing
95-
// instances (backwards compatibility). This field should be set by openstack-operator to control
96-
// the RabbitMQ version.
97-
Version *string `json:"version,omitempty"`
98-
// +kubebuilder:validation:Optional
99-
// +operator-sdk:csv:customresourcedefinitions:type=spec
10096
// PodOverride - Override configuration for per-pod services. When specified, individual LoadBalancer
10197
// services will be created for each pod with the provided configuration, and the transport URL will be
10298
// configured to use these per-pod services.
@@ -144,7 +140,7 @@ type RabbitMqStatus struct {
144140

145141
// CurrentVersion - the currently deployed RabbitMQ version (e.g., "3.9", "4.0")
146142
// This is controller-managed and reflects the actual running version.
147-
// Users should use spec.version to request version changes.
143+
// openstack-operator should use the "rabbitmq.openstack.org/target-version" annotation to request version changes.
148144
CurrentVersion string `json:"currentVersion,omitempty"`
149145

150146
// UpgradePhase - tracks the current phase of a version upgrade or migration

apis/rabbitmq/v1beta1/rabbitmq_webhook.go

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,20 @@ func (r *RabbitMq) Default(k8sClient client.Client) {
101101

102102
if !userChangingToMirrored {
103103
// User is not explicitly changing TO Mirrored, so we can auto-override
104-
// Check the target version (spec.version) to see if upgrading to 4.0+
105-
if r.Spec.Version != nil && *r.Spec.Version != "" {
106-
// Parse version to check if major version is 4 or higher
107-
majorVersion, err := parseVersionMajor(*r.Spec.Version)
108-
if err == nil && majorVersion >= 4 {
109-
shouldOverride = true
110-
queueType := "Quorum"
111-
r.Spec.QueueType = &queueType
112-
rabbitmqlog.Info("overriding Mirrored to Quorum on RabbitMQ 4.0+",
113-
"name", r.Name,
114-
"targetVersion", *r.Spec.Version,
115-
"queueType", "Quorum")
104+
// Check the target version (annotation) to see if upgrading to 4.0+
105+
if r.Annotations != nil {
106+
if targetVersion, hasTarget := r.Annotations[AnnotationTargetVersion]; hasTarget {
107+
// Parse version to check if major version is 4 or higher
108+
majorVersion, err := parseVersionMajor(targetVersion)
109+
if err == nil && majorVersion >= 4 {
110+
shouldOverride = true
111+
queueType := "Quorum"
112+
r.Spec.QueueType = &queueType
113+
rabbitmqlog.Info("overriding Mirrored to Quorum on RabbitMQ 4.0+",
114+
"name", r.Name,
115+
"targetVersion", targetVersion,
116+
"queueType", "Quorum")
117+
}
116118
}
117119
}
118120
}
@@ -134,14 +136,6 @@ func (r *RabbitMq) Default(k8sClient client.Client) {
134136
}
135137
}
136138

137-
// Preserve existing version if not specified
138-
if r.Spec.Version == nil || *r.Spec.Version == "" {
139-
if existingRabbitMq.Spec.Version != nil && *existingRabbitMq.Spec.Version != "" {
140-
r.Spec.Version = existingRabbitMq.Spec.Version
141-
rabbitmqlog.Info("preserving Version from existing CR", "name", r.Name, "version", *r.Spec.Version)
142-
}
143-
}
144-
145139
isNew = false
146140
} else {
147141
// Check if RabbitMQCluster exists (upgrade scenario: cluster exists but CR is new)
@@ -176,19 +170,7 @@ func (spec *RabbitMqSpec) Default(isNew bool) {
176170

177171
// Default - set defaults for this RabbitMqSpecCore
178172
func (spec *RabbitMqSpecCore) Default(isNew bool) {
179-
// Default Version based on whether this is a new or existing instance
180-
if spec.Version == nil || *spec.Version == "" {
181-
if isNew {
182-
// New instances default to RabbitMQ 4.0
183-
version := "4.0"
184-
spec.Version = &version
185-
} else {
186-
// Existing instances default to 3.9 (for backwards compatibility)
187-
version := "3.9"
188-
spec.Version = &version
189-
}
190-
}
191-
173+
// Default QueueType for new instances
192174
if isNew && (spec.QueueType == nil || *spec.QueueType == "") {
193175
queueType := "Quorum"
194176
spec.QueueType = &queueType
@@ -287,12 +269,14 @@ func (r *RabbitMq) ValidateUpdate(old runtime.Object) (admission.Warnings, error
287269
// Parse version - if major version is 3.x, check if upgrading
288270
majorVersion, err := parseVersionMajor(currentVersion)
289271
if err == nil && majorVersion == 3 {
290-
// Check if there's a concurrent version upgrade to 4.x
272+
// Check if there's a concurrent version upgrade to 4.x via annotation
291273
isUpgradingTo4x := false
292-
if r.Spec.Version != nil && *r.Spec.Version != "" {
293-
targetMajor, err := parseVersionMajor(*r.Spec.Version)
294-
if err == nil && targetMajor >= 4 {
295-
isUpgradingTo4x = true
274+
if r.Annotations != nil {
275+
if targetVersion, hasTarget := r.Annotations[AnnotationTargetVersion]; hasTarget {
276+
targetMajor, err := parseVersionMajor(targetVersion)
277+
if err == nil && targetMajor >= 4 {
278+
isUpgradingTo4x = true
279+
}
296280
}
297281
}
298282

apis/rabbitmq/v1beta1/zz_generated.deepcopy.go

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

config/crd/bases/rabbitmq.openstack.org_rabbitmqs.yaml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,14 +1892,6 @@ spec:
18921892
current project
18931893
type: string
18941894
type: object
1895-
version:
1896-
description: |-
1897-
Version - Desired RabbitMQ version (e.g., "3.9", "4.0").
1898-
Changing this value triggers a version upgrade/downgrade process which requires storage wipe
1899-
for major/minor version changes. Defaults to "4.0" for new instances and "3.9" for existing
1900-
instances (backwards compatibility). This field should be set by openstack-operator to control
1901-
the RabbitMQ version.
1902-
type: string
19031895
required:
19041896
- containerImage
19051897
type: object
@@ -1952,7 +1944,7 @@ spec:
19521944
description: |-
19531945
CurrentVersion - the currently deployed RabbitMQ version (e.g., "3.9", "4.0")
19541946
This is controller-managed and reflects the actual running version.
1955-
Users should use spec.version to request version changes.
1947+
openstack-operator should use the "rabbitmq.openstack.org/target-version" annotation to request version changes.
19561948
type: string
19571949
lastAppliedTopology:
19581950
description: LastAppliedTopology - the last applied Topology

internal/controller/rabbitmq/rabbitmq_controller.go

Lines changed: 96 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ const (
114114

115115
// RabbitMQ version upgrade constants
116116
const (
117-
// DefaultRabbitMQVersion is the default RabbitMQ version when Spec.Version is not set
118-
// New instances default to 4.0, but this constant is used for Status.CurrentVersion
119-
// initialization to maintain backwards compatibility with existing instances
117+
// DefaultRabbitMQVersion is the default RabbitMQ version when the target-version annotation is not set
118+
// This constant is used for Status.CurrentVersion initialization to maintain backwards compatibility
120119
DefaultRabbitMQVersion = "4.0"
121120
// UpgradeCheckInterval is how often to check upgrade progress
122121
UpgradeCheckInterval = 2 * time.Second
@@ -193,14 +192,38 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
193192

194193
// Initialize RabbitMQ version in Status if it doesn't exist
195194
// Status is controller-managed and can't be directly modified by users
196-
// We initialize it to match Spec.Version to avoid triggering an immediate upgrade
195+
// We initialize it to match the target-version annotation to avoid triggering an immediate upgrade
197196
if instance.Status.CurrentVersion == "" {
198-
// Use Spec.Version if set (webhook will have defaulted it appropriately)
199-
// Otherwise fall back to the constant for backwards compatibility
197+
// Priority order for determining initial version:
198+
// 1. Use annotation if set by openstack-operator
199+
// 2. Check if RabbitMQCluster exists (upgrade scenario - infer we're on 3.9 for backwards compat)
200+
// 3. Fall back to DefaultRabbitMQVersion for new deployments
200201
initialVersion := DefaultRabbitMQVersion
201-
if instance.Spec.Version != nil && *instance.Spec.Version != "" {
202-
initialVersion = *instance.Spec.Version
202+
203+
if instance.Annotations != nil {
204+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
205+
initialVersion = targetVersion
206+
}
207+
} else {
208+
// No annotation - check if this is an existing cluster
209+
existingCluster := &rabbitmqv2.RabbitmqCluster{}
210+
clusterKey := types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}
211+
err := r.Get(ctx, clusterKey, existingCluster)
212+
213+
if err == nil && !existingCluster.CreationTimestamp.IsZero() {
214+
// RabbitMQCluster exists but no annotation - this is an upgrade from old version
215+
// Assume 3.9 for backwards compatibility (old deployments default to 3.9)
216+
initialVersion = "3.9"
217+
Log.Info("Existing RabbitMQCluster found without version annotation, defaulting to 3.9 for backwards compatibility",
218+
"cluster", clusterKey,
219+
"clusterCreated", existingCluster.CreationTimestamp)
220+
} else if err != nil && !k8s_errors.IsNotFound(err) {
221+
// Error checking for cluster (not NotFound) - log but continue with default
222+
Log.Error(err, "Failed to check for existing RabbitMQCluster during version initialization")
223+
}
224+
// If cluster doesn't exist (NotFound), use DefaultRabbitMQVersion (4.0) for new deployments
203225
}
226+
204227
instance.Status.CurrentVersion = initialVersion
205228
Log.Info("Initialized RabbitMQ current version in status", "version", initialVersion)
206229
// Persist the status update
@@ -218,46 +241,47 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
218241

219242
// Check for version upgrade requiring wipe
220243
// Current version comes from Status (controller-managed)
221-
// Target version comes from Spec (user-specified via openstack-operator)
244+
// Target version comes from annotation (set by openstack-operator)
222245
currentVersion := instance.Status.CurrentVersion
223-
if instance.Spec.Version != nil && *instance.Spec.Version != "" {
224-
targetVersion := *instance.Spec.Version
225-
needsWipe, err := requiresStorageWipe(currentVersion, targetVersion)
226-
if err != nil {
227-
Log.Error(err, "Failed to determine upgrade compatibility",
228-
"currentVersion", currentVersion,
229-
"targetVersion", targetVersion)
230-
return ctrl.Result{}, fmt.Errorf("failed to check upgrade compatibility: %w", err)
231-
}
232-
if needsWipe {
233-
requiresWipe = true
234-
wipeReason = "version upgrade"
235-
Log.Info("RabbitMQ upgrade requires storage wipe (no direct upgrade path)",
236-
"currentVersion", currentVersion,
237-
"targetVersion", targetVersion)
238-
239-
// Automatically migrate from Mirrored to Quorum when upgrading from 3.x to 4.x
240-
// Mirrored queues are deprecated in RabbitMQ 4.0+
241-
currentVer, err := rabbitmq.ParseRabbitMQVersion(currentVersion)
242-
if err == nil {
243-
targetVer, err := rabbitmq.ParseRabbitMQVersion(targetVersion)
244-
if err == nil && currentVer.Major == 3 && targetVer.Major >= 4 {
245-
// Upgrading from 3.x to 4.x - automatically enforce Quorum queues
246-
if instance.Spec.QueueType == nil || *instance.Spec.QueueType != rabbitmqv1beta1.QueueTypeQuorum {
247-
Log.Info("Upgrading from RabbitMQ 3.x to 4.x - automatically migrating to Quorum queues",
248-
"currentVersion", currentVersion,
249-
"targetVersion", targetVersion)
250-
queueType := rabbitmqv1beta1.QueueTypeQuorum
251-
instance.Spec.QueueType = &queueType
252-
253-
// Patch the instance to persist the queue type change
254-
if err := helper.PatchInstance(ctx, instance); err != nil {
255-
Log.Error(err, "Failed to patch instance with Quorum queue type")
256-
return ctrl.Result{}, fmt.Errorf("failed to update queue type during upgrade: %w", err)
246+
if instance.Annotations != nil {
247+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
248+
needsWipe, err := requiresStorageWipe(currentVersion, targetVersion)
249+
if err != nil {
250+
Log.Error(err, "Failed to determine upgrade compatibility",
251+
"currentVersion", currentVersion,
252+
"targetVersion", targetVersion)
253+
return ctrl.Result{}, fmt.Errorf("failed to check upgrade compatibility: %w", err)
254+
}
255+
if needsWipe {
256+
requiresWipe = true
257+
wipeReason = "version upgrade"
258+
Log.Info("RabbitMQ upgrade requires storage wipe (no direct upgrade path)",
259+
"currentVersion", currentVersion,
260+
"targetVersion", targetVersion)
261+
262+
// Automatically migrate from Mirrored to Quorum when upgrading from 3.x to 4.x
263+
// Mirrored queues are deprecated in RabbitMQ 4.0+
264+
currentVer, err := rabbitmq.ParseRabbitMQVersion(currentVersion)
265+
if err == nil {
266+
targetVer, err := rabbitmq.ParseRabbitMQVersion(targetVersion)
267+
if err == nil && currentVer.Major == 3 && targetVer.Major >= 4 {
268+
// Upgrading from 3.x to 4.x - automatically enforce Quorum queues
269+
if instance.Spec.QueueType == nil || *instance.Spec.QueueType != rabbitmqv1beta1.QueueTypeQuorum {
270+
Log.Info("Upgrading from RabbitMQ 3.x to 4.x - automatically migrating to Quorum queues",
271+
"currentVersion", currentVersion,
272+
"targetVersion", targetVersion)
273+
queueType := rabbitmqv1beta1.QueueTypeQuorum
274+
instance.Spec.QueueType = &queueType
275+
276+
// Patch the instance to persist the queue type change
277+
if err := helper.PatchInstance(ctx, instance); err != nil {
278+
Log.Error(err, "Failed to patch instance with Quorum queue type")
279+
return ctrl.Result{}, fmt.Errorf("failed to update queue type during upgrade: %w", err)
280+
}
281+
Log.Info("Updated spec.queueType to Quorum for RabbitMQ 4.x compatibility")
282+
// Requeue to let the change propagate
283+
return ctrl.Result{Requeue: true}, nil
257284
}
258-
Log.Info("Updated spec.queueType to Quorum for RabbitMQ 4.x compatibility")
259-
// Requeue to let the change propagate
260-
return ctrl.Result{Requeue: true}, nil
261285
}
262286
}
263287
}
@@ -538,10 +562,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
538562

539563
// Emit event for observability
540564
if r.Recorder != nil {
541-
if wipeReason == "version upgrade" && instance.Spec.Version != nil && *instance.Spec.Version != "" {
542-
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "UpgradeStarted",
543-
"Starting RabbitMQ upgrade from %s to %s (requires storage wipe)",
544-
instance.Status.CurrentVersion, *instance.Spec.Version)
565+
if wipeReason == "version upgrade" && instance.Annotations != nil {
566+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
567+
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "UpgradeStarted",
568+
"Starting RabbitMQ upgrade from %s to %s (requires storage wipe)",
569+
instance.Status.CurrentVersion, targetVersion)
570+
}
545571
} else {
546572
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "MigrationStarted",
547573
"Starting queue type migration (requires storage wipe)")
@@ -599,27 +625,29 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
599625

600626
// Storage wipe complete - update Status.CurrentVersion for version upgrades
601627
if wipeReason == "version upgrade" {
602-
if instance.Spec.Version != nil && *instance.Spec.Version != "" {
603-
instance.Status.CurrentVersion = *instance.Spec.Version
604-
// Clear the upgrade phase and timestamp
605-
instance.Status.UpgradePhase = ""
606-
instance.Status.StorageWipeStartedAt = nil
607-
608-
// If queue type changed during upgrade, update Status.QueueType to prevent
609-
// triggering another wipe for "queue type migration"
610-
if instance.Spec.QueueType != nil && *instance.Spec.QueueType == rabbitmqv1beta1.QueueTypeQuorum {
611-
if instance.Status.QueueType == rabbitmqv1beta1.QueueTypeMirrored {
612-
instance.Status.QueueType = rabbitmqv1beta1.QueueTypeQuorum
613-
Log.Info("Updated Status.QueueType during version upgrade", "queueType", "Quorum")
628+
if instance.Annotations != nil {
629+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
630+
instance.Status.CurrentVersion = targetVersion
631+
// Clear the upgrade phase and timestamp
632+
instance.Status.UpgradePhase = ""
633+
instance.Status.StorageWipeStartedAt = nil
634+
635+
// If queue type changed during upgrade, update Status.QueueType to prevent
636+
// triggering another wipe for "queue type migration"
637+
if instance.Spec.QueueType != nil && *instance.Spec.QueueType == rabbitmqv1beta1.QueueTypeQuorum {
638+
if instance.Status.QueueType == rabbitmqv1beta1.QueueTypeMirrored {
639+
instance.Status.QueueType = rabbitmqv1beta1.QueueTypeQuorum
640+
Log.Info("Updated Status.QueueType during version upgrade", "queueType", "Quorum")
641+
}
614642
}
615-
}
616643

617-
Log.Info("Storage cleanup complete - updated current version in status", "version", *instance.Spec.Version)
644+
Log.Info("Storage cleanup complete - updated current version in status", "version", targetVersion)
618645

619-
// Emit event for observability
620-
if r.Recorder != nil {
621-
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "StorageWipeComplete",
622-
"Storage successfully wiped, recreating cluster with RabbitMQ %s", *instance.Spec.Version)
646+
// Emit event for observability
647+
if r.Recorder != nil {
648+
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "StorageWipeComplete",
649+
"Storage successfully wiped, recreating cluster with RabbitMQ %s", targetVersion)
650+
}
623651
}
624652
}
625653
} else {

0 commit comments

Comments
 (0)