From e48f1a7f90ae2ea26fbc7e426afb094b9f5a378f Mon Sep 17 00:00:00 2001 From: Luca Miccini Date: Fri, 6 Feb 2026 22:22:38 +0100 Subject: [PATCH 1/2] Propagate rabbitmq user to nova-cellX secret --- internal/controller/novacell_controller.go | 9 ++++++++- test/functional/novacell_controller_test.go | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/internal/controller/novacell_controller.go b/internal/controller/novacell_controller.go index 4c1f68d25..029ed0438 100644 --- a/internal/controller/novacell_controller.go +++ b/internal/controller/novacell_controller.go @@ -806,9 +806,16 @@ func (r *NovaCellReconciler) generateComputeConfigs( hashes := make(map[string]env.Setter) + // Propagate RabbitMQ user names to compute-config secret so the + // openstack-operator can track which RabbitMQUser CRs are in use + extraData := map[string]string{ + RabbitmqUserNameSelector: string(secret.Data[RabbitmqUserNameSelector]), + NotificationRabbitmqUserNameSelector: string(secret.Data[NotificationRabbitmqUserNameSelector]), + } + configName := instance.GetName() + "-compute-config" err := r.GenerateConfigs( - ctx, h, instance, configName, &hashes, templateParameters, map[string]string{}, cmLabels, map[string]string{}, + ctx, h, instance, configName, &hashes, templateParameters, extraData, cmLabels, map[string]string{}, ) if err != nil { return err diff --git a/test/functional/novacell_controller_test.go b/test/functional/novacell_controller_test.go index a5c3cc3dd..cd7141896 100644 --- a/test/functional/novacell_controller_test.go +++ b/test/functional/novacell_controller_test.go @@ -339,6 +339,13 @@ var _ = Describe("NovaCell controller", func() { ) Expect(GetNovaCell(cell1.CellCRName).Status.Hash).To(HaveKey(cell1.ComputeConfigSecretName.Name)) + + // Verify that RabbitMQ user names are propagated to the compute-config secret + // so the openstack-operator can track which RabbitMQUser CRs are in use + Expect(computeConfigData.Data).To( + HaveKey(controllers.RabbitmqUserNameSelector)) + Expect(computeConfigData.Data).To( + HaveKey(controllers.NotificationRabbitmqUserNameSelector)) }) It("should have quorum queues disabled by default and enabled when configured", func() { From 338e6295818c37d1e694db27ed1b9f315b5299d9 Mon Sep 17 00:00:00 2001 From: Luca Miccini Date: Tue, 10 Feb 2026 10:00:05 +0100 Subject: [PATCH 2/2] Optimize tests to reduce cpu/mem usage --- test/functional/base_test.go | 13 ++-- test/functional/nova_controller_test.go | 20 +++--- test/functional/nova_multicell_test.go | 6 +- test/functional/nova_scheduler_test.go | 83 +++++++++++----------- test/functional/novaapi_controller_test.go | 83 +++++++++++----------- 5 files changed, 107 insertions(+), 98 deletions(-) diff --git a/test/functional/base_test.go b/test/functional/base_test.go index a1e9870da..84c0871ca 100644 --- a/test/functional/base_test.go +++ b/test/functional/base_test.go @@ -1063,12 +1063,15 @@ func CreateNovaWithNCellsAndEnsureReady(cellNumber int, novaNames *NovaNames) { DeferCleanup(keystone.DeleteKeystoneAPI, novaNames.KeystoneAPIName) // Set region on KeystoneAPI to ensure GetRegion() returns a value + keystoneAPI := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) + keystoneAPI.Spec.Region = "regionOne" + Expect(k8sClient.Update(ctx, keystoneAPI)).To(Succeed()) + + // Update status separately after spec is applied Eventually(func(g Gomega) { - keystoneAPI := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) - keystoneAPI.Spec.Region = "regionOne" - g.Expect(k8sClient.Update(ctx, keystoneAPI)).To(Succeed()) - keystoneAPI.Status.Region = "regionOne" - g.Expect(k8sClient.Status().Update(ctx, keystoneAPI)).To(Succeed()) + ks := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) + ks.Status.Region = "regionOne" + g.Expect(k8sClient.Status().Update(ctx, ks)).To(Succeed()) }, timeout, interval).Should(Succeed()) memcachedSpec := infra.GetDefaultMemcachedSpec() diff --git a/test/functional/nova_controller_test.go b/test/functional/nova_controller_test.go index 44b9c7153..37f83656b 100644 --- a/test/functional/nova_controller_test.go +++ b/test/functional/nova_controller_test.go @@ -1516,10 +1516,8 @@ var _ = Describe("Nova controller", func() { infra.SimulateMemcachedReady(novaNames.MemcachedNamespace) keystoneAPIName := keystone.CreateKeystoneAPI(novaNames.NovaName.Namespace) DeferCleanup(keystone.DeleteKeystoneAPI, keystoneAPIName) - keystoneAPI := keystone.GetKeystoneAPI(keystoneAPIName) - Eventually(func(g Gomega) { - g.Expect(k8sClient.Status().Update(ctx, keystoneAPI.DeepCopy())).Should(Succeed()) - }, timeout, interval).Should(Succeed()) + // Note: Status update removed as it was updating with stale object and not setting any fields. + // The SimulateKeystoneServiceReady call below handles necessary setup. keystone.SimulateKeystoneServiceReady(novaNames.KeystoneServiceName) }) It("propagates topology to the Nova components except cell1 that has an override", func() { @@ -1940,12 +1938,16 @@ var _ = Describe("Nova controller - region defaults", func() { }) It("defaults the region to regionOne", func() { + // Clear region on KeystoneAPI spec first + keystoneAPI := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) + keystoneAPI.Spec.Region = "" + Expect(k8sClient.Update(ctx, keystoneAPI)).To(Succeed()) + + // Update status separately after spec is applied Eventually(func(g Gomega) { - keystoneAPI := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) - keystoneAPI.Spec.Region = "" - g.Expect(k8sClient.Update(ctx, keystoneAPI)).To(Succeed()) - keystoneAPI.Status.Region = "" - g.Expect(k8sClient.Status().Update(ctx, keystoneAPI)).To(Succeed()) + ks := keystone.GetKeystoneAPI(novaNames.KeystoneAPIName) + ks.Status.Region = "" + g.Expect(k8sClient.Status().Update(ctx, ks)).To(Succeed()) }, timeout, interval).Should(Succeed()) Eventually(func(g Gomega) { diff --git a/test/functional/nova_multicell_test.go b/test/functional/nova_multicell_test.go index 7596204d0..53bc5ec5e 100644 --- a/test/functional/nova_multicell_test.go +++ b/test/functional/nova_multicell_test.go @@ -889,10 +889,8 @@ var _ = Describe("Nova multi cell", func() { infra.SimulateMemcachedReady(novaNames.MemcachedNamespace) keystoneAPIName := keystone.CreateKeystoneAPI(novaNames.NovaName.Namespace) DeferCleanup(keystone.DeleteKeystoneAPI, keystoneAPIName) - keystoneAPI := keystone.GetKeystoneAPI(keystoneAPIName) - Eventually(func(g Gomega) { - g.Expect(k8sClient.Status().Update(ctx, keystoneAPI.DeepCopy())).Should(Succeed()) - }, timeout, interval).Should(Succeed()) + // Note: Status update removed as it was updating with stale object and not setting any fields. + // The SimulateKeystoneServiceReady call below handles necessary setup. keystone.SimulateKeystoneServiceReady(novaNames.KeystoneServiceName) }) It("cell0 becomes ready without metadata and the rest of nova is deployed", func() { diff --git a/test/functional/nova_scheduler_test.go b/test/functional/nova_scheduler_test.go index abd51c43d..ab81750e6 100644 --- a/test/functional/nova_scheduler_test.go +++ b/test/functional/nova_scheduler_test.go @@ -285,51 +285,54 @@ var _ = Describe("NovaScheduler controller", func() { ) // Wait for config to be regenerated with region + var configData string Eventually(func(g Gomega) { configDataMap := th.GetSecret(novaNames.SchedulerConfigDataName) g.Expect(configDataMap).ShouldNot(BeNil()) g.Expect(configDataMap.Data).Should(HaveKey("01-nova.conf")) - configData := string(configDataMap.Data["01-nova.conf"]) - - // Parse the INI file to properly access sections - cfg, err := ini.Load([]byte(configData)) - g.Expect(err).ShouldNot(HaveOccurred(), "Should be able to parse config as INI") - - // Verify region_name in [keystone_authtoken] - section := cfg.Section("keystone_authtoken") - g.Expect(section).ShouldNot(BeNil(), "Should find [keystone_authtoken] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [placement] - section = cfg.Section("placement") - g.Expect(section).ShouldNot(BeNil(), "Should find [placement] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [glance] - section = cfg.Section("glance") - g.Expect(section).ShouldNot(BeNil(), "Should find [glance] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [neutron] - section = cfg.Section("neutron") - g.Expect(section).ShouldNot(BeNil(), "Should find [neutron] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify os_region_name in [cinder] - section = cfg.Section("cinder") - g.Expect(section).ShouldNot(BeNil(), "Should find [cinder] section") - g.Expect(section.Key("os_region_name").String()).Should(Equal(testRegion)) - - // Verify barbican_region_name in [barbican] - section = cfg.Section("barbican") - g.Expect(section).ShouldNot(BeNil(), "Should find [barbican] section") - g.Expect(section.Key("barbican_region_name").String()).Should(Equal(testRegion)) - - // Verify endpoint_region_name in [oslo_limit] - section = cfg.Section("oslo_limit") - g.Expect(section).ShouldNot(BeNil(), "Should find [oslo_limit] section") - g.Expect(section.Key("endpoint_region_name").String()).Should(Equal(testRegion)) + configData = string(configDataMap.Data["01-nova.conf"]) + // Quick check that config contains the expected region before parsing + g.Expect(configData).To(ContainSubstring(testRegion)) }, timeout, interval).Should(Succeed()) + + // Parse the INI file once after Eventually succeeds (avoids repeated parsing) + cfg, err := ini.Load([]byte(configData)) + Expect(err).ShouldNot(HaveOccurred(), "Should be able to parse config as INI") + + // Verify region_name in [keystone_authtoken] + section := cfg.Section("keystone_authtoken") + Expect(section).ShouldNot(BeNil(), "Should find [keystone_authtoken] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [placement] + section = cfg.Section("placement") + Expect(section).ShouldNot(BeNil(), "Should find [placement] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [glance] + section = cfg.Section("glance") + Expect(section).ShouldNot(BeNil(), "Should find [glance] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [neutron] + section = cfg.Section("neutron") + Expect(section).ShouldNot(BeNil(), "Should find [neutron] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify os_region_name in [cinder] + section = cfg.Section("cinder") + Expect(section).ShouldNot(BeNil(), "Should find [cinder] section") + Expect(section.Key("os_region_name").String()).Should(Equal(testRegion)) + + // Verify barbican_region_name in [barbican] + section = cfg.Section("barbican") + Expect(section).ShouldNot(BeNil(), "Should find [barbican] section") + Expect(section.Key("barbican_region_name").String()).Should(Equal(testRegion)) + + // Verify endpoint_region_name in [oslo_limit] + section = cfg.Section("oslo_limit") + Expect(section).ShouldNot(BeNil(), "Should find [oslo_limit] section") + Expect(section.Key("endpoint_region_name").String()).Should(Equal(testRegion)) }) It("stored the input hash in the Status", func() { diff --git a/test/functional/novaapi_controller_test.go b/test/functional/novaapi_controller_test.go index e5c5d9623..08dda27cd 100644 --- a/test/functional/novaapi_controller_test.go +++ b/test/functional/novaapi_controller_test.go @@ -307,51 +307,54 @@ endpoint_service_type = compute`)) ) // Wait for config to be regenerated with region + var configData string Eventually(func(g Gomega) { configDataMap := th.GetSecret(novaNames.APIConfigDataName) g.Expect(configDataMap).ShouldNot(BeNil()) g.Expect(configDataMap.Data).Should(HaveKey("01-nova.conf")) - configData := string(configDataMap.Data["01-nova.conf"]) - - // Parse the INI file to properly access sections - cfg, err := ini.Load([]byte(configData)) - g.Expect(err).ShouldNot(HaveOccurred(), "Should be able to parse config as INI") - - // Verify region_name in [keystone_authtoken] - section := cfg.Section("keystone_authtoken") - g.Expect(section).ShouldNot(BeNil(), "Should find [keystone_authtoken] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [placement] - section = cfg.Section("placement") - g.Expect(section).ShouldNot(BeNil(), "Should find [placement] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [glance] - section = cfg.Section("glance") - g.Expect(section).ShouldNot(BeNil(), "Should find [glance] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify region_name in [neutron] - section = cfg.Section("neutron") - g.Expect(section).ShouldNot(BeNil(), "Should find [neutron] section") - g.Expect(section.Key("region_name").String()).Should(Equal(testRegion)) - - // Verify os_region_name in [cinder] - section = cfg.Section("cinder") - g.Expect(section).ShouldNot(BeNil(), "Should find [cinder] section") - g.Expect(section.Key("os_region_name").String()).Should(Equal(testRegion)) - - // Verify barbican_region_name in [barbican] - section = cfg.Section("barbican") - g.Expect(section).ShouldNot(BeNil(), "Should find [barbican] section") - g.Expect(section.Key("barbican_region_name").String()).Should(Equal(testRegion)) - - // Verify endpoint_region_name in [oslo_limit] - section = cfg.Section("oslo_limit") - g.Expect(section).ShouldNot(BeNil(), "Should find [oslo_limit] section") - g.Expect(section.Key("endpoint_region_name").String()).Should(Equal(testRegion)) + configData = string(configDataMap.Data["01-nova.conf"]) + // Quick check that config contains the expected region before parsing + g.Expect(configData).To(ContainSubstring(testRegion)) }, timeout, interval).Should(Succeed()) + + // Parse the INI file once after Eventually succeeds (avoids repeated parsing) + cfg, err := ini.Load([]byte(configData)) + Expect(err).ShouldNot(HaveOccurred(), "Should be able to parse config as INI") + + // Verify region_name in [keystone_authtoken] + section := cfg.Section("keystone_authtoken") + Expect(section).ShouldNot(BeNil(), "Should find [keystone_authtoken] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [placement] + section = cfg.Section("placement") + Expect(section).ShouldNot(BeNil(), "Should find [placement] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [glance] + section = cfg.Section("glance") + Expect(section).ShouldNot(BeNil(), "Should find [glance] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify region_name in [neutron] + section = cfg.Section("neutron") + Expect(section).ShouldNot(BeNil(), "Should find [neutron] section") + Expect(section.Key("region_name").String()).Should(Equal(testRegion)) + + // Verify os_region_name in [cinder] + section = cfg.Section("cinder") + Expect(section).ShouldNot(BeNil(), "Should find [cinder] section") + Expect(section.Key("os_region_name").String()).Should(Equal(testRegion)) + + // Verify barbican_region_name in [barbican] + section = cfg.Section("barbican") + Expect(section).ShouldNot(BeNil(), "Should find [barbican] section") + Expect(section.Key("barbican_region_name").String()).Should(Equal(testRegion)) + + // Verify endpoint_region_name in [oslo_limit] + section = cfg.Section("oslo_limit") + Expect(section).ShouldNot(BeNil(), "Should find [oslo_limit] section") + Expect(section.Key("endpoint_region_name").String()).Should(Equal(testRegion)) }) It("stored the input hash in the Status", func() {