Skip to content

Commit dfeb678

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

10 files changed

Lines changed: 167 additions & 170 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: 73 additions & 67 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,13 +192,15 @@ 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)
197+
// Use annotation if set by openstack-operator
199198
// Otherwise fall back to the constant for backwards compatibility
200199
initialVersion := DefaultRabbitMQVersion
201-
if instance.Spec.Version != nil && *instance.Spec.Version != "" {
202-
initialVersion = *instance.Spec.Version
200+
if instance.Annotations != nil {
201+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
202+
initialVersion = targetVersion
203+
}
203204
}
204205
instance.Status.CurrentVersion = initialVersion
205206
Log.Info("Initialized RabbitMQ current version in status", "version", initialVersion)
@@ -218,46 +219,47 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
218219

219220
// Check for version upgrade requiring wipe
220221
// Current version comes from Status (controller-managed)
221-
// Target version comes from Spec (user-specified via openstack-operator)
222+
// Target version comes from annotation (set by openstack-operator)
222223
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)
224+
if instance.Annotations != nil {
225+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
226+
needsWipe, err := requiresStorageWipe(currentVersion, targetVersion)
227+
if err != nil {
228+
Log.Error(err, "Failed to determine upgrade compatibility",
229+
"currentVersion", currentVersion,
230+
"targetVersion", targetVersion)
231+
return ctrl.Result{}, fmt.Errorf("failed to check upgrade compatibility: %w", err)
232+
}
233+
if needsWipe {
234+
requiresWipe = true
235+
wipeReason = "version upgrade"
236+
Log.Info("RabbitMQ upgrade requires storage wipe (no direct upgrade path)",
237+
"currentVersion", currentVersion,
238+
"targetVersion", targetVersion)
239+
240+
// Automatically migrate from Mirrored to Quorum when upgrading from 3.x to 4.x
241+
// Mirrored queues are deprecated in RabbitMQ 4.0+
242+
currentVer, err := rabbitmq.ParseRabbitMQVersion(currentVersion)
243+
if err == nil {
244+
targetVer, err := rabbitmq.ParseRabbitMQVersion(targetVersion)
245+
if err == nil && currentVer.Major == 3 && targetVer.Major >= 4 {
246+
// Upgrading from 3.x to 4.x - automatically enforce Quorum queues
247+
if instance.Spec.QueueType == nil || *instance.Spec.QueueType != rabbitmqv1beta1.QueueTypeQuorum {
248+
Log.Info("Upgrading from RabbitMQ 3.x to 4.x - automatically migrating to Quorum queues",
249+
"currentVersion", currentVersion,
250+
"targetVersion", targetVersion)
251+
queueType := rabbitmqv1beta1.QueueTypeQuorum
252+
instance.Spec.QueueType = &queueType
253+
254+
// Patch the instance to persist the queue type change
255+
if err := helper.PatchInstance(ctx, instance); err != nil {
256+
Log.Error(err, "Failed to patch instance with Quorum queue type")
257+
return ctrl.Result{}, fmt.Errorf("failed to update queue type during upgrade: %w", err)
258+
}
259+
Log.Info("Updated spec.queueType to Quorum for RabbitMQ 4.x compatibility")
260+
// Requeue to let the change propagate
261+
return ctrl.Result{Requeue: true}, nil
257262
}
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
261263
}
262264
}
263265
}
@@ -538,10 +540,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
538540

539541
// Emit event for observability
540542
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)
543+
if wipeReason == "version upgrade" && instance.Annotations != nil {
544+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
545+
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "UpgradeStarted",
546+
"Starting RabbitMQ upgrade from %s to %s (requires storage wipe)",
547+
instance.Status.CurrentVersion, targetVersion)
548+
}
545549
} else {
546550
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "MigrationStarted",
547551
"Starting queue type migration (requires storage wipe)")
@@ -599,27 +603,29 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
599603

600604
// Storage wipe complete - update Status.CurrentVersion for version upgrades
601605
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")
606+
if instance.Annotations != nil {
607+
if targetVersion, hasTarget := instance.Annotations[rabbitmqv1beta1.AnnotationTargetVersion]; hasTarget && targetVersion != "" {
608+
instance.Status.CurrentVersion = targetVersion
609+
// Clear the upgrade phase and timestamp
610+
instance.Status.UpgradePhase = ""
611+
instance.Status.StorageWipeStartedAt = nil
612+
613+
// If queue type changed during upgrade, update Status.QueueType to prevent
614+
// triggering another wipe for "queue type migration"
615+
if instance.Spec.QueueType != nil && *instance.Spec.QueueType == rabbitmqv1beta1.QueueTypeQuorum {
616+
if instance.Status.QueueType == rabbitmqv1beta1.QueueTypeMirrored {
617+
instance.Status.QueueType = rabbitmqv1beta1.QueueTypeQuorum
618+
Log.Info("Updated Status.QueueType during version upgrade", "queueType", "Quorum")
619+
}
614620
}
615-
}
616621

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

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)
624+
// Emit event for observability
625+
if r.Recorder != nil {
626+
r.Recorder.Eventf(instance, corev1.EventTypeNormal, "StorageWipeComplete",
627+
"Storage successfully wiped, recreating cluster with RabbitMQ %s", targetVersion)
628+
}
623629
}
624630
}
625631
} else {

internal/controller/rabbitmq/storage_upgrade.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ limitations under the License.
2020
//
2121
// Steps to roll back a failed upgrade:
2222
//
23-
// 1. Stop the upgrade process by reverting spec.version to the original version:
24-
// kubectl patch rabbitmq <name> -n <namespace> --type=merge -p '{"spec":{"version":"<original-version>"}}'
23+
// 1. Stop the upgrade process by reverting the target-version annotation to the original version:
24+
// kubectl annotate rabbitmq <name> -n <namespace> rabbitmq.openstack.org/target-version=<original-version> --overwrite
2525
//
2626
// 2. Check the current upgrade phase:
2727
// kubectl get rabbitmq <name> -n <namespace> -o jsonpath='{.status.upgradePhase}'

internal/webhook/rabbitmq/v1beta1/rabbitmq_webhook_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,13 @@ var _ = Describe("RabbitMq webhook", func() {
286286
ObjectMeta: metav1.ObjectMeta{
287287
Name: "test-rabbitmq",
288288
Namespace: "default",
289+
Annotations: map[string]string{
290+
rabbitmqv1beta1.AnnotationTargetVersion: "3.13",
291+
},
289292
},
290293
Spec: rabbitmqv1beta1.RabbitMqSpec{
291294
RabbitMqSpecCore: rabbitmqv1beta1.RabbitMqSpecCore{
292295
QueueType: ptr.To("Quorum"),
293-
Version: ptr.To("3.13"),
294296
},
295297
},
296298
}

0 commit comments

Comments
 (0)