From b7891d7667f5de3f2a2380bf385dce7e855ad8fa Mon Sep 17 00:00:00 2001 From: Luca Miccini Date: Tue, 23 Dec 2025 10:29:09 +0100 Subject: [PATCH] Rabbitmq vhost and user support Add support for a dedicated rabbitmq cluster for notifications. Add new messagingBus and notificationsBus interfaces to hold cluster, user and vhost names for optional usage. The controller adds these values to the TransportURL create request when present. Additionally, we migrate RabbitMQ cluster name to RabbitMq config struct using DefaultRabbitMqConfig from infra-operator to automatically populate the new Cluster field from legacy RabbitMqClusterName. Example usage: spec: messagingBus: cluster: rpc-rabbitmq user: rpc-user vhost: rpc-vhost notificationsBus: cluster: notifications-rabbitmq user: notifications-user vhost: notifications-vhost Jira: https://issues.redhat.com/browse/OSPRH-23909 --- Makefile | 2 +- .../telemetry.openstack.org_autoscalings.yaml | 39 ++++++ .../telemetry.openstack.org_ceilometers.yaml | 38 ++++++ .../telemetry.openstack.org_cloudkitties.yaml | 38 ++++++ .../telemetry.openstack.org_telemetries.yaml | 105 +++++++++++++++ api/go.mod | 14 +- api/go.sum | 21 +-- api/v1beta1/autoscaling_types.go | 13 ++ api/v1beta1/autoscaling_webhook.go | 70 +++++++++- api/v1beta1/ceilometer_types.go | 17 ++- api/v1beta1/ceilometer_webhook.go | 75 ++++++++++- api/v1beta1/cloudkitty_types.go | 13 ++ api/v1beta1/cloudkitty_webhook.go | 22 ++++ api/v1beta1/telemetry_webhook.go | 8 ++ api/v1beta1/telemetry_webhook_test.go | 26 ++++ api/v1beta1/zz_generated.deepcopy.go | 34 +++++ .../telemetry.openstack.org_autoscalings.yaml | 39 ++++++ .../telemetry.openstack.org_ceilometers.yaml | 38 ++++++ .../telemetry.openstack.org_cloudkitties.yaml | 38 ++++++ .../telemetry.openstack.org_telemetries.yaml | 105 +++++++++++++++ go.mod | 13 +- go.sum | 18 ++- internal/autoscaling/const.go | 16 +++ internal/ceilometer/const.go | 16 +++ internal/cloudkitty/const.go | 13 ++ internal/controller/autoscaling_controller.go | 112 +++++++++++++++- internal/controller/ceilometer_controller.go | 122 ++++++++++++++++-- internal/controller/cloudkitty_controller.go | 102 ++++++++++++++- templates/autoscaling/config/aodh.conf | 8 +- .../ceilometercentral/config/ceilometer.conf | 4 + templates/cloudkitty/config/cloudkitty.conf | 8 ++ 31 files changed, 1117 insertions(+), 70 deletions(-) create mode 100644 api/v1beta1/telemetry_webhook_test.go diff --git a/Makefile b/Makefile index 72153f294..e9f2619f1 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ tidy: ## Run go mod tidy on every mod file in the repo .PHONY: test test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... ./api/... -coverprofile cover.out ##@ Build diff --git a/api/bases/telemetry.openstack.org_autoscalings.yaml b/api/bases/telemetry.openstack.org_autoscalings.yaml index 6b6d7c53b..7ec6e2335 100644 --- a/api/bases/telemetry.openstack.org_autoscalings.yaml +++ b/api/bases/telemetry.openstack.org_autoscalings.yaml @@ -104,6 +104,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object networkAttachmentDefinitions: description: NetworkAttachmentDefinitions list of network attachment definitions the service pod gets attached to @@ -116,6 +133,23 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object notifierImage: type: string override: @@ -309,6 +343,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Aodh + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -496,6 +531,10 @@ spec: items: type: string type: array + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this diff --git a/api/bases/telemetry.openstack.org_ceilometers.yaml b/api/bases/telemetry.openstack.org_ceilometers.yaml index 68c4de188..25fbc461a 100644 --- a/api/bases/telemetry.openstack.org_ceilometers.yaml +++ b/api/bases/telemetry.openstack.org_ceilometers.yaml @@ -153,6 +153,22 @@ spec: description: SecretName - holding the cert, key for the service type: string type: object + messagingBus: + description: MessagingBus configuration (username, vhost, and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object mysqldExporterDatabaseAccountPrefix: default: mysqld-exporter description: |- @@ -193,6 +209,23 @@ spec: type: object notificationImage: type: string + notificationsBus: + description: NotificationsBus configuration (username, vhost, and + cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: ceilometerService: CeilometerPassword @@ -222,6 +255,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Telemetry + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -367,6 +401,10 @@ spec: items: type: string type: array + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this diff --git a/api/bases/telemetry.openstack.org_cloudkitties.yaml b/api/bases/telemetry.openstack.org_cloudkitties.yaml index 5b5b615ea..a8648390f 100644 --- a/api/bases/telemetry.openstack.org_cloudkitties.yaml +++ b/api/bases/telemetry.openstack.org_cloudkitties.yaml @@ -523,6 +523,22 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object nodeSelector: additionalProperties: type: string @@ -531,6 +547,23 @@ spec: NodeSelector here acts as a default value and can be overridden by service specific NodeSelector Settings. type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, and + cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: cloudKittyService: CloudKittyPassword @@ -602,6 +635,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in CloudKitty + Deprecated: Use MessagingBus.Cluster instead type: string s3StorageConfig: default: @@ -771,6 +805,10 @@ spec: type: string description: Map of hashes to track e.g. job status type: object + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this service. diff --git a/api/bases/telemetry.openstack.org_telemetries.yaml b/api/bases/telemetry.openstack.org_telemetries.yaml index 5a78108cf..f180b708c 100644 --- a/api/bases/telemetry.openstack.org_telemetries.yaml +++ b/api/bases/telemetry.openstack.org_telemetries.yaml @@ -107,6 +107,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, + and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object networkAttachmentDefinitions: description: NetworkAttachmentDefinitions list of network attachment definitions the service pod gets attached to @@ -119,6 +136,23 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object notifierImage: type: string override: @@ -312,6 +346,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Aodh + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -475,6 +510,23 @@ spec: description: SecretName - holding the cert, key for the service type: string type: object + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object mysqldExporterDatabaseAccountPrefix: default: mysqld-exporter description: |- @@ -515,6 +567,23 @@ spec: type: object notificationImage: type: string + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: ceilometerService: CeilometerPassword @@ -544,6 +613,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Telemetry + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -1088,6 +1158,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object nodeSelector: additionalProperties: type: string @@ -1096,6 +1183,23 @@ spec: NodeSelector here acts as a default value and can be overridden by service specific NodeSelector Settings. type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: cloudKittyService: CloudKittyPassword @@ -1168,6 +1272,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in CloudKitty + Deprecated: Use MessagingBus.Cluster instead type: string s3StorageConfig: default: diff --git a/api/go.mod b/api/go.mod index 61f7d97df..1685318c7 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,11 +3,12 @@ module github.com/openstack-k8s-operators/telemetry-operator/api go 1.24.4 require ( - github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e - github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077 + github.com/onsi/gomega v1.38.2 + github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e + github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c github.com/rhobs/observability-operator v0.3.1 - k8s.io/api v0.31.13 - k8s.io/apimachinery v0.31.13 + k8s.io/api v0.31.14 + k8s.io/apimachinery v0.31.14 sigs.k8s.io/controller-runtime v0.19.7 ) @@ -16,7 +17,6 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect - github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect @@ -38,12 +38,12 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.27.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/rabbitmq/cluster-operator/v2 v2.16.0 // indirect github.com/rhobs/obo-prometheus-operator/pkg/apis/monitoring v0.71.0-rhobs1 // indirect github.com/spf13/pflag v1.0.7 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -61,7 +61,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.33.2 // indirect - k8s.io/client-go v0.31.13 // indirect + k8s.io/client-go v0.31.14 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect diff --git a/api/go.sum b/api/go.sum index eb99dd834..03b74a6b0 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,3 +1,4 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -74,14 +75,16 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.27.1 h1:0LJC8MpUSQnfnp4n/3W3GdlmJP3ENGF0ZPzjQGLPP7s= -github.com/onsi/ginkgo/v2 v2.27.1/go.mod h1:wmy3vCqiBjirARfVhAqFpYt8uvX0yaFe+GudAqqcCqA= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e h1:5q47hHT53v0PnNj2pwHHQ1+ZWC3kQLu1jtulTUrJ2cE= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e/go.mod h1:LfqzznghLpo+b9jVgyvqUoOZMcc3Ff0gXSmLLtFsj9w= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077 h1:missBxDwEfOdkHVKd6zyCyaQjSObw9Ge1O4A7WU5EuM= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077/go.mod h1:CjsYQ/dUr4eUmBEvM3UFUxvYvl2bAhGfGflaD+N4fWA= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e h1:PIjcXzMMwfvBRFgFpaq/W9tqy0t2cYvcWX+kq6uNtTM= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e/go.mod h1:ex8ou6/3ms6ovR+CMXD6XhTlNakm1GhB6UZgagVRNW8= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c h1:wM8qXCB5mQwSosCvtaydzuXitWVVKBHTzH0A2znQ+Jg= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:+Me0raWPPdz8gRi9D4z1khmvUgS9vIKAVC8ckg1yJZU= +github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec h1:saovr368HPAKHN0aRPh8h8n9s9dn3d8Frmfua0UYRlc= +github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec/go.mod h1:Nh2NEePLjovUQof2krTAg4JaAoLacqtPTZQXK6izNfg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -111,14 +114,12 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/api/v1beta1/autoscaling_types.go b/api/v1beta1/autoscaling_types.go index f541155eb..b260fd0f2 100644 --- a/api/v1beta1/autoscaling_types.go +++ b/api/v1beta1/autoscaling_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" @@ -63,8 +64,17 @@ type AodhCore struct { // APITimeout for Route and Apache APITimeout int `json:"apiTimeout"` + // +kubebuilder:validation:Optional + // MessagingBus configuration (username, vhost, and cluster) + MessagingBus rabbitmqv1.RabbitMqConfig `json:"messagingBus,omitempty"` + + // +kubebuilder:validation:Optional + // NotificationsBus configuration (username, vhost, and cluster) for notifications + NotificationsBus *rabbitmqv1.RabbitMqConfig `json:"notificationsBus,omitempty"` + // RabbitMQ instance name // Needed to request a transportURL that is created and used in Aodh + // Deprecated: Use MessagingBus.Cluster instead // +kubebuilder:default=rabbitmq RabbitMqClusterName string `json:"rabbitMqClusterName,omitempty"` @@ -195,6 +205,9 @@ type AutoscalingStatus struct { // TransportURLSecret - Secret containing RabbitMQ transportURL TransportURLSecret string `json:"transportURLSecret,omitempty"` + // NotificationsURLSecret - Secret containing RabbitMQ notification transportURL + NotificationsURLSecret *string `json:"notificationsURLSecret,omitempty"` + // DatabaseHostname - Hostname for the database DatabaseHostname string `json:"databaseHostname,omitempty"` diff --git a/api/v1beta1/autoscaling_webhook.go b/api/v1beta1/autoscaling_webhook.go index 903aa7319..987c35653 100644 --- a/api/v1beta1/autoscaling_webhook.go +++ b/api/v1beta1/autoscaling_webhook.go @@ -19,7 +19,11 @@ package v1beta1 import ( "fmt" + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -73,6 +77,11 @@ func (spec *AutoscalingSpec) Default() { // Default - note only *Core* versions like this will have validations that are called from the // Controlplane webhook func (spec *AodhCore) Default() { + if spec.RabbitMqClusterName == "" { + spec.RabbitMqClusterName = "rabbitmq" + } + rabbitmqv1.DefaultRabbitMqConfig(&spec.MessagingBus, spec.RabbitMqClusterName) + if spec.MemcachedInstance == "" { spec.MemcachedInstance = "memcached" } @@ -111,7 +120,19 @@ var _ webhook.Validator = &Autoscaling{} func (r *Autoscaling) ValidateCreate() (admission.Warnings, error) { autoscalinglog.Info("validate create", "name", r.Name) - // TODO(user): fill in your validation logic upon object creation. + var allErrs field.ErrorList + basePath := field.NewPath("spec") + + if err := r.Spec.Aodh.ValidateCreate(basePath.Child("aodh"), r.Namespace); err != nil { + allErrs = append(allErrs, err...) + } + + if len(allErrs) != 0 { + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "telemetry.openstack.org", Kind: "Autoscaling"}, + r.Name, allErrs) + } + return nil, nil } @@ -119,7 +140,24 @@ func (r *Autoscaling) ValidateCreate() (admission.Warnings, error) { func (r *Autoscaling) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { autoscalinglog.Info("validate update", "name", r.Name) - // TODO(user): fill in your validation logic upon object update. + oldAutoscaling, ok := old.(*Autoscaling) + if !ok || oldAutoscaling == nil { + return nil, apierrors.NewInternalError(nil) + } + + var allErrs field.ErrorList + basePath := field.NewPath("spec") + + if err := r.Spec.Aodh.ValidateUpdate(oldAutoscaling.Spec.Aodh.AodhCore, basePath.Child("aodh"), r.Namespace); err != nil { + allErrs = append(allErrs, err...) + } + + if len(allErrs) != 0 { + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "telemetry.openstack.org", Kind: "Autoscaling"}, + r.Name, allErrs) + } + return nil, nil } @@ -127,6 +165,32 @@ func (r *Autoscaling) ValidateUpdate(old runtime.Object) (admission.Warnings, er func (r *Autoscaling) ValidateDelete() (admission.Warnings, error) { autoscalinglog.Info("validate delete", "name", r.Name) - // TODO(user): fill in your validation logic upon object deletion. return nil, nil } + +// ValidateCreate - Exported function wrapping non-exported validate functions, +// this function can be called externally to validate an aodh spec. +func (spec *AodhCore) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + allErrs = append(allErrs, spec.ValidateTopology(basePath, namespace)...) + + return allErrs +} + +// ValidateUpdate - Exported function wrapping non-exported validate functions, +// this function can be called externally to validate an aodh spec. +func (spec *AodhCore) ValidateUpdate(old AodhCore, basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + // Reject changes to deprecated RabbitMqClusterName field + if spec.RabbitMqClusterName != old.RabbitMqClusterName { + allErrs = append(allErrs, field.Forbidden( + basePath.Child("rabbitMqClusterName"), + "rabbitMqClusterName is deprecated and cannot be changed. Please use messagingBus.cluster instead")) + } + + allErrs = append(allErrs, spec.ValidateTopology(basePath, namespace)...) + + return allErrs +} diff --git a/api/v1beta1/ceilometer_types.go b/api/v1beta1/ceilometer_types.go index e3545b3f7..b0b5809ca 100644 --- a/api/v1beta1/ceilometer_types.go +++ b/api/v1beta1/ceilometer_types.go @@ -17,12 +17,13 @@ limitations under the License. package v1beta1 import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/validation/field" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" ) const ( @@ -81,8 +82,17 @@ type CeilometerSpecCore struct { // APITimeout for Apache APITimeout int `json:"apiTimeout"` + // +kubebuilder:validation:Optional + // MessagingBus configuration (username, vhost, and cluster) + MessagingBus rabbitmqv1.RabbitMqConfig `json:"messagingBus,omitempty"` + + // +kubebuilder:validation:Optional + // NotificationsBus configuration (username, vhost, and cluster) for notifications + NotificationsBus *rabbitmqv1.RabbitMqConfig `json:"notificationsBus,omitempty"` + // RabbitMQ instance name // Needed to request a transportURL that is created and used in Telemetry + // Deprecated: Use MessagingBus.Cluster instead // +kubebuilder:default=rabbitmq RabbitMqClusterName string `json:"rabbitMqClusterName,omitempty"` @@ -171,6 +181,9 @@ type CeilometerStatus struct { // TransportURLSecret - Secret containing RabbitMQ transportURL TransportURLSecret string `json:"transportURLSecret,omitempty"` + // NotificationsURLSecret - Secret containing RabbitMQ notification transportURL + NotificationsURLSecret *string `json:"notificationsURLSecret,omitempty"` + // Networks in addtion to the cluster network, the service is attached to Networks []string `json:"networks,omitempty"` diff --git a/api/v1beta1/ceilometer_webhook.go b/api/v1beta1/ceilometer_webhook.go index be2e3351b..b281d67b6 100644 --- a/api/v1beta1/ceilometer_webhook.go +++ b/api/v1beta1/ceilometer_webhook.go @@ -17,7 +17,11 @@ limitations under the License. package v1beta1 import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -81,6 +85,16 @@ func (spec *CeilometerSpec) Default() { if spec.MysqldExporterImage == "" { spec.MysqldExporterImage = ceilometerDefaults.MysqldExporterContainerImageURL } + + spec.CeilometerSpecCore.Default() +} + +// Default - set defaults for this CeilometerSpecCore. NOTE: this version is used by the OpenStackControlplane webhook +func (spec *CeilometerSpecCore) Default() { + if spec.RabbitMqClusterName == "" { + spec.RabbitMqClusterName = "rabbitmq" + } + rabbitmqv1.DefaultRabbitMqConfig(&spec.MessagingBus, spec.RabbitMqClusterName) } var _ webhook.Validator = &Ceilometer{} @@ -89,7 +103,19 @@ var _ webhook.Validator = &Ceilometer{} func (r *Ceilometer) ValidateCreate() (admission.Warnings, error) { ceilometerlog.Info("validate create", "name", r.Name) - // TODO(user): fill in your validation logic upon object creation. + var allErrs field.ErrorList + basePath := field.NewPath("spec") + + if err := r.Spec.CeilometerSpecCore.ValidateCreate(basePath, r.Namespace); err != nil { + allErrs = append(allErrs, err...) + } + + if len(allErrs) != 0 { + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "telemetry.openstack.org", Kind: "Ceilometer"}, + r.Name, allErrs) + } + return nil, nil } @@ -97,7 +123,24 @@ func (r *Ceilometer) ValidateCreate() (admission.Warnings, error) { func (r *Ceilometer) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { ceilometerlog.Info("validate update", "name", r.Name) - // TODO(user): fill in your validation logic upon object update. + oldCeilometer, ok := old.(*Ceilometer) + if !ok || oldCeilometer == nil { + return nil, apierrors.NewInternalError(nil) + } + + var allErrs field.ErrorList + basePath := field.NewPath("spec") + + if err := r.Spec.CeilometerSpecCore.ValidateUpdate(oldCeilometer.Spec.CeilometerSpecCore, basePath, r.Namespace); err != nil { + allErrs = append(allErrs, err...) + } + + if len(allErrs) != 0 { + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "telemetry.openstack.org", Kind: "Ceilometer"}, + r.Name, allErrs) + } + return nil, nil } @@ -105,6 +148,32 @@ func (r *Ceilometer) ValidateUpdate(old runtime.Object) (admission.Warnings, err func (r *Ceilometer) ValidateDelete() (admission.Warnings, error) { ceilometerlog.Info("validate delete", "name", r.Name) - // TODO(user): fill in your validation logic upon object deletion. return nil, nil } + +// ValidateCreate - Exported function wrapping non-exported validate functions, +// this function can be called externally to validate a ceilometer spec. +func (spec *CeilometerSpecCore) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + allErrs = append(allErrs, spec.ValidateTopology(basePath, namespace)...) + + return allErrs +} + +// ValidateUpdate - Exported function wrapping non-exported validate functions, +// this function can be called externally to validate a ceilometer spec. +func (spec *CeilometerSpecCore) ValidateUpdate(old CeilometerSpecCore, basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + // Reject changes to deprecated RabbitMqClusterName field + if spec.RabbitMqClusterName != old.RabbitMqClusterName { + allErrs = append(allErrs, field.Forbidden( + basePath.Child("rabbitMqClusterName"), + "rabbitMqClusterName is deprecated and cannot be changed. Please use messagingBus.cluster instead")) + } + + allErrs = append(allErrs, spec.ValidateTopology(basePath, namespace)...) + + return allErrs +} diff --git a/api/v1beta1/cloudkitty_types.go b/api/v1beta1/cloudkitty_types.go index 717a7061b..29887c5ae 100644 --- a/api/v1beta1/cloudkitty_types.go +++ b/api/v1beta1/cloudkitty_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" @@ -133,10 +134,19 @@ type CloudKittySpecBase struct { // Might not be required in future DatabaseInstance string `json:"databaseInstance"` + // +kubebuilder:validation:Optional + // MessagingBus configuration (username, vhost, and cluster) + MessagingBus rabbitmqv1.RabbitMqConfig `json:"messagingBus,omitempty"` + + // +kubebuilder:validation:Optional + // NotificationsBus configuration (username, vhost, and cluster) for notifications + NotificationsBus *rabbitmqv1.RabbitMqConfig `json:"notificationsBus,omitempty"` + // +kubebuilder:validation:Optional // +kubebuilder:default=rabbitmq // RabbitMQ instance name // Needed to request a transportURL that is created and used in CloudKitty + // Deprecated: Use MessagingBus.Cluster instead RabbitMqClusterName string `json:"rabbitMqClusterName"` // +kubebuilder:validation:Optional @@ -308,6 +318,9 @@ type CloudKittyStatus struct { // TransportURLSecret - Secret containing RabbitMQ transportURL TransportURLSecret string `json:"transportURLSecret,omitempty"` + // NotificationsURLSecret - Secret containing RabbitMQ notification transportURL + NotificationsURLSecret *string `json:"notificationsURLSecret,omitempty"` + // API endpoints APIEndpoints map[string]map[string]string `json:"apiEndpoints,omitempty"` diff --git a/api/v1beta1/cloudkitty_webhook.go b/api/v1beta1/cloudkitty_webhook.go index f69c5b5b4..81f09cf2d 100644 --- a/api/v1beta1/cloudkitty_webhook.go +++ b/api/v1beta1/cloudkitty_webhook.go @@ -20,6 +20,7 @@ import ( "fmt" "slices" + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -64,6 +65,20 @@ func (spec *CloudKittySpec) Default() { spec.CloudKittyProc.ContainerImage = cloudKittyDefaults.ProcContainerImageURL } + spec.CloudKittySpecBase.Default() +} + +// Default - set defaults for this CloudKittySpecCore. NOTE: this version is used by the OpenStackControlplane webhook +func (spec *CloudKittySpecCore) Default() { + spec.CloudKittySpecBase.Default() +} + +// Default - set defaults for this CloudKittySpecBase +func (spec *CloudKittySpecBase) Default() { + if spec.RabbitMqClusterName == "" { + spec.RabbitMqClusterName = "rabbitmq" + } + rabbitmqv1.DefaultRabbitMqConfig(&spec.MessagingBus, spec.RabbitMqClusterName) } var _ webhook.Validator = &CloudKitty{} @@ -196,6 +211,13 @@ func (r *CloudKittySpecCore) ValidateUpdate(old CloudKittySpecCore, basePath *fi func (r *CloudKittySpecBase) ValidateUpdate(old CloudKittySpecBase, basePath *field.Path, namespace string) field.ErrorList { var allErrs field.ErrorList + // Reject changes to deprecated RabbitMqClusterName field + if r.RabbitMqClusterName != old.RabbitMqClusterName { + allErrs = append(allErrs, field.Forbidden( + basePath.Child("rabbitMqClusterName"), + "rabbitMqClusterName is deprecated and cannot be changed. Please use messagingBus.cluster instead")) + } + allErrs = append(allErrs, r.S3StorageConfig.Validate(basePath.Child("s3StorageConfig"))...) // TODO: Add other CK spec field validations as needed diff --git a/api/v1beta1/telemetry_webhook.go b/api/v1beta1/telemetry_webhook.go index 2bd919f2d..acb831d16 100644 --- a/api/v1beta1/telemetry_webhook.go +++ b/api/v1beta1/telemetry_webhook.go @@ -114,12 +114,20 @@ func (spec *TelemetrySpec) Default() { if spec.CloudKitty.CloudKittyProc.ContainerImage == "" { spec.CloudKitty.CloudKittyProc.ContainerImage = telemetryDefaults.CloudKittyProcContainerImageURL } + + // Call nested Default() methods to set rabbitmq cluster defaults + spec.Autoscaling.AutoscalingSpec.Default() + spec.Autoscaling.Aodh.Default() + spec.Ceilometer.CeilometerSpec.Default() + spec.CloudKitty.CloudKittySpec.Default() } // Default - set defaults for this Telemetry spec core // NOTE: only this version gets called by the Controlplane Webhook func (spec *TelemetrySpecCore) Default() { spec.Autoscaling.Aodh.Default() + spec.Ceilometer.Default() + spec.CloudKitty.Default() } var _ webhook.Validator = &Telemetry{} diff --git a/api/v1beta1/telemetry_webhook_test.go b/api/v1beta1/telemetry_webhook_test.go new file mode 100644 index 000000000..6f74f6322 --- /dev/null +++ b/api/v1beta1/telemetry_webhook_test.go @@ -0,0 +1,26 @@ +package v1beta1 + +import ( + "testing" + + . "github.com/onsi/gomega" +) + +func TestTelemetrySpecCoreDefault(t *testing.T) { + g := NewWithT(t) + + spec := &TelemetrySpecCore{} + spec.Ceilometer.RabbitMqClusterName = "test-rabbitmq" + spec.CloudKitty.RabbitMqClusterName = "test-cloudkitty-rabbitmq" + + // Call Default() which should migrate rabbitMqClusterName to messagingBus.cluster + spec.Default() + + // Verify Ceilometer got defaulted + g.Expect(spec.Ceilometer.MessagingBus.Cluster).To(Equal("test-rabbitmq"), + "Ceilometer messagingBus.cluster should be defaulted from rabbitMqClusterName") + + // Verify CloudKitty got defaulted + g.Expect(spec.CloudKitty.MessagingBus.Cluster).To(Equal("test-cloudkitty-rabbitmq"), + "CloudKitty messagingBus.cluster should be defaulted from rabbitMqClusterName") +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 3a99ea0fd..9f767ac58 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ package v1beta1 import ( networkv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1" + rabbitmqv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" topologyv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/service" @@ -71,6 +72,12 @@ func (in *Aodh) DeepCopy() *Aodh { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AodhCore) DeepCopyInto(out *AodhCore) { *out = *in + out.MessagingBus = in.MessagingBus + if in.NotificationsBus != nil { + in, out := &in.NotificationsBus, &out.NotificationsBus + *out = new(rabbitmqv1beta1.RabbitMqConfig) + **out = **in + } out.PasswordSelectors = in.PasswordSelectors if in.DefaultConfigOverwrite != nil { in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite @@ -306,6 +313,11 @@ func (in *AutoscalingStatus) DeepCopyInto(out *AutoscalingStatus) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.NotificationsURLSecret != nil { + in, out := &in.NotificationsURLSecret, &out.NotificationsURLSecret + *out = new(string) + **out = **in + } if in.APIEndpoints != nil { in, out := &in.APIEndpoints, &out.APIEndpoints *out = make(map[string]string, len(*in)) @@ -481,6 +493,12 @@ func (in *CeilometerSpec) DeepCopy() *CeilometerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CeilometerSpecCore) DeepCopyInto(out *CeilometerSpecCore) { *out = *in + out.MessagingBus = in.MessagingBus + if in.NotificationsBus != nil { + in, out := &in.NotificationsBus, &out.NotificationsBus + *out = new(rabbitmqv1beta1.RabbitMqConfig) + **out = **in + } out.PasswordSelectors = in.PasswordSelectors if in.DefaultConfigOverwrite != nil { in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite @@ -552,6 +570,11 @@ func (in *CeilometerStatus) DeepCopyInto(out *CeilometerStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.NotificationsURLSecret != nil { + in, out := &in.NotificationsURLSecret, &out.NotificationsURLSecret + *out = new(string) + **out = **in + } if in.Networks != nil { in, out := &in.Networks, &out.Networks *out = make([]string, len(*in)) @@ -1127,6 +1150,12 @@ func (in *CloudKittySpec) DeepCopy() *CloudKittySpec { func (in *CloudKittySpecBase) DeepCopyInto(out *CloudKittySpecBase) { *out = *in out.CloudKittyTemplate = in.CloudKittyTemplate + out.MessagingBus = in.MessagingBus + if in.NotificationsBus != nil { + in, out := &in.NotificationsBus, &out.NotificationsBus + *out = new(rabbitmqv1beta1.RabbitMqConfig) + **out = **in + } if in.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = new(map[string]string) @@ -1196,6 +1225,11 @@ func (in *CloudKittyStatus) DeepCopyInto(out *CloudKittyStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.NotificationsURLSecret != nil { + in, out := &in.NotificationsURLSecret, &out.NotificationsURLSecret + *out = new(string) + **out = **in + } if in.APIEndpoints != nil { in, out := &in.APIEndpoints, &out.APIEndpoints *out = make(map[string]map[string]string, len(*in)) diff --git a/config/crd/bases/telemetry.openstack.org_autoscalings.yaml b/config/crd/bases/telemetry.openstack.org_autoscalings.yaml index 6b6d7c53b..7ec6e2335 100644 --- a/config/crd/bases/telemetry.openstack.org_autoscalings.yaml +++ b/config/crd/bases/telemetry.openstack.org_autoscalings.yaml @@ -104,6 +104,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object networkAttachmentDefinitions: description: NetworkAttachmentDefinitions list of network attachment definitions the service pod gets attached to @@ -116,6 +133,23 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object notifierImage: type: string override: @@ -309,6 +343,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Aodh + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -496,6 +531,10 @@ spec: items: type: string type: array + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this diff --git a/config/crd/bases/telemetry.openstack.org_ceilometers.yaml b/config/crd/bases/telemetry.openstack.org_ceilometers.yaml index 68c4de188..25fbc461a 100644 --- a/config/crd/bases/telemetry.openstack.org_ceilometers.yaml +++ b/config/crd/bases/telemetry.openstack.org_ceilometers.yaml @@ -153,6 +153,22 @@ spec: description: SecretName - holding the cert, key for the service type: string type: object + messagingBus: + description: MessagingBus configuration (username, vhost, and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object mysqldExporterDatabaseAccountPrefix: default: mysqld-exporter description: |- @@ -193,6 +209,23 @@ spec: type: object notificationImage: type: string + notificationsBus: + description: NotificationsBus configuration (username, vhost, and + cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: ceilometerService: CeilometerPassword @@ -222,6 +255,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Telemetry + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -367,6 +401,10 @@ spec: items: type: string type: array + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this diff --git a/config/crd/bases/telemetry.openstack.org_cloudkitties.yaml b/config/crd/bases/telemetry.openstack.org_cloudkitties.yaml index 5b5b615ea..a8648390f 100644 --- a/config/crd/bases/telemetry.openstack.org_cloudkitties.yaml +++ b/config/crd/bases/telemetry.openstack.org_cloudkitties.yaml @@ -523,6 +523,22 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object nodeSelector: additionalProperties: type: string @@ -531,6 +547,23 @@ spec: NodeSelector here acts as a default value and can be overridden by service specific NodeSelector Settings. type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, and + cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: cloudKittyService: CloudKittyPassword @@ -602,6 +635,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in CloudKitty + Deprecated: Use MessagingBus.Cluster instead type: string s3StorageConfig: default: @@ -771,6 +805,10 @@ spec: type: string description: Map of hashes to track e.g. job status type: object + notificationsURLSecret: + description: NotificationsURLSecret - Secret containing RabbitMQ notification + transportURL + type: string observedGeneration: description: |- ObservedGeneration - the most recent generation observed for this service. diff --git a/config/crd/bases/telemetry.openstack.org_telemetries.yaml b/config/crd/bases/telemetry.openstack.org_telemetries.yaml index 5a78108cf..f180b708c 100644 --- a/config/crd/bases/telemetry.openstack.org_telemetries.yaml +++ b/config/crd/bases/telemetry.openstack.org_telemetries.yaml @@ -107,6 +107,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, + and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object networkAttachmentDefinitions: description: NetworkAttachmentDefinitions list of network attachment definitions the service pod gets attached to @@ -119,6 +136,23 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object notifierImage: type: string override: @@ -312,6 +346,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Aodh + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -475,6 +510,23 @@ spec: description: SecretName - holding the cert, key for the service type: string type: object + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object mysqldExporterDatabaseAccountPrefix: default: mysqld-exporter description: |- @@ -515,6 +567,23 @@ spec: type: object notificationImage: type: string + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: ceilometerService: CeilometerPassword @@ -544,6 +613,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in Telemetry + Deprecated: Use MessagingBus.Cluster instead type: string secret: default: osp-secret @@ -1088,6 +1158,23 @@ spec: default: memcached description: Memcached instance name. type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object nodeSelector: additionalProperties: type: string @@ -1096,6 +1183,23 @@ spec: NodeSelector here acts as a default value and can be overridden by service specific NodeSelector Settings. type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, + and cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object passwordSelector: default: cloudKittyService: CloudKittyPassword @@ -1168,6 +1272,7 @@ spec: description: |- RabbitMQ instance name Needed to request a transportURL that is created and used in CloudKitty + Deprecated: Use MessagingBus.Cluster instead type: string s3StorageConfig: default: diff --git a/go.mod b/go.mod index 09cba609c..f02dd196d 100644 --- a/go.mod +++ b/go.mod @@ -10,20 +10,20 @@ require ( github.com/grafana/loki/operator/api/loki v0.0.0-20250910094332-a082b8a061ba github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7 github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251004062530-e48be5cc4d68 - github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e + github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20250929092825-4c2402451077 github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.0 - github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077 + github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20251002102126-84fdf59cb2fb github.com/openstack-k8s-operators/ovn-operator/api v0.6.1-0.20251002145853-52dcb63c343b github.com/openstack-k8s-operators/telemetry-operator/api v0.3.1-0.20240529090522-c780bd90b147 github.com/rabbitmq/cluster-operator v1.14.0 github.com/rhobs/obo-prometheus-operator/pkg/apis/monitoring v0.71.0-rhobs1 github.com/rhobs/observability-operator v0.3.1 - k8s.io/api v0.31.13 - k8s.io/apimachinery v0.31.13 - k8s.io/client-go v0.31.13 + k8s.io/api v0.31.14 + k8s.io/apimachinery v0.31.14 + k8s.io/client-go v0.31.14 k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d sigs.k8s.io/controller-runtime v0.19.7 ) @@ -87,11 +87,10 @@ require ( go.opentelemetry.io/otel/trace v1.34.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect - golang.org/x/mod v0.27.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.16.0 // indirect diff --git a/go.sum b/go.sum index 129ec51c9..b16c3c1da 100644 --- a/go.sum +++ b/go.sum @@ -105,24 +105,24 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWu github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.27.1 h1:0LJC8MpUSQnfnp4n/3W3GdlmJP3ENGF0ZPzjQGLPP7s= -github.com/onsi/ginkgo/v2 v2.27.1/go.mod h1:wmy3vCqiBjirARfVhAqFpYt8uvX0yaFe+GudAqqcCqA= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyUt0GEdoAE+r5TXy7YS21yNEo+2U= github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251004062530-e48be5cc4d68 h1:wiP9mtrOKc4jzj026ln1AWPc1RIDr7LDmqshLQRZbpE= github.com/openstack-k8s-operators/heat-operator/api v0.6.1-0.20251004062530-e48be5cc4d68/go.mod h1:jeO3FcGj38TheKGtsA64EWaHOM6GhAB0GN+2pc1w3hQ= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e h1:5q47hHT53v0PnNj2pwHHQ1+ZWC3kQLu1jtulTUrJ2cE= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251002120642-c2d58c6fc03e/go.mod h1:LfqzznghLpo+b9jVgyvqUoOZMcc3Ff0gXSmLLtFsj9w= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e h1:PIjcXzMMwfvBRFgFpaq/W9tqy0t2cYvcWX+kq6uNtTM= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251217131115-0f117a938d4e/go.mod h1:ex8ou6/3ms6ovR+CMXD6XhTlNakm1GhB6UZgagVRNW8= github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1 h1:QohvX44nxoV2GwvvOURGXYyDuCn4SCrnwubTKJtzehY= github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20251027074845-ed8154b20ad1/go.mod h1:FMFoO4MjEQ85JpdLtDHxYSZxvJ9KzHua+HdKhpl0KRI= github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20250929092825-4c2402451077 h1:wAonK5ng4dZdQPdBGnLRLQ0zYu5cQ0OmDO46iiN+Quw= github.com/openstack-k8s-operators/lib-common/modules/ansible v0.6.1-0.20250929092825-4c2402451077/go.mod h1:/t8UOevAIOdAu7SAkfwfyZj6p2pkuupl3mZJPMNqNOo= github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.0 h1:cFOyP37qQ9T1D6mVTCwuPGt86LB4sTErpHT+L1e+VKY= github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.0/go.mod h1:jgfvFeljXxot0LODLYCmjESxoMXbClXcBcf0DaX4zA0= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077 h1:missBxDwEfOdkHVKd6zyCyaQjSObw9Ge1O4A7WU5EuM= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20250929092825-4c2402451077/go.mod h1:CjsYQ/dUr4eUmBEvM3UFUxvYvl2bAhGfGflaD+N4fWA= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c h1:wM8qXCB5mQwSosCvtaydzuXitWVVKBHTzH0A2znQ+Jg= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c/go.mod h1:+Me0raWPPdz8gRi9D4z1khmvUgS9vIKAVC8ckg1yJZU= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb h1:wToXqX7AS1JV3Kna7RcJfkRart8rSGun2biKNfyY6Zg= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251021145236-2b84ec9fd9bb/go.mod h1:yf13jWb60XV26eA7A8o86ZCXNWBLNK9dPkTSWFaTPCw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20250929092825-4c2402451077 h1:9tpPDBV2RLXMDgt13ec8XR2OatFriItseqg+Oyvx9GA= @@ -195,14 +195,12 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/internal/autoscaling/const.go b/internal/autoscaling/const.go index 73a004924..9edfff7aa 100644 --- a/internal/autoscaling/const.go +++ b/internal/autoscaling/const.go @@ -16,6 +16,10 @@ limitations under the License. package autoscaling +import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" +) + const ( // ServiceName - ServiceName = "aodh" @@ -47,6 +51,18 @@ const ( // AodhUserID - AodhUserID = 42402 + + // AutoscalingNotificationBusReadyCondition Status=True condition indicates if the RabbitMQ NotificationsBus TransportURL is configured + AutoscalingNotificationBusReadyCondition condition.Type = "AutoscalingNotificationBusReady" + + // AutoscalingNotificationBusReadyMessage + AutoscalingNotificationBusReadyMessage = "NotificationsBus TransportURL successfully created" + + // AutoscalingNotificationBusReadyRunningMessage + AutoscalingNotificationBusReadyRunningMessage = "NotificationsBus TransportURL creation in progress" + + // AutoscalingNotificationBusReadyErrorMessage + AutoscalingNotificationBusReadyErrorMessage = "NotificationsBus TransportURL error occured %s" ) // PrometheusReplicas - diff --git a/internal/ceilometer/const.go b/internal/ceilometer/const.go index 68c3d8cf5..598b64ce5 100644 --- a/internal/ceilometer/const.go +++ b/internal/ceilometer/const.go @@ -16,6 +16,10 @@ limitations under the License. // Package ceilometer provides functionality for managing OpenStack Ceilometer telemetry components package ceilometer +import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" +) + const ( // ServiceName - ServiceName = "ceilometer" @@ -37,4 +41,16 @@ const ( // CeilometerUserID - CeilometerUserID = 42405 + + // CeilometerNotificationBusReadyCondition Status=True condition indicates if the RabbitMQ NotificationsBus TransportURL is configured + CeilometerNotificationBusReadyCondition condition.Type = "CeilometerNotificationBusReady" + + // CeilometerNotificationBusReadyMessage + CeilometerNotificationBusReadyMessage = "NotificationsBus TransportURL successfully created" + + // CeilometerNotificationBusReadyRunningMessage + CeilometerNotificationBusReadyRunningMessage = "NotificationsBus TransportURL creation in progress" + + // CeilometerNotificationBusReadyErrorMessage + CeilometerNotificationBusReadyErrorMessage = "NotificationsBus TransportURL error occured %s" ) diff --git a/internal/cloudkitty/const.go b/internal/cloudkitty/const.go index 7a862dd11..ac19434bc 100644 --- a/internal/cloudkitty/const.go +++ b/internal/cloudkitty/const.go @@ -18,6 +18,7 @@ package cloudkitty import ( "time" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" ctrl "sigs.k8s.io/controller-runtime" ) @@ -65,6 +66,18 @@ const ( // CloudKittyUserID - CloudKittyUserID = 42406 + + // CloudKittyNotificationBusReadyCondition Status=True condition indicates if the RabbitMQ NotificationsBus TransportURL is configured + CloudKittyNotificationBusReadyCondition condition.Type = "CloudKittyNotificationBusReady" + + // CloudKittyNotificationBusReadyMessage + CloudKittyNotificationBusReadyMessage = "NotificationsBus TransportURL successfully created" + + // CloudKittyNotificationBusReadyRunningMessage + CloudKittyNotificationBusReadyRunningMessage = "NotificationsBus TransportURL creation in progress" + + // CloudKittyNotificationBusReadyErrorMessage + CloudKittyNotificationBusReadyErrorMessage = "NotificationsBus TransportURL error occured %s" ) // ResultRequeue is a ctrl.Result that requeues after NormalDuration diff --git a/internal/controller/autoscaling_controller.go b/internal/controller/autoscaling_controller.go index c92190ca9..d5cbbd5f5 100644 --- a/internal/controller/autoscaling_controller.go +++ b/internal/controller/autoscaling_controller.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -323,7 +324,7 @@ func (r *AutoscalingReconciler) reconcileNormal( // // create RabbitMQ transportURL CR and get the actual URL from the associated secret that is created // - transportURL, op, err := r.transportURLCreateOrUpdate(instance) + transportURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, nil) if err != nil { Log.Info("Error getting transportURL. Setting error condition on status and returning") instance.Status.Conditions.Set(condition.FalseCondition( @@ -354,6 +355,55 @@ func (r *AutoscalingReconciler) reconcileNormal( instance.Status.Conditions.MarkTrue(condition.RabbitMqTransportURLReadyCondition, condition.RabbitMqTransportURLReadyMessage) // end transportURL + // + // create NotificationsBus TransportURL if configured + // + if instance.Spec.Aodh.NotificationsBus != nil { + // init .Status.NotificationsURLSecret + instance.Status.NotificationsURLSecret = ptr.To("") + + // setting notificationBusConfig to nil ensures that we do not + // request a new transportURL unless the two spec fields do not match + var notificationBusConfig *rabbitmqv1.RabbitMqConfig + if instance.Spec.Aodh.NotificationsBus.Cluster != instance.Spec.Aodh.MessagingBus.Cluster { + notificationBusConfig = instance.Spec.Aodh.NotificationsBus + } + + notificationBusInstanceURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, notificationBusConfig) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + autoscaling.AutoscalingNotificationBusReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + autoscaling.AutoscalingNotificationBusReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + if op != controllerutil.OperationResultNone { + Log.Info(fmt.Sprintf("NotificationBusInstanceURL %s successfully reconciled - operation: %s", notificationBusInstanceURL.Name, string(op))) + } + + *instance.Status.NotificationsURLSecret = notificationBusInstanceURL.Status.SecretName + + if *instance.Status.NotificationsURLSecret == "" { + Log.Info(fmt.Sprintf("Waiting for NotificationBusInstanceURL %s secret to be created", notificationBusInstanceURL.Name)) + instance.Status.Conditions.Set(condition.FalseCondition( + autoscaling.AutoscalingNotificationBusReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + autoscaling.AutoscalingNotificationBusReadyRunningMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + + instance.Status.Conditions.MarkTrue(autoscaling.AutoscalingNotificationBusReadyCondition, autoscaling.AutoscalingNotificationBusReadyMessage) + } else { + // make sure we do not have an entry in the status if + // .Spec.Aodh.NotificationsBus is not provided + instance.Status.NotificationsURLSecret = nil + } + // end notificationsBus + configMapVars := make(map[string]env.Setter) // @@ -759,6 +809,21 @@ func (r *AutoscalingReconciler) generateServiceConfig( // Quorum Queues templateParameters["QuorumQueues"] = string(transportURLSecret.Data["quorumqueues"]) == "true" + // Add NotificationsURL if configured + if instance.Status.NotificationsURLSecret != nil { + // Get separate secret only if different cluster, otherwise reuse main transport_url + if instance.Spec.Aodh.NotificationsBus.Cluster != instance.Spec.Aodh.MessagingBus.Cluster { + notificationInstanceURLSecret, _, err := secret.GetSecret(ctx, h, *instance.Status.NotificationsURLSecret, instance.Namespace) + if err != nil { + return err + } + templateParameters["NotificationsURL"] = string(notificationInstanceURLSecret.Data["transport_url"]) + } else { + // Same cluster - reuse main transport_url + templateParameters["NotificationsURL"] = string(transportURLSecret.Data["transport_url"]) + } + } + cms := []util.Template{ // ScriptsSecret { @@ -825,18 +890,51 @@ func (r *AutoscalingReconciler) getSecret(ctx context.Context, h *helper.Helper, return ctrl.Result{}, nil } -func (r *AutoscalingReconciler) transportURLCreateOrUpdate(instance *telemetryv1.Autoscaling) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) { +func (r *AutoscalingReconciler) transportURLCreateOrUpdate( + ctx context.Context, + instance *telemetryv1.Autoscaling, + serviceLabels map[string]string, + rabbitmqConfig *rabbitmqv1.RabbitMqConfig, +) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) { + // Default values for regular messagingBus transportURL + rmqName := fmt.Sprintf("%s-transport", autoscaling.ServiceName) + config := &instance.Spec.Aodh.MessagingBus + + // When rabbitmqConfig is passed (notificationsBus use case) + // update the default rmqName and use the provided config + if rabbitmqConfig != nil { + rmqName = fmt.Sprintf("%s-notification-%s", autoscaling.ServiceName, rabbitmqConfig.Cluster) + config = rabbitmqConfig + } + + // Prepare the spec values before CreateOrUpdate so webhooks see them during CREATE + clusterName := config.Cluster + username := config.User + vhost := config.Vhost + transportURL := &rabbitmqv1.TransportURL{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-transport", autoscaling.ServiceName), + Name: rmqName, Namespace: instance.Namespace, + Labels: serviceLabels, + }, + Spec: rabbitmqv1.TransportURLSpec{ + RabbitmqClusterName: clusterName, + Username: username, + Vhost: vhost, }, } - op, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, transportURL, func() error { - transportURL.Spec.RabbitmqClusterName = instance.Spec.Aodh.RabbitMqClusterName - err := controllerutil.SetControllerReference(instance, transportURL, r.Scheme) - return err + + op, err := controllerutil.CreateOrUpdate(ctx, r.Client, transportURL, func() error { + transportURL.Spec.RabbitmqClusterName = clusterName + if username != "" { + transportURL.Spec.Username = username + } + // Always set Vhost - empty string means use default "/" vhost + transportURL.Spec.Vhost = vhost + return controllerutil.SetControllerReference(instance, transportURL, r.Scheme) }) + return transportURL, op, err } diff --git a/internal/controller/ceilometer_controller.go b/internal/controller/ceilometer_controller.go index 863829374..421695684 100644 --- a/internal/controller/ceilometer_controller.go +++ b/internal/controller/ceilometer_controller.go @@ -34,6 +34,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -526,10 +527,15 @@ func (r *CeilometerReconciler) reconcileCeilometer( Log := r.GetLogger(ctx) Log.Info(fmt.Sprintf(msgReconcileStart, ceilometer.ServiceName)) + serviceLabels := map[string]string{ + common.AppSelector: ceilometer.ServiceName, + common.OwnerSelector: instance.Name, + } + // // create RabbitMQ transportURL CR and get the actual URL from the associated secret that is created // - transportURL, op, err := r.transportURLCreateOrUpdate(instance) + transportURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, nil) if err != nil { Log.Info("Error getting transportURL. Setting error condition on status and returning") instance.Status.Conditions.Set(condition.FalseCondition( @@ -560,6 +566,55 @@ func (r *CeilometerReconciler) reconcileCeilometer( instance.Status.Conditions.MarkTrue(condition.RabbitMqTransportURLReadyCondition, condition.RabbitMqTransportURLReadyMessage) // end transportURL + // + // create NotificationsBus TransportURL if configured + // + if instance.Spec.NotificationsBus != nil { + // init .Status.NotificationsURLSecret + instance.Status.NotificationsURLSecret = ptr.To("") + + // setting notificationBusConfig to nil ensures that we do not + // request a new transportURL unless the two spec fields do not match + var notificationBusConfig *rabbitmqv1.RabbitMqConfig + if instance.Spec.NotificationsBus.Cluster != instance.Spec.MessagingBus.Cluster { + notificationBusConfig = instance.Spec.NotificationsBus + } + + notificationBusInstanceURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, notificationBusConfig) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + ceilometer.CeilometerNotificationBusReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + ceilometer.CeilometerNotificationBusReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + if op != controllerutil.OperationResultNone { + Log.Info(fmt.Sprintf("NotificationBusInstanceURL %s successfully reconciled - operation: %s", notificationBusInstanceURL.Name, string(op))) + } + + *instance.Status.NotificationsURLSecret = notificationBusInstanceURL.Status.SecretName + + if *instance.Status.NotificationsURLSecret == "" { + Log.Info(fmt.Sprintf("Waiting for NotificationBusInstanceURL %s secret to be created", notificationBusInstanceURL.Name)) + instance.Status.Conditions.Set(condition.FalseCondition( + ceilometer.CeilometerNotificationBusReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + ceilometer.CeilometerNotificationBusReadyRunningMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + + instance.Status.Conditions.MarkTrue(ceilometer.CeilometerNotificationBusReadyCondition, ceilometer.CeilometerNotificationBusReadyMessage) + } else { + // make sure we do not have an entry in the status if + // .Spec.NotificationsBus is not provided + instance.Status.NotificationsURLSecret = nil + } + // end notificationsBus + // // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map // @@ -725,11 +780,6 @@ func (r *CeilometerReconciler) reconcileCeilometer( instance.Status.Conditions.MarkTrue(condition.ServiceConfigReadyCondition, condition.ServiceConfigReadyMessage) - serviceLabels := map[string]string{ - common.AppSelector: ceilometer.ServiceName, - common.OwnerSelector: instance.Name, - } - topology, err := ensureTopology( ctx, helper, @@ -1282,6 +1332,21 @@ func (r *CeilometerReconciler) generateServiceConfig( templateParameters["SwiftRole"] = true } + // Add NotificationsURL if configured + if instance.Status.NotificationsURLSecret != nil { + // Get separate secret only if different cluster, otherwise reuse main transport_url + if instance.Spec.NotificationsBus.Cluster != instance.Spec.MessagingBus.Cluster { + notificationInstanceURLSecret, _, err := secret.GetSecret(ctx, h, *instance.Status.NotificationsURLSecret, instance.Namespace) + if err != nil { + return err + } + templateParameters["NotificationsURL"] = string(notificationInstanceURLSecret.Data["transport_url"]) + } else { + // Same cluster - reuse main transport_url + templateParameters["NotificationsURL"] = string(transportURLSecret.Data["transport_url"]) + } + } + cms := []util.Template{ // ScriptsSecrets { @@ -1621,18 +1686,51 @@ func (r *CeilometerReconciler) createHashOfInputHashes( return hash, changed, nil } -func (r *CeilometerReconciler) transportURLCreateOrUpdate(instance *telemetryv1.Ceilometer) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) { +func (r *CeilometerReconciler) transportURLCreateOrUpdate( + ctx context.Context, + instance *telemetryv1.Ceilometer, + serviceLabels map[string]string, + rabbitmqConfig *rabbitmqv1.RabbitMqConfig, +) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) { + // Default values for regular messagingBus transportURL + rmqName := fmt.Sprintf("%s-transport", ceilometer.ServiceName) + config := &instance.Spec.MessagingBus + + // When rabbitmqConfig is passed (notificationsBus use case) + // update the default rmqName and use the provided config + if rabbitmqConfig != nil { + rmqName = fmt.Sprintf("%s-notification-%s", ceilometer.ServiceName, rabbitmqConfig.Cluster) + config = rabbitmqConfig + } + + // Prepare the spec values before CreateOrUpdate so webhooks see them during CREATE + clusterName := config.Cluster + username := config.User + vhost := config.Vhost + transportURL := &rabbitmqv1.TransportURL{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-transport", ceilometer.ServiceName), + Name: rmqName, Namespace: instance.Namespace, + Labels: serviceLabels, + }, + Spec: rabbitmqv1.TransportURLSpec{ + RabbitmqClusterName: clusterName, + Username: username, + Vhost: vhost, }, } - op, err := controllerutil.CreateOrUpdate(context.TODO(), r.Client, transportURL, func() error { - transportURL.Spec.RabbitmqClusterName = instance.Spec.RabbitMqClusterName - err := controllerutil.SetControllerReference(instance, transportURL, r.Scheme) - return err + + op, err := controllerutil.CreateOrUpdate(ctx, r.Client, transportURL, func() error { + transportURL.Spec.RabbitmqClusterName = clusterName + if username != "" { + transportURL.Spec.Username = username + } + // Always set Vhost - empty string means use default "/" vhost + transportURL.Spec.Vhost = vhost + return controllerutil.SetControllerReference(instance, transportURL, r.Scheme) }) + return transportURL, op, err } diff --git a/internal/controller/cloudkitty_controller.go b/internal/controller/cloudkitty_controller.go index 80869014e..0e76ce13e 100644 --- a/internal/controller/cloudkitty_controller.go +++ b/internal/controller/cloudkitty_controller.go @@ -33,6 +33,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -796,7 +797,7 @@ func (r *CloudKittyReconciler) reconcileNormal(ctx context.Context, instance *te // create RabbitMQ transportURL CR and get the actual URL from the associated secret that is created // - transportURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels) + transportURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, nil) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.RabbitMqTransportURLReadyCondition, @@ -827,6 +828,55 @@ func (r *CloudKittyReconciler) reconcileNormal(ctx context.Context, instance *te // end transportURL + // + // create NotificationsBus TransportURL if configured + // + if instance.Spec.NotificationsBus != nil { + // init .Status.NotificationsURLSecret + instance.Status.NotificationsURLSecret = ptr.To("") + + // setting notificationBusConfig to nil ensures that we do not + // request a new transportURL unless the two spec fields do not match + var notificationBusConfig *rabbitmqv1.RabbitMqConfig + if instance.Spec.NotificationsBus.Cluster != instance.Spec.MessagingBus.Cluster { + notificationBusConfig = instance.Spec.NotificationsBus + } + + notificationBusInstanceURL, op, err := r.transportURLCreateOrUpdate(ctx, instance, serviceLabels, notificationBusConfig) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + cloudkitty.CloudKittyNotificationBusReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + cloudkitty.CloudKittyNotificationBusReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + + if op != controllerutil.OperationResultNone { + Log.Info(fmt.Sprintf("NotificationBusInstanceURL %s successfully reconciled - operation: %s", notificationBusInstanceURL.Name, string(op))) + } + + *instance.Status.NotificationsURLSecret = notificationBusInstanceURL.Status.SecretName + + if *instance.Status.NotificationsURLSecret == "" { + Log.Info(fmt.Sprintf("Waiting for NotificationBusInstanceURL %s secret to be created", notificationBusInstanceURL.Name)) + instance.Status.Conditions.Set(condition.FalseCondition( + cloudkitty.CloudKittyNotificationBusReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + cloudkitty.CloudKittyNotificationBusReadyRunningMessage)) + return cloudkitty.ResultRequeue, nil + } + + instance.Status.Conditions.MarkTrue(cloudkitty.CloudKittyNotificationBusReadyCondition, cloudkitty.CloudKittyNotificationBusReadyMessage) + } else { + // make sure we do not have an entry in the status if + // .Spec.NotificationsBus is not provided + instance.Status.NotificationsURLSecret = nil + } + // end notificationsBus + // // Check for required memcached used for caching // @@ -1224,6 +1274,21 @@ func (r *CloudKittyReconciler) generateServiceConfigs( templateParameters["PrometheusCAFile"] = tls.DownstreamTLSCABundlePath } + // Add NotificationsURL if configured + if instance.Status.NotificationsURLSecret != nil { + // Get separate secret only if different cluster, otherwise reuse main transport_url + if instance.Spec.NotificationsBus.Cluster != instance.Spec.MessagingBus.Cluster { + notificationInstanceURLSecret, _, err := secret.GetSecret(ctx, h, *instance.Status.NotificationsURLSecret, instance.Namespace) + if err != nil { + return err + } + templateParameters["NotificationsURL"] = string(notificationInstanceURLSecret.Data["transport_url"]) + } else { + // Same cluster - reuse main transport_url + templateParameters["NotificationsURL"] = string(transportURLSecret.Data["transport_url"]) + } + } + // create httpd vhost template parameters httpdVhostConfig := map[string]interface{}{} for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} { @@ -1290,20 +1355,45 @@ func (r *CloudKittyReconciler) transportURLCreateOrUpdate( ctx context.Context, instance *telemetryv1.CloudKitty, serviceLabels map[string]string, + rabbitmqConfig *rabbitmqv1.RabbitMqConfig, ) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) { + // Default values for regular messagingBus transportURL + rmqName := fmt.Sprintf("%s-transport", instance.Name) + config := &instance.Spec.MessagingBus + + // When rabbitmqConfig is passed (notificationsBus use case) + // update the default rmqName and use the provided config + if rabbitmqConfig != nil { + rmqName = fmt.Sprintf("%s-notification-%s", instance.Name, rabbitmqConfig.Cluster) + config = rabbitmqConfig + } + + // Prepare the spec values before CreateOrUpdate so webhooks see them during CREATE + clusterName := config.Cluster + username := config.User + vhost := config.Vhost + transportURL := &rabbitmqv1.TransportURL{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-transport", instance.Name), + Name: rmqName, Namespace: instance.Namespace, Labels: serviceLabels, }, + Spec: rabbitmqv1.TransportURLSpec{ + RabbitmqClusterName: clusterName, + Username: username, + Vhost: vhost, + }, } op, err := controllerutil.CreateOrUpdate(ctx, r.Client, transportURL, func() error { - transportURL.Spec.RabbitmqClusterName = instance.Spec.RabbitMqClusterName - - err := controllerutil.SetControllerReference(instance, transportURL, r.Scheme) - return err + transportURL.Spec.RabbitmqClusterName = clusterName + if username != "" { + transportURL.Spec.Username = username + } + // Always set Vhost - empty string means use default "/" vhost + transportURL.Spec.Vhost = vhost + return controllerutil.SetControllerReference(instance, transportURL, r.Scheme) }) return transportURL, op, err diff --git a/templates/autoscaling/config/aodh.conf b/templates/autoscaling/config/aodh.conf index e53c7ac15..a9f93b9a4 100644 --- a/templates/autoscaling/config/aodh.conf +++ b/templates/autoscaling/config/aodh.conf @@ -25,8 +25,12 @@ enable_proxy_headers_parsing=True policy_file=/etc/aodh/policy.yaml [oslo_messaging_notifications] -driver=noop -transport_url = {{ .TransportURL }} +driver=messagingv2 +{{ if (index . "NotificationsURL") -}} +transport_url={{ .NotificationsURL }} +{{- else -}} +transport_url={{ .TransportURL }} +{{- end }} {{ if (index . "QuorumQueues") }} [oslo_messaging_rabbit] rabbit_quorum_queue=true diff --git a/templates/ceilometercentral/config/ceilometer.conf b/templates/ceilometercentral/config/ceilometer.conf index 05e1388c1..35cbfb117 100644 --- a/templates/ceilometercentral/config/ceilometer.conf +++ b/templates/ceilometercentral/config/ceilometer.conf @@ -39,7 +39,11 @@ notify_address_prefix= [oslo_messaging_notifications] driver=messagingv2 topics=notifications +{{ if (index . "NotificationsURL") -}} +transport_url={{ .NotificationsURL }} +{{- else -}} transport_url={{ .TransportURL }} +{{- end }} {{ if (index . "QuorumQueues") }} [oslo_messaging_rabbit] rabbit_quorum_queue=true diff --git a/templates/cloudkitty/config/cloudkitty.conf b/templates/cloudkitty/config/cloudkitty.conf index 80ba122e1..1ac8eacfe 100644 --- a/templates/cloudkitty/config/cloudkitty.conf +++ b/templates/cloudkitty/config/cloudkitty.conf @@ -7,6 +7,14 @@ debug = True notification_topics = notifications transport_url = {{ .TransportURL }} +[oslo_messaging_notifications] +driver = messagingv2 +{{ if (index . "NotificationsURL") -}} +transport_url = {{ .NotificationsURL }} +{{- else -}} +transport_url = {{ .TransportURL }} +{{- end }} + [authinfos] debug = True project_domain_name = default