@@ -17,6 +17,7 @@ import (
1717 . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers"
1818 mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
1919 watcherv1beta1 "github.com/openstack-k8s-operators/watcher-operator/api/v1beta1"
20+ "github.com/openstack-k8s-operators/watcher-operator/internal/watcher"
2021 corev1 "k8s.io/api/core/v1"
2122 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -1858,6 +1859,188 @@ var _ = Describe("Watcher controller", func() {
18581859 })
18591860 })
18601861
1862+ When ("ApplicationCredential consumer finalizer is managed" , func () {
1863+ var acSecretName string
1864+
1865+ BeforeEach (func () {
1866+ acSecretName = "ac-watcher-consumer-fnz-secret" //nolint:gosec
1867+
1868+ acSecret := & corev1.Secret {
1869+ ObjectMeta : metav1.ObjectMeta {
1870+ Name : acSecretName ,
1871+ Namespace : watcherTest .Instance .Namespace ,
1872+ },
1873+ Data : map [string ][]byte {
1874+ keystonev1beta1 .ACIDSecretKey : []byte ("consumer-test-ac-id" ),
1875+ keystonev1beta1 .ACSecretSecretKey : []byte ("consumer-test-ac-secret" ), //nolint:gosec
1876+ },
1877+ }
1878+ Expect (k8sClient .Create (ctx , acSecret )).To (Succeed ())
1879+ DeferCleanup (k8sClient .Delete , ctx , acSecret )
1880+
1881+ DeferCleanup (k8sClient .Delete , ctx , CreateWatcherMessageBusSecret (watcherTest .Instance .Namespace , "rabbitmq-secret" ))
1882+
1883+ memcachedSpec := memcachedv1.MemcachedSpec {
1884+ MemcachedSpecCore : memcachedv1.MemcachedSpecCore {
1885+ Replicas : ptr .To (int32 (1 )),
1886+ },
1887+ }
1888+ DeferCleanup (infra .DeleteMemcached , infra .CreateMemcached (watcherTest .Watcher .Namespace , MemcachedInstance , memcachedSpec ))
1889+ infra .SimulateMemcachedReady (watcherTest .MemcachedNamespace )
1890+
1891+ DeferCleanup (keystone .DeleteKeystoneAPI , keystone .CreateKeystoneAPI (watcherTest .WatcherAPI .Namespace ))
1892+
1893+ DeferCleanup (
1894+ k8sClient .Delete , ctx , th .CreateSecret (
1895+ types.NamespacedName {Namespace : watcherTest .Instance .Namespace , Name : "metric-storage-prometheus-endpoint" },
1896+ map [string ][]byte {
1897+ "host" : []byte ("prometheus.example.com" ),
1898+ "port" : []byte ("9090" ),
1899+ },
1900+ ))
1901+
1902+ DeferCleanup (
1903+ k8sClient .Delete , ctx , th .CreateSecret (
1904+ types.NamespacedName {Namespace : watcherTest .Instance .Namespace , Name : SecretName },
1905+ map [string ][]byte {
1906+ "WatcherPassword" : []byte ("password" ),
1907+ },
1908+ ))
1909+
1910+ // Create Watcher CR after all secrets and dependencies are in place
1911+ // so sub-CR controllers don't enter long exponential backoff.
1912+ spec := GetDefaultWatcherSpec ()
1913+ spec ["auth" ] = map [string ]any {"applicationCredentialSecret" : acSecretName }
1914+ DeferCleanup (th .DeleteInstance , CreateWatcher (watcherTest .Instance , spec ))
1915+
1916+ DeferCleanup (
1917+ mariadb .DeleteDBService ,
1918+ mariadb .CreateDBService (
1919+ watcherTest .Instance .Namespace ,
1920+ * GetWatcher (watcherTest .Instance ).Spec .DatabaseInstance ,
1921+ corev1.ServiceSpec {
1922+ Ports : []corev1.ServicePort {{Port : 3306 }},
1923+ },
1924+ ),
1925+ )
1926+
1927+ mariadb .SimulateMariaDBAccountCompleted (watcherTest .WatcherDatabaseAccount )
1928+ mariadb .SimulateMariaDBDatabaseCompleted (watcherTest .WatcherDatabaseName )
1929+ infra .SimulateTransportURLReady (watcherTest .WatcherTransportURL )
1930+
1931+ keystone .SimulateKeystoneServiceReady (watcherTest .KeystoneServiceName )
1932+ th .SimulateJobSuccess (watcherTest .WatcherDBSync )
1933+ })
1934+
1935+ It ("should add the consumer finalizer to the AC secret" , func () {
1936+ Eventually (func (g Gomega ) {
1937+ secret := th .GetSecret (types.NamespacedName {
1938+ Namespace : watcherTest .Instance .Namespace ,
1939+ Name : acSecretName ,
1940+ })
1941+ g .Expect (secret .Finalizers ).To (
1942+ ContainElement (watcher .ACConsumerFinalizer ))
1943+ }, timeout , interval ).Should (Succeed ())
1944+ })
1945+
1946+ It ("should track the consumed AC secret in status" , func () {
1947+ Eventually (func (g Gomega ) {
1948+ w := GetWatcher (watcherTest .Instance )
1949+ g .Expect (w .Status .ApplicationCredentialSecret ).To (Equal (acSecretName ))
1950+ }, timeout , interval ).Should (Succeed ())
1951+ })
1952+
1953+ It ("should move the finalizer from the old to the new secret on rotation" , func () {
1954+ Eventually (func (g Gomega ) {
1955+ secret := th .GetSecret (types.NamespacedName {
1956+ Namespace : watcherTest .Instance .Namespace ,
1957+ Name : acSecretName ,
1958+ })
1959+ g .Expect (secret .Finalizers ).To (
1960+ ContainElement (watcher .ACConsumerFinalizer ))
1961+ }, timeout , interval ).Should (Succeed ())
1962+
1963+ newACSecretName := "ac-watcher-consumer-rotated-secret" //nolint:gosec
1964+ newSecret := & corev1.Secret {
1965+ ObjectMeta : metav1.ObjectMeta {
1966+ Namespace : watcherTest .Instance .Namespace ,
1967+ Name : newACSecretName ,
1968+ },
1969+ Data : map [string ][]byte {
1970+ keystonev1beta1 .ACIDSecretKey : []byte ("rotated-ac-id" ),
1971+ keystonev1beta1 .ACSecretSecretKey : []byte ("rotated-ac-secret-value" ), //nolint:gosec
1972+ },
1973+ }
1974+ DeferCleanup (k8sClient .Delete , ctx , newSecret )
1975+ Expect (k8sClient .Create (ctx , newSecret )).To (Succeed ())
1976+
1977+ Eventually (func (g Gomega ) {
1978+ w := GetWatcher (watcherTest .Instance )
1979+ w .Spec .Auth .ApplicationCredentialSecret = newACSecretName
1980+ g .Expect (k8sClient .Update (ctx , w )).Should (Succeed ())
1981+ }, timeout , interval ).Should (Succeed ())
1982+
1983+ // New secret gets the consumer finalizer immediately (early in reconcile)
1984+ Eventually (func (g Gomega ) {
1985+ secret := th .GetSecret (types.NamespacedName {
1986+ Namespace : watcherTest .Instance .Namespace ,
1987+ Name : newACSecretName ,
1988+ })
1989+ g .Expect (secret .Finalizers ).To (
1990+ ContainElement (watcher .ACConsumerFinalizer ))
1991+ }, timeout , interval ).Should (Succeed ())
1992+
1993+ // Old secret keeps the finalizer until all services deploy (split pattern)
1994+ secret := th .GetSecret (types.NamespacedName {
1995+ Namespace : watcherTest .Instance .Namespace ,
1996+ Name : acSecretName ,
1997+ })
1998+ Expect (secret .Finalizers ).To (
1999+ ContainElement (watcher .ACConsumerFinalizer ))
2000+
2001+ // Simulate all watcher services deploying successfully
2002+ th .SimulateStatefulSetReplicaReady (watcherTest .WatcherAPIStatefulSet )
2003+ keystone .SimulateKeystoneEndpointReady (watcherTest .WatcherKeystoneEndpointName )
2004+ th .SimulateStatefulSetReplicaReady (watcherTest .WatcherApplierStatefulSet )
2005+ th .SimulateStatefulSetReplicaReady (watcherTest .WatcherDecisionEngineStatefulSet )
2006+
2007+ // Now the old secret's finalizer is removed and status updated
2008+ Eventually (func (g Gomega ) {
2009+ secret := th .GetSecret (types.NamespacedName {
2010+ Namespace : watcherTest .Instance .Namespace ,
2011+ Name : acSecretName ,
2012+ })
2013+ g .Expect (secret .Finalizers ).NotTo (
2014+ ContainElement (watcher .ACConsumerFinalizer ))
2015+ }, timeout , interval ).Should (Succeed ())
2016+
2017+ Eventually (func (g Gomega ) {
2018+ w := GetWatcher (watcherTest .Instance )
2019+ g .Expect (w .Status .ApplicationCredentialSecret ).To (Equal (newACSecretName ))
2020+ }, timeout , interval ).Should (Succeed ())
2021+ })
2022+
2023+ It ("should remove the consumer finalizer from AC secret on CR deletion" , func () {
2024+ Eventually (func (g Gomega ) {
2025+ secret := th .GetSecret (types.NamespacedName {
2026+ Namespace : watcherTest .Instance .Namespace ,
2027+ Name : acSecretName ,
2028+ })
2029+ g .Expect (secret .Finalizers ).To (
2030+ ContainElement (watcher .ACConsumerFinalizer ))
2031+ }, timeout , interval ).Should (Succeed ())
2032+
2033+ th .DeleteInstance (GetWatcher (watcherTest .Instance ))
2034+
2035+ secret := th .GetSecret (types.NamespacedName {
2036+ Namespace : watcherTest .Instance .Namespace ,
2037+ Name : acSecretName ,
2038+ })
2039+ Expect (secret .Finalizers ).NotTo (
2040+ ContainElement (watcher .ACConsumerFinalizer ))
2041+ })
2042+ })
2043+
18612044 When ("ApplicationCredential is adopted on existing deployment" , func () {
18622045 var appCredSecretName string
18632046 var appCredID string
0 commit comments