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