diff --git a/api/go.mod b/api/go.mod index c302baa0..bb2fd221 100644 --- a/api/go.mod +++ b/api/go.mod @@ -8,7 +8,7 @@ require ( github.com/gophercloud/gophercloud/v2 v2.8.0 github.com/onsi/gomega v1.42.0 github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a - github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89 + github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260617073944-e6e62be1c4ec github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260610101308-d3778a549c89 github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260610101308-d3778a549c89 github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260610101308-d3778a549c89 diff --git a/api/go.sum b/api/go.sum index 3ba8c540..4c754325 100644 --- a/api/go.sum +++ b/api/go.sum @@ -87,8 +87,8 @@ github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyU github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a h1:WaxIhndF3v/TmaHSF1fauhn7dVKN9AMmw42kYraGNto= github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a/go.mod h1:yEsSG78PHAWyoxTFWWHdh2StSfAL05Zxkpsk73pb9dE= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89 h1:+tWqTXuBAKW4BLfegP0HvtMsE9KK8tX4lw76gaW+oeA= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89/go.mod h1:JC04T5G4E/he5ukonV1oCqa0QzFkLv761VbLruVghJM= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260617073944-e6e62be1c4ec h1:1ctU1kEaqL+kIZGeLSwzKO9dXsoEHMkghkzZ28+pCjM= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260617073944-e6e62be1c4ec/go.mod h1:JC04T5G4E/he5ukonV1oCqa0QzFkLv761VbLruVghJM= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260610101308-d3778a549c89 h1:0rYrSFCNxuwZR2/SfnVRQLPw2uEkSZp+anZm/jXVESs= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260610101308-d3778a549c89/go.mod h1:7yqbVpg0k0vW+kZks+TMU/cd1ovoejyHfVPWcyGYLHI= github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260610101308-d3778a549c89 h1:mSGvj60EKV4W3sMRCoOyjilbwTFwe7LeQMi19yKfFOA= diff --git a/api/v1beta1/keystoneapi_types.go b/api/v1beta1/keystoneapi_types.go index 9e9cc972..005a8afc 100644 --- a/api/v1beta1/keystoneapi_types.go +++ b/api/v1beta1/keystoneapi_types.go @@ -33,6 +33,11 @@ import ( ) const ( + // KeystoneTransportConsumerFinalizer is added to transport secrets that + // KeystoneAPI is actively consuming, preventing premature deletion + // during credential rotation. + KeystoneTransportConsumerFinalizer = "openstack.org/keystone-transport-consumer" + // DbSyncHash hash DbSyncHash = "dbsync" diff --git a/go.mod b/go.mod index edc9f3c2..8d5c4d88 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/onsi/gomega v1.42.0 github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a github.com/openstack-k8s-operators/keystone-operator/api v0.3.1-0.20240213125925-e40975f3db7e - github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89 + github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260617073944-e6e62be1c4ec github.com/openstack-k8s-operators/lib-common/modules/edpm v0.0.0-20260610101308-d3778a549c89 github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260610101308-d3778a549c89 github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260610101308-d3778a549c89 @@ -145,3 +145,7 @@ replace sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.19.7 replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging + +replace github.com/openstack-k8s-operators/infra-operator/apis => github.com/lmiccini/infra-operator/apis v0.0.0-20260619053312-3890a255ab49 + +replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/lmiccini/lib-common/modules/common v0.0.0-20260619123003-c59b288dcc9c diff --git a/go.sum b/go.sum index 3bfc3c61..ee1957c4 100644 --- a/go.sum +++ b/go.sum @@ -100,6 +100,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lmiccini/infra-operator/apis v0.0.0-20260619053312-3890a255ab49 h1:HA3f+q8PFOWYF4WHp46/f13ohlbvAKZyTfqIqbGrWQI= +github.com/lmiccini/infra-operator/apis v0.0.0-20260619053312-3890a255ab49/go.mod h1:OWqo6t7LwQmFxxLrOa9i5Gl8GAA2PwwThhjE8fTrVYc= +github.com/lmiccini/lib-common/modules/common v0.0.0-20260619123003-c59b288dcc9c h1:77i9WjpNQCz85aAqQ139pNyLJRX0+0Hx/XWuj3ekTw0= +github.com/lmiccini/lib-common/modules/common v0.0.0-20260619123003-c59b288dcc9c/go.mod h1:oeIagnkOxEsxluKFcFMW80Lf1rXdV7FT2W+peB6kSE0= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= @@ -120,10 +124,6 @@ github.com/onsi/gomega v1.42.0 h1:CJby8u36xb7v34W78F8WKvqTQP7PCMIPB78IVDB73l4= github.com/onsi/gomega v1.42.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= 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/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a h1:WaxIhndF3v/TmaHSF1fauhn7dVKN9AMmw42kYraGNto= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260610094512-d6e7a2257c1a/go.mod h1:yEsSG78PHAWyoxTFWWHdh2StSfAL05Zxkpsk73pb9dE= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89 h1:+tWqTXuBAKW4BLfegP0HvtMsE9KK8tX4lw76gaW+oeA= -github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260610101308-d3778a549c89/go.mod h1:JC04T5G4E/he5ukonV1oCqa0QzFkLv761VbLruVghJM= github.com/openstack-k8s-operators/lib-common/modules/edpm v0.0.0-20260610101308-d3778a549c89 h1:CPPKxM9CRXVU2NbzK5ksFG7I9PR89wKbVeEUBULcwWk= github.com/openstack-k8s-operators/lib-common/modules/edpm v0.0.0-20260610101308-d3778a549c89/go.mod h1:xsKeDFU3/xEObaVDqd6XEYV3MzvFswbWMlnr2Z3q3ZI= github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260610101308-d3778a549c89 h1:0rYrSFCNxuwZR2/SfnVRQLPw2uEkSZp+anZm/jXVESs= diff --git a/internal/controller/keystoneapi_controller.go b/internal/controller/keystoneapi_controller.go index 53b638c9..1c34747b 100644 --- a/internal/controller/keystoneapi_controller.go +++ b/internal/controller/keystoneapi_controller.go @@ -522,6 +522,14 @@ func (r *KeystoneAPIReconciler) reconcileDelete(ctx context.Context, instance *k } } + if err := rabbitmqv1.RemoveTransportSecretConsumerFinalizer( + ctx, helper, instance.Namespace, + instance.Status.TransportURLSecret, + keystonev1.KeystoneTransportConsumerFinalizer, + ); err != nil { + return ctrl.Result{}, err + } + // Service is deleted so remove the finalizer. controllerutil.RemoveFinalizer(instance, helper.GetFinalizer()) Log.Info("Reconciled Service delete successfully") @@ -1069,11 +1077,15 @@ func (r *KeystoneAPIReconciler) reconcileNormal( Log.Info(fmt.Sprintf("TransportURL %s successfully reconciled - operation: %s", transportURL.Name, string(op))) } - instance.Status.TransportURLSecret = transportURL.Status.SecretName + if err := rabbitmqv1.ManageTransportSecretFinalizer( + ctx, helper, instance.Namespace, + transportURL.Status.SecretName, + keystonev1.KeystoneTransportConsumerFinalizer, + ); err != nil { + return ctrl.Result{}, err + } - if instance.Status.TransportURLSecret == "" { - // Since the TransportURL secret is automatically created by the Infra operator, - // we treat this as an info (because the user is not responsible for manually creating it). + if transportURL.Status.SecretName == "" { Log.Info(fmt.Sprintf("Waiting for TransportURL %s secret to be created", transportURL.Name)) instance.Status.Conditions.Set(condition.FalseCondition( condition.RabbitMqTransportURLReadyCondition, @@ -1152,7 +1164,7 @@ func (r *KeystoneAPIReconciler) reconcileNormal( // - %-config configmap holding minimal keystone config required to get the service up, user can add additional files to be added to the service // - parameters which has passwords gets added from the OpenStack secret via the init container // - err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, memcached, db) + err = r.generateServiceConfigMaps(ctx, instance, helper, &configMapVars, memcached, db, transportURL.Status.SecretName) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( condition.ServiceConfigReadyCondition, @@ -1469,6 +1481,20 @@ func (r *KeystoneAPIReconciler) reconcileNormal( } Log.Info("Reconciled Service successfully") + + allSubCRsStable := op == controllerutil.OperationResultNone + secretName, err := rabbitmqv1.FinalizeTransportSecretRotation( + ctx, helper, instance.Namespace, + instance.Status.TransportURLSecret, + transportURL.Status.SecretName, + keystonev1.KeystoneTransportConsumerFinalizer, + allSubCRsStable && instance.Status.Conditions.AllSubConditionIsTrue(), + ) + if err != nil { + return ctrl.Result{}, err + } + instance.Status.TransportURLSecret = secretName + return ctrl.Result{}, nil } @@ -1520,6 +1546,7 @@ func (r *KeystoneAPIReconciler) generateServiceConfigMaps( envVars *map[string]env.Setter, mc *memcachedv1.Memcached, db *mariadbv1.Database, + transportURLSecretName string, ) error { // // create Configmap/Secret required for keystone input @@ -1545,7 +1572,7 @@ func (r *KeystoneAPIReconciler) generateServiceConfigMaps( } maps.Copy(customData, instance.Spec.DefaultConfigOverwrite) - transportURLSecret, _, err := oko_secret.GetSecret(ctx, h, instance.Status.TransportURLSecret, instance.Namespace) + transportURLSecret, _, err := oko_secret.GetSecret(ctx, h, transportURLSecretName, instance.Namespace) if err != nil { return err } diff --git a/test/functional/keystoneapi_controller_test.go b/test/functional/keystoneapi_controller_test.go index 421a808e..87ce2537 100644 --- a/test/functional/keystoneapi_controller_test.go +++ b/test/functional/keystoneapi_controller_test.go @@ -2787,4 +2787,71 @@ OIDCRedirectURI "{{ .KeystoneEndpointPublic }}/v3/auth/OS-FEDERATION/websso/open Expect(err.Error()).To(ContainSubstring("gracePeriodDays must be smaller than expirationDays")) }) }) + + When("TransportURL consumer finalizer is managed", func() { + BeforeEach(func() { + DeferCleanup(th.DeleteInstance, CreateKeystoneAPI(keystoneAPIName, GetDefaultKeystoneAPISpec())) + DeferCleanup( + k8sClient.Delete, ctx, CreateKeystoneMessageBusSecret(namespace, "rabbitmq-secret")) + DeferCleanup( + k8sClient.Delete, ctx, CreateKeystoneAPISecret(namespace, SecretName)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + namespace, + GetKeystoneAPI(keystoneAPIName).Spec.DatabaseInstance, + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + mariadb.SimulateMariaDBAccountCompleted(keystoneAccountName) + mariadb.SimulateMariaDBDatabaseCompleted(keystoneDatabaseName) + infra.SimulateTransportURLReady(types.NamespacedName{ + Name: fmt.Sprintf("%s-keystone-transport", keystoneAPIName.Name), + Namespace: namespace, + }) + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(namespace, "memcached", memcachedSpec)) + infra.SimulateMemcachedReady(types.NamespacedName{ + Name: "memcached", + Namespace: namespace, + }) + th.SimulateJobSuccess(dbSyncJobName) + th.SimulateJobSuccess(bootstrapJobName) + th.SimulateDeploymentReplicaReady(deploymentName) + }) + + It("should add the consumer finalizer to the transport secret", func() { + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: namespace, + Name: "rabbitmq-secret", + }) + g.Expect(secret.Finalizers).To( + ContainElement(keystonev1.KeystoneTransportConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + }) + + It("should remove the consumer finalizer from transport secret on CR deletion", func() { + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: namespace, + Name: "rabbitmq-secret", + }) + g.Expect(secret.Finalizers).To( + ContainElement(keystonev1.KeystoneTransportConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + th.DeleteInstance(GetKeystoneAPI(keystoneAPIName)) + + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: namespace, + Name: "rabbitmq-secret", + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(keystonev1.KeystoneTransportConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + }) + }) })