diff --git a/api/v1alpha1/exchange_types.go b/api/v1alpha1/exchange_types.go index 93a31c19..01563a8b 100644 --- a/api/v1alpha1/exchange_types.go +++ b/api/v1alpha1/exchange_types.go @@ -12,6 +12,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ) // ExchangeSpec defines the desired state of Exchange @@ -64,6 +65,13 @@ type ExchangeList struct { Items []Exchange `json:"items"` } +func (e *Exchange) GroupResource() schema.GroupResource { + return schema.GroupResource{ + Group: e.GroupVersionKind().Group, + Resource: e.GroupVersionKind().Kind, + } +} + func init() { SchemeBuilder.Register(&Exchange{}, &ExchangeList{}) } diff --git a/api/v1alpha1/exchange_webhook.go b/api/v1alpha1/exchange_webhook.go new file mode 100644 index 00000000..1d4dc58d --- /dev/null +++ b/api/v1alpha1/exchange_webhook.go @@ -0,0 +1,88 @@ +package v1alpha1 + +import ( + "fmt" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +func (r *Exchange) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1alpha1-exchange,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=exchanges,versions=v1alpha1,name=vexchange.kb.io,sideEffects=none,admissionReviewVersions=v1 + +var _ webhook.Validator = &Exchange{} + +// no validation on create +func (e *Exchange) ValidateCreate() error { + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +// returns error type 'forbidden' for updates that the controller chooses to disallow: exchange name/vhost/rabbitmqClusterReference +// returns error type 'invalid' for updates that will be rejected by rabbitmq server: exchange types/autoDelete/durable +// exchange.spec.arguments can be updated +func (e *Exchange) ValidateUpdate(old runtime.Object) error { + oldExchange, ok := old.(*Exchange) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected an exchange but got a %T", old)) + } + + var allErrs field.ErrorList + detailMsg := "updates on name, vhost, and rabbitmqClusterReference are all forbidden" + if e.Spec.Name != oldExchange.Spec.Name { + return apierrors.NewForbidden(e.GroupResource(), e.Name, + field.Forbidden(field.NewPath("spec", "name"), detailMsg)) + } + + if e.Spec.Vhost != oldExchange.Spec.Vhost { + return apierrors.NewForbidden(e.GroupResource(), e.Name, + field.Forbidden(field.NewPath("spec", "vhost"), detailMsg)) + } + + if e.Spec.RabbitmqClusterReference != oldExchange.Spec.RabbitmqClusterReference { + return apierrors.NewForbidden(e.GroupResource(), e.Name, + field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), detailMsg)) + } + + if e.Spec.Type != oldExchange.Spec.Type { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "type"), + e.Spec.Type, + "exchange type cannot be updated", + )) + } + + if e.Spec.AutoDelete != oldExchange.Spec.AutoDelete { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "autoDelete"), + e.Spec.AutoDelete, + "autoDelete cannot be updated", + )) + } + + if e.Spec.Durable != oldExchange.Spec.Durable { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "durable"), + e.Spec.AutoDelete, + "durable cannot be updated", + )) + } + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(GroupVersion.WithKind("Exchange").GroupKind(), e.Name, allErrs) +} + +// no validation on delete +func (e *Exchange) ValidateDelete() error { + return nil +} diff --git a/api/v1alpha1/exchange_webhook_test.go b/api/v1alpha1/exchange_webhook_test.go new file mode 100644 index 00000000..12ec778a --- /dev/null +++ b/api/v1alpha1/exchange_webhook_test.go @@ -0,0 +1,74 @@ +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +var _ = Describe("exchange webhook", func() { + + var exchange = Exchange{ + ObjectMeta: metav1.ObjectMeta{ + Name: "update-binding", + }, + Spec: ExchangeSpec{ + Name: "test", + Vhost: "/test", + Type: "fanout", + Durable: false, + AutoDelete: true, + RabbitmqClusterReference: RabbitmqClusterReference{ + Name: "some-cluster", + Namespace: "default", + }, + }, + } + + It("does not allow updates on exchange name", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.Name = "new-name" + Expect(apierrors.IsForbidden(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("does not allow updates on vhost", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.Vhost = "/a-new-vhost" + Expect(apierrors.IsForbidden(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("does not allow updates on RabbitmqClusterReference", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.RabbitmqClusterReference = RabbitmqClusterReference{ + Name: "new-cluster", + Namespace: "default", + } + Expect(apierrors.IsForbidden(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("does not allow updates on exchange type", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.Type = "direct" + Expect(apierrors.IsInvalid(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("does not allow updates on durable", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.Durable = true + Expect(apierrors.IsInvalid(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("does not allow updates on autoDelete", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.AutoDelete = false + Expect(apierrors.IsInvalid(newExchange.ValidateUpdate(&exchange))).To(BeTrue()) + }) + + It("allows updates on arguments", func() { + newExchange := exchange.DeepCopy() + newExchange.Spec.Arguments = &runtime.RawExtension{Raw: []byte(`{"new":"new-value"}`)} + Expect(newExchange.ValidateUpdate(&exchange)).To(Succeed()) + }) +}) diff --git a/api/v1alpha1/queue_types.go b/api/v1alpha1/queue_types.go index e2b6ee9f..1758396d 100644 --- a/api/v1alpha1/queue_types.go +++ b/api/v1alpha1/queue_types.go @@ -12,6 +12,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ) // using runtime.RawExtension to represent queue arguments @@ -77,6 +78,13 @@ type QueueList struct { Items []Queue `json:"items"` } +func (q *Queue) GroupResource() schema.GroupResource { + return schema.GroupResource{ + Group: q.GroupVersionKind().Group, + Resource: q.GroupVersionKind().Kind, + } +} + func init() { SchemeBuilder.Register(&Queue{}, &QueueList{}) } diff --git a/api/v1alpha1/queue_webhook.go b/api/v1alpha1/queue_webhook.go new file mode 100644 index 00000000..854658c8 --- /dev/null +++ b/api/v1alpha1/queue_webhook.go @@ -0,0 +1,88 @@ +package v1alpha1 + +import ( + "fmt" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +func (r *Queue) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1alpha1-queue,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=queues,versions=v1alpha1,name=vqueue.kb.io,sideEffects=none,admissionReviewVersions=v1sideEffects=none,admissionReviewVersions=v1 + +var _ webhook.Validator = &Queue{} + +// no validation on create +func (q *Queue) ValidateCreate() error { + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +// returns error type 'forbidden' for updates that the controller chooses to disallow: queue name/vhost/rabbitmqClusterReference +// returns error type 'invalid' for updates that will be rejected by rabbitmq server: queue types/autoDelete/durable +// queue arguments not handled because implementation couldn't change +func (q *Queue) ValidateUpdate(old runtime.Object) error { + oldQueue, ok := old.(*Queue) + if !ok { + return apierrors.NewBadRequest(fmt.Sprintf("expected a queue but got a %T", old)) + } + + var allErrs field.ErrorList + detailMsg := "updates on name, vhost, and rabbitmqClusterReference are all forbidden" + if q.Spec.Name != oldQueue.Spec.Name { + return apierrors.NewForbidden(q.GroupResource(), q.Name, + field.Forbidden(field.NewPath("spec", "name"), detailMsg)) + } + + if q.Spec.Vhost != oldQueue.Spec.Vhost { + return apierrors.NewForbidden(q.GroupResource(), q.Name, + field.Forbidden(field.NewPath("spec", "vhost"), detailMsg)) + } + + if q.Spec.RabbitmqClusterReference != oldQueue.Spec.RabbitmqClusterReference { + return apierrors.NewForbidden(q.GroupResource(), q.Name, + field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), detailMsg)) + } + + if q.Spec.Type != oldQueue.Spec.Type { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "type"), + q.Spec.Type, + "queue type cannot be updated", + )) + } + + if q.Spec.AutoDelete != oldQueue.Spec.AutoDelete { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "autoDelete"), + q.Spec.AutoDelete, + "autoDelete cannot be updated", + )) + } + + if q.Spec.Durable != oldQueue.Spec.Durable { + allErrs = append(allErrs, field.Invalid( + field.NewPath("spec", "durable"), + q.Spec.AutoDelete, + "durable cannot be updated", + )) + } + + if len(allErrs) == 0 { + return nil + } + + return apierrors.NewInvalid(GroupVersion.WithKind("Queue").GroupKind(), q.Name, allErrs) +} + +// no validation on delete +func (q *Queue) ValidateDelete() error { + return nil +} diff --git a/api/v1alpha1/queue_webhook_test.go b/api/v1alpha1/queue_webhook_test.go new file mode 100644 index 00000000..720e5e7e --- /dev/null +++ b/api/v1alpha1/queue_webhook_test.go @@ -0,0 +1,67 @@ +package v1alpha1 + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("queue webhook", func() { + + var queue = Queue{ + ObjectMeta: metav1.ObjectMeta{ + Name: "update-binding", + }, + Spec: QueueSpec{ + Name: "test", + Vhost: "/a-vhost", + Type: "quorum", + Durable: false, + AutoDelete: true, + RabbitmqClusterReference: RabbitmqClusterReference{ + Name: "some-cluster", + Namespace: "default", + }, + }, + } + + It("does not allow updates on queue name", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.Name = "new-name" + Expect(apierrors.IsForbidden(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) + + It("does not allow updates on vhost", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.Vhost = "/new-vhost" + Expect(apierrors.IsForbidden(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) + + It("does not allow updates on RabbitmqClusterReference", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.RabbitmqClusterReference = RabbitmqClusterReference{ + Name: "new-cluster", + Namespace: "default", + } + Expect(apierrors.IsForbidden(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) + + It("does not allow updates on queue type", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.Type = "classic" + Expect(apierrors.IsInvalid(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) + + It("does not allow updates on durable", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.Durable = true + Expect(apierrors.IsInvalid(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) + + It("does not allow updates on autoDelete", func() { + newQueue := queue.DeepCopy() + newQueue.Spec.AutoDelete = false + Expect(apierrors.IsInvalid(newQueue.ValidateUpdate(&queue))).To(BeTrue()) + }) +}) diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 535d2496..101e9a06 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -12,9 +12,13 @@ resources: patchesStrategicMerge: - patches/webhook_in_bindings.yaml +- patches/webhook_in_queues.yaml +- patches/webhook_in_exchanges.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch - patches/cainjection_in_bindings.yaml +- patches/cainjection_in_queues.yaml +- patches/webhook_in_exchanges.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch configurations: diff --git a/config/crd/patches/cainjection_in_exchanges.yaml b/config/crd/patches/cainjection_in_exchanges.yaml index 86a727f6..d16035ac 100644 --- a/config/crd/patches/cainjection_in_exchanges.yaml +++ b/config/crd/patches/cainjection_in_exchanges.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1alpha1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/cainjection_in_queues.yaml b/config/crd/patches/cainjection_in_queues.yaml index 16dbc8a2..1e8d2c48 100644 --- a/config/crd/patches/cainjection_in_queues.yaml +++ b/config/crd/patches/cainjection_in_queues.yaml @@ -1,6 +1,4 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1alpha1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/webhook_in_exchanges.yaml b/config/crd/patches/webhook_in_exchanges.yaml index 291f6fd5..d54a0522 100644 --- a/config/crd/patches/webhook_in_exchanges.yaml +++ b/config/crd/patches/webhook_in_exchanges.yaml @@ -1,17 +1,17 @@ # The following patch enables conversion webhook for CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1alpha1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: exchanges.rabbitmq.com spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + conversionReviewVersions: ["v1", "v1beta1"] + clientConfig: + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/crd/patches/webhook_in_queues.yaml b/config/crd/patches/webhook_in_queues.yaml index 6e367509..4c1bbfe1 100644 --- a/config/crd/patches/webhook_in_queues.yaml +++ b/config/crd/patches/webhook_in_queues.yaml @@ -1,17 +1,15 @@ -# The following patch enables conversion webhook for CRD -# CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1alpha1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: queues.rabbitmq.com spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + conversionReviewVersions: ["v1", "v1beta1"] + clientConfig: + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/crd/patches/webhook_in_users.yaml b/config/crd/patches/webhook_in_users.yaml index bec54c92..489f953f 100644 --- a/config/crd/patches/webhook_in_users.yaml +++ b/config/crd/patches/webhook_in_users.yaml @@ -7,11 +7,11 @@ metadata: spec: conversion: strategy: Webhook - webhookClientConfig: - # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, - # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) - caBundle: Cg== - service: - namespace: system - name: webhook-service - path: /convert + webhook: + conversionReviewVersions: ["v1", "v1beta1"] + clientConfig: + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 40de2130..7ff5d598 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -26,3 +26,43 @@ webhooks: resources: - bindings sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-rabbitmq-com-v1alpha1-exchange + failurePolicy: Fail + name: vexchange.kb.io + rules: + - apiGroups: + - rabbitmq.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - exchanges + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-rabbitmq-com-v1alpha1-queue + failurePolicy: Fail + name: vqueue.kb.io + rules: + - apiGroups: + - rabbitmq.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - queues + sideEffects: None diff --git a/main.go b/main.go index 68fad4e2..fdc0ccd4 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,6 @@ import ( rabbitmqv1beta1 "github.com/rabbitmq/cluster-operator/api/v1beta1" - rabbitmqcomv1alpha1 "github.com/rabbitmq/messaging-topology-operator/api/v1alpha1" topologyv1alpha1 "github.com/rabbitmq/messaging-topology-operator/api/v1alpha1" "github.com/rabbitmq/messaging-topology-operator/controllers" // +kubebuilder:scaffold:imports @@ -119,10 +118,18 @@ func main() { log.Error(err, "unable to create controller", "controller", policyControllerName) os.Exit(1) } - if err = (&rabbitmqcomv1alpha1.Binding{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&topologyv1alpha1.Binding{}).SetupWebhookWithManager(mgr); err != nil { log.Error(err, "unable to create webhook", "webhook", "Binding") os.Exit(1) } + if err = (&topologyv1alpha1.Queue{}).SetupWebhookWithManager(mgr); err != nil { + log.Error(err, "unable to create webhook", "webhook", "Queue") + os.Exit(1) + } + if err = (&topologyv1alpha1.Exchange{}).SetupWebhookWithManager(mgr); err != nil { + log.Error(err, "unable to create webhook", "webhook", "Exchange") + os.Exit(1) + } // +kubebuilder:scaffold:builder log.Info("starting manager") diff --git a/system_tests/exchange_system_tests.go b/system_tests/exchange_system_tests.go index a28648a2..0aaf5294 100644 --- a/system_tests/exchange_system_tests.go +++ b/system_tests/exchange_system_tests.go @@ -65,18 +65,27 @@ var _ = Describe("Exchange", func() { Expect(exchangeInfo.Arguments).To(HaveKeyWithValue("alternate-exchange", "system-test")) By("updating status condition 'Ready'") - updatedExchange := topologyv1alpha1.Exchange{} - Expect(k8sClient.Get(ctx, types.NamespacedName{Name: exchange.Name, Namespace: exchange.Namespace}, &updatedExchange)).To(Succeed()) + fetched := topologyv1alpha1.Exchange{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: exchange.Name, Namespace: exchange.Namespace}, &fetched)).To(Succeed()) - Expect(updatedExchange.Status.Conditions).To(HaveLen(1)) - readyCondition := updatedExchange.Status.Conditions[0] + Expect(fetched.Status.Conditions).To(HaveLen(1)) + readyCondition := fetched.Status.Conditions[0] Expect(string(readyCondition.Type)).To(Equal("Ready")) Expect(readyCondition.Status).To(Equal(corev1.ConditionTrue)) Expect(readyCondition.Reason).To(Equal("SuccessfulCreateOrUpdate")) Expect(readyCondition.LastTransitionTime).NotTo(Equal(metav1.Time{})) By("setting status.observedGeneration") - Expect(updatedExchange.Status.ObservedGeneration).To(Equal(updatedExchange.GetGeneration())) + Expect(fetched.Status.ObservedGeneration).To(Equal(fetched.GetGeneration())) + + By("not allowing certain updates") + updatedExchange := topologyv1alpha1.Exchange{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: exchange.Name, Namespace: exchange.Namespace}, &updatedExchange)).To(Succeed()) + updatedExchange.Spec.Vhost = "/new-vhost" + Expect(k8sClient.Update(ctx, &updatedExchange).Error()).To(ContainSubstring("spec.vhost: Forbidden: updates on name, vhost, and rabbitmqClusterReference are all forbidden")) + updatedExchange.Spec.Vhost = exchange.Spec.Vhost + updatedExchange.Spec.Durable = false + Expect(k8sClient.Update(ctx, &updatedExchange).Error()).To(ContainSubstring("spec.durable: Invalid value: false: durable cannot be updated")) By("deleting exchange") Expect(k8sClient.Delete(ctx, exchange)).To(Succeed()) diff --git a/system_tests/queue_system_tests.go b/system_tests/queue_system_tests.go index d7e30946..310b7930 100644 --- a/system_tests/queue_system_tests.go +++ b/system_tests/queue_system_tests.go @@ -78,6 +78,15 @@ var _ = Describe("Queue Controller", func() { By("setting status.observedGeneration") Expect(updatedQueue.Status.ObservedGeneration).To(Equal(updatedQueue.GetGeneration())) + By("not allowing certain updates") + updateQ := topologyv1alpha1.Queue{} + Expect(k8sClient.Get(ctx, types.NamespacedName{Name: q.Name, Namespace: q.Namespace}, &updateQ)).To(Succeed()) + updateQ.Spec.Name = "a-new-name" + Expect(k8sClient.Update(ctx, &updateQ).Error()).To(ContainSubstring("spec.name: Forbidden: updates on name, vhost, and rabbitmqClusterReference are all forbidden")) + updateQ.Spec.Name = q.Spec.Name + updateQ.Spec.Type = "classic" + Expect(k8sClient.Update(ctx, &updateQ).Error()).To(ContainSubstring("spec.type: Invalid value: \"classic\": queue type cannot be updated")) + By("deleting queue") Expect(k8sClient.Delete(ctx, q)).To(Succeed()) var err error