diff --git a/api/bases/nova.openstack.org_nova.yaml b/api/bases/nova.openstack.org_nova.yaml index 322c6ee19..f2c1e485e 100644 --- a/api/bases/nova.openstack.org_nova.yaml +++ b/api/bases/nova.openstack.org_nova.yaml @@ -1914,6 +1914,13 @@ spec: from nova-api format: int32 type: integer + applicationCredentialSecret: + description: |- + ApplicationCredentialSecret - the AC secret nova is currently + consuming and protecting with the openstack.org/nova-ac-consumer + finalizer. Tracked so the controller can remove its finalizer from the + old secret when the openstack-operator rotates the reference. + type: string conditions: description: Conditions items: diff --git a/api/go.mod b/api/go.mod index 182c65598..b36e9c1ef 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,7 +4,7 @@ go 1.24.4 require ( github.com/google/go-cmp v0.7.0 - github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260416122644-5476763a36b6 + github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587 github.com/robfig/cron/v3 v3.0.1 k8s.io/api v0.31.14 @@ -43,16 +43,17 @@ require ( 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/spf13/pflag v1.0.7 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/x448/float16 v0.8.4 // 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/net v0.49.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.39.0 // indirect - golang.org/x/text v0.33.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect + golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/protobuf v1.36.7 // indirect diff --git a/api/go.sum b/api/go.sum index f938bfd46..e516a45d8 100644 --- a/api/go.sum +++ b/api/go.sum @@ -78,8 +78,8 @@ github.com/onsi/ginkgo/v2 v2.28.2 h1:DTrMfpqxiNUyQ3Y0zhn1n3cOO2euFgQPYIpkWwxVFps github.com/onsi/ginkgo/v2 v2.28.2/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260416122644-5476763a36b6 h1:117Gu9HCSu2tAp579WnCJ9QtnslH2qnPB8UFvn8ZpqE= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260416122644-5476763a36b6/go.mod h1:i7l8cihvFktd/LSuyvL2z6OcwauarQGoVhDMePL4VyI= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c h1:9379F/g0DN6MiCnBNQvSdXV+LHzS79bSOFIviriqa0w= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c/go.mod h1:RFFB4Zs9IJv1jXs/yMjo+VswSW+rsrFZsoP0QrB1EbI= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587 h1:p03uEXoSreyu7LpFmb9YyYM8tEx2D2+7qqhLXNWHTq0= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587/go.mod h1:JC04T5G4E/he5ukonV1oCqa0QzFkLv761VbLruVghJM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -99,12 +99,12 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= @@ -144,14 +144,14 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/api/nova/v1beta1/nova_types.go b/api/nova/v1beta1/nova_types.go index 39d4a5c11..4c21caf83 100644 --- a/api/nova/v1beta1/nova_types.go +++ b/api/nova/v1beta1/nova_types.go @@ -185,6 +185,12 @@ type NovaStatus struct { //ObservedGeneration - the most recent generation observed for this service. If the observed generation is less than the spec generation, then the controller has not processed the latest changes. ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // ApplicationCredentialSecret - the AC secret nova is currently + // consuming and protecting with the openstack.org/nova-ac-consumer + // finalizer. Tracked so the controller can remove its finalizer from the + // old secret when the openstack-operator rotates the reference. + ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"` } //+kubebuilder:object:root=true diff --git a/config/crd/bases/nova.openstack.org_nova.yaml b/config/crd/bases/nova.openstack.org_nova.yaml index 322c6ee19..f2c1e485e 100644 --- a/config/crd/bases/nova.openstack.org_nova.yaml +++ b/config/crd/bases/nova.openstack.org_nova.yaml @@ -1914,6 +1914,13 @@ spec: from nova-api format: int32 type: integer + applicationCredentialSecret: + description: |- + ApplicationCredentialSecret - the AC secret nova is currently + consuming and protecting with the openstack.org/nova-ac-consumer + finalizer. Tracked so the controller can remove its finalizer from the + old secret when the openstack-operator rotates the reference. + type: string conditions: description: Conditions items: diff --git a/go.mod b/go.mod index e25e40bc2..ba77a0961 100644 --- a/go.mod +++ b/go.mod @@ -10,12 +10,12 @@ require ( github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7 github.com/onsi/ginkgo/v2 v2.28.2 github.com/onsi/gomega v1.41.0 - github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260416122644-5476763a36b6 - github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260420052838-77f94aef5af2 + github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c + github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260520090027-4d7b7a01c0bf github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587 - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260417092244-81c71b39e981 - github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260417092244-81c71b39e981 - github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260413152655-564a51226a2a + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260518125357-72bdd580c587 + github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260518125357-72bdd580c587 + github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260523055435-57442d177f21 github.com/openstack-k8s-operators/nova-operator/api v0.0.0-20221209164002-f9e6b9363961 go.uber.org/zap v1.28.0 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 @@ -65,7 +65,7 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openshift/api v3.9.0+incompatible // indirect - github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260417092244-81c71b39e981 // indirect + github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260506154724-30a976ba8ef0 // 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 @@ -73,7 +73,7 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect @@ -92,9 +92,9 @@ require ( golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/term v0.39.0 // indirect - golang.org/x/text v0.33.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect + golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.41.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect diff --git a/go.sum b/go.sum index 1291d77ca..5b9e63c4b 100644 --- a/go.sum +++ b/go.sum @@ -118,20 +118,20 @@ github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= github.com/onsi/gomega v1.41.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.20260416122644-5476763a36b6 h1:117Gu9HCSu2tAp579WnCJ9QtnslH2qnPB8UFvn8ZpqE= -github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260416122644-5476763a36b6/go.mod h1:i7l8cihvFktd/LSuyvL2z6OcwauarQGoVhDMePL4VyI= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260420052838-77f94aef5af2 h1:h7pTz90cHqX6nTYjYDphuitIfD4UpM9yGnI3AbLdHrY= -github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260420052838-77f94aef5af2/go.mod h1:SpO4CL7c5/1HG+61fP6kWhL2+3aqR+5SNatdZueKrz8= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c h1:9379F/g0DN6MiCnBNQvSdXV+LHzS79bSOFIviriqa0w= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260521141938-b817d49cac5c/go.mod h1:RFFB4Zs9IJv1jXs/yMjo+VswSW+rsrFZsoP0QrB1EbI= +github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260520090027-4d7b7a01c0bf h1:FoKK0zNo48i4ZMFxScupCK/YAmy6Ps4IILz3CK4BCTI= +github.com/openstack-k8s-operators/keystone-operator/api v0.6.1-0.20260520090027-4d7b7a01c0bf/go.mod h1:VNX1Mda2u5+yGxycIyVrgABucitMDR9ct3Lj6ROS92I= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587 h1:p03uEXoSreyu7LpFmb9YyYM8tEx2D2+7qqhLXNWHTq0= github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260518125357-72bdd580c587/go.mod h1:JC04T5G4E/he5ukonV1oCqa0QzFkLv761VbLruVghJM= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260417092244-81c71b39e981 h1:jN3Kvt+RYUTaL9EXeeeIqRXVjqeNF74SuLTDXmi4X2Y= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260417092244-81c71b39e981/go.mod h1:7yqbVpg0k0vW+kZks+TMU/cd1ovoejyHfVPWcyGYLHI= -github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260417092244-81c71b39e981 h1:X3/Gc+i0ZxaROExrpLXonz9EPhftlubFnOK4aSkRLvo= -github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260417092244-81c71b39e981/go.mod h1:3loLaPUDQyvbPekylZd9OCLF+EXH2klRI9IeeQhuMcs= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260417092244-81c71b39e981 h1:KAQ8T+Ri3JWgsyK1D6QybScMh6fpkYUUA+0ntnOiAl4= -github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260417092244-81c71b39e981/go.mod h1:dEjz8zHRIlP3vnMmWdHytlLeSZ6BHcIiSTPM7xTQxFg= -github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260413152655-564a51226a2a h1:1VRHhhCE8U0+Q6jPNppxcklIVfK7gZ2Js9VaLpPR7sw= -github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260413152655-564a51226a2a/go.mod h1:g/xgMnzNHxdTkqnEgAKwVOv75uPN4nuApbkGqSvASvs= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260518125357-72bdd580c587 h1:sg+VXXWM8amYFpynStptXbE3fNFwzYef4vVniMMzmAE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260518125357-72bdd580c587/go.mod h1:7yqbVpg0k0vW+kZks+TMU/cd1ovoejyHfVPWcyGYLHI= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260506154724-30a976ba8ef0 h1:wuzSibIT9F/5RbMmxvBVFj6fy2vtKo58nibzmk5L4PM= +github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20260506154724-30a976ba8ef0/go.mod h1:tft3oDiN+v6wX3ILPXGUM/gCLJz6QtrPN63hxpJ3E24= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260518125357-72bdd580c587 h1:jpouKcgs2Kc5z2JHIpvsXMxEonfXLgzX3KswuBoeKQ0= +github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260518125357-72bdd580c587/go.mod h1:nLS2oK4pBo756JNN1cPgr44S0X9V11QScgVla89Ojok= +github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260523055435-57442d177f21 h1:Hp6/C8qq+F6/0WQnDKS7wAEVhjGIKGljdtlZkHVMotM= +github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260523055435-57442d177f21/go.mod h1:/9N3XCC/QR2CowKaVmz1R5BP9NVJxL6RAb6aplOYGe8= 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= @@ -153,15 +153,15 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -229,14 +229,14 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/controller/nova/nova_controller.go b/internal/controller/nova/nova_controller.go index 4efcb433e..3c0fab596 100644 --- a/internal/controller/nova/nova_controller.go +++ b/internal/controller/nova/nova_controller.go @@ -303,6 +303,24 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul } } + // Add consumer finalizer to the new AC secret early, before deployment. + // The old secret's finalizer is removed later (after all services deploy) + // so that rapid rotations don't revoke a credential still in use by pods. + if instance.Spec.Auth.ApplicationCredentialSecret != "" { + if err := keystonev1.ManageACSecretFinalizer(ctx, h, instance.Namespace, + instance.Spec.Auth.ApplicationCredentialSecret, + "", + nova.ACConsumerFinalizer); err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.InputReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.InputReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + } + instance.Status.Conditions.MarkTrue(condition.InputReadyCondition, condition.InputReadyMessage) err = r.ensureKeystoneServiceUser(ctx, h, instance) @@ -774,6 +792,25 @@ func (r *NovaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (resul ) } + // Manage the old AC secret's finalizer and status tracking. + // On rotation (old != new), only remove the old secret's finalizer after + // all sub-services are ready with the new credentials. This prevents + // premature revocation during rapid rotations. + isRotation := instance.Status.ApplicationCredentialSecret != "" && instance.Status.ApplicationCredentialSecret != instance.Spec.Auth.ApplicationCredentialSecret + + if isRotation { + allServicesReady := instance.Status.Conditions.AllSubConditionIsTrue() + if allServicesReady { + if err := keystonev1.RemoveACSecretConsumerFinalizer(ctx, h, instance.Namespace, + instance.Status.ApplicationCredentialSecret, nova.ACConsumerFinalizer); err != nil { + return ctrl.Result{}, err + } + instance.Status.ApplicationCredentialSecret = instance.Spec.Auth.ApplicationCredentialSecret + } + } else if instance.Status.ApplicationCredentialSecret != instance.Spec.Auth.ApplicationCredentialSecret { + instance.Status.ApplicationCredentialSecret = instance.Spec.Auth.ApplicationCredentialSecret + } + Log.Info("Successfully reconciled") return ctrl.Result{}, nil } @@ -1770,6 +1807,17 @@ func (r *NovaReconciler) reconcileDelete( return err } + // Remove consumer finalizer from AC secrets nova was consuming. + for _, secretName := range []string{ + instance.Status.ApplicationCredentialSecret, + instance.Spec.Auth.ApplicationCredentialSecret, + } { + if err := keystonev1.RemoveACSecretConsumerFinalizer(ctx, h, instance.Namespace, + secretName, nova.ACConsumerFinalizer); err != nil { + return err + } + } + // Successfully cleaned up everything. So as the final step let's remove the // finalizer from ourselves to allow the deletion of Nova CR itself updated := controllerutil.RemoveFinalizer(instance, h.GetFinalizer()) diff --git a/internal/nova/common.go b/internal/nova/common.go index c4afe775e..900ef31a7 100644 --- a/internal/nova/common.go +++ b/internal/nova/common.go @@ -30,6 +30,11 @@ const ( NovaUserID int64 = 42436 ) +const ( + // ACConsumerFinalizer is added to AC secrets that nova is actively consuming + ACConsumerFinalizer = "openstack.org/nova-ac-consumer" +) + // GetScriptSecretName returns the name of the Secret used for the // db sync scripts func GetScriptSecretName(crName string) string { diff --git a/test/functional/nova/nova_controller_test.go b/test/functional/nova/nova_controller_test.go index 0882f1aff..2d351ccbc 100644 --- a/test/functional/nova/nova_controller_test.go +++ b/test/functional/nova/nova_controller_test.go @@ -37,6 +37,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/util" novav1 "github.com/openstack-k8s-operators/nova-operator/api/nova/v1beta1" controllers "github.com/openstack-k8s-operators/nova-operator/internal/controller/nova" + nova "github.com/openstack-k8s-operators/nova-operator/internal/nova" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -2157,4 +2158,309 @@ var _ = Describe("application credentials", func() { }, timeout, interval).Should(Succeed()) }) }) + + When("ApplicationCredential consumer finalizer is managed", func() { + var acSecretName string + + BeforeEach(func() { + acSecretName = "ac-nova-a1b2c-secret" //nolint:gosec // G101 + ac := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }, + Data: map[string][]byte{ + keystonev1.ACIDSecretKey: []byte("a1b2ctest-ac-id"), + keystonev1.ACSecretSecretKey: []byte("test-ac-secret"), + }, + } + DeferCleanup(k8sClient.Delete, ctx, ac) + Expect(k8sClient.Create(ctx, ac)).To(Succeed()) + + DeferCleanup(k8sClient.Delete, ctx, CreateNovaSecret(novaNames.NovaName.Namespace, SecretName)) + DeferCleanup(k8sClient.Delete, ctx, CreateNovaMessageBusSecret(cell0)) + DeferCleanup( + mariadb.DeleteDBService, + mariadb.CreateDBService( + novaNames.NovaName.Namespace, + "openstack", + corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{Port: 3306}}, + }, + ), + ) + memcachedSpec := infra.GetDefaultMemcachedSpec() + DeferCleanup(infra.DeleteMemcached, infra.CreateMemcached(novaNames.NovaName.Namespace, MemcachedInstance, memcachedSpec)) + infra.SimulateMemcachedReady(novaNames.MemcachedNamespace) + + DeferCleanup(keystone.DeleteKeystoneAPI, keystone.CreateKeystoneAPI(novaNames.NovaName.Namespace)) + + rawNova := map[string]any{ + "apiVersion": "nova.openstack.org/v1beta1", + "kind": "Nova", + "metadata": map[string]any{ + "name": novaNames.NovaName.Name, + "namespace": novaNames.NovaName.Namespace, + }, + "spec": map[string]any{ + "secret": SecretName, + "apiDatabaseAccount": novaNames.APIMariaDBDatabaseAccount.Name, + "cellTemplates": map[string]any{ + "cell0": map[string]any{ + "cellDatabaseAccount": cell0.MariaDBAccountName.Name, + "apiDatabaseAccount": novaNames.APIMariaDBDatabaseAccount.Name, + "hasAPIAccess": true, + "dbPurge": map[string]any{ + "schedule": "1 0 * * *", + }, + }, + }, + "messagingBus": map[string]any{ + "cluster": cell0.TransportURLName.Name, + }, + "auth": map[string]any{ + "applicationCredentialSecret": acSecretName, + }, + }, + } + DeferCleanup(th.DeleteInstance, th.CreateUnstructured(rawNova)) + }) + + It("should add the consumer finalizer to the AC secret", func() { + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + }) + + It("should track the consumed AC secret in status", func() { + keystone.SimulateKeystoneServiceReady(novaNames.KeystoneServiceName) + mariadb.SimulateMariaDBDatabaseCompleted(novaNames.APIMariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(novaNames.APIMariaDBDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(cell0.MariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(cell0.MariaDBAccountName) + infra.SimulateTransportURLReady(cell0.TransportURLName) + SimulateReadyOfNovaTopServices() + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + g.Expect(n.Status.ApplicationCredentialSecret).To(Equal(acSecretName)) + }, timeout, interval).Should(Succeed()) + }) + + It("should move the finalizer from the old to the new secret on rotation", func() { + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + keystone.SimulateKeystoneServiceReady(novaNames.KeystoneServiceName) + mariadb.SimulateMariaDBDatabaseCompleted(novaNames.APIMariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(novaNames.APIMariaDBDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(cell0.MariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(cell0.MariaDBAccountName) + infra.SimulateTransportURLReady(cell0.TransportURLName) + SimulateReadyOfNovaTopServices() + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + g.Expect(n.Status.Conditions.IsTrue(condition.ReadyCondition)).To(BeTrue()) + }, timeout, interval).Should(Succeed()) + + newACSecretName := "ac-nova-x9y8z-secret" //nolint:gosec // G101 + newSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: novaNames.NovaName.Namespace, + Name: newACSecretName, + }, + Data: map[string][]byte{ + keystonev1.ACIDSecretKey: []byte("x9y8zrotated-ac-id"), + keystonev1.ACSecretSecretKey: []byte("rotated-ac-secret"), + }, + } + DeferCleanup(k8sClient.Delete, ctx, newSecret) + Expect(k8sClient.Create(ctx, newSecret)).To(Succeed()) + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + n.Spec.Auth.ApplicationCredentialSecret = newACSecretName + g.Expect(k8sClient.Update(ctx, n)).Should(Succeed()) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: newACSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + SimulateReadyOfNovaTopServices() + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + g.Expect(n.Status.ApplicationCredentialSecret).To(Equal(newACSecretName)) + }, timeout, interval).Should(Succeed()) + }) + + It("should remove the consumer finalizer from AC secret on CR deletion", func() { + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + th.DeleteInstance(GetNova(novaNames.NovaName)) + + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + }) + + It("should remove finalizer and clear status when AC secret is cleared from spec", func() { + keystone.SimulateKeystoneServiceReady(novaNames.KeystoneServiceName) + mariadb.SimulateMariaDBDatabaseCompleted(novaNames.APIMariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(novaNames.APIMariaDBDatabaseAccount) + mariadb.SimulateMariaDBDatabaseCompleted(cell0.MariaDBDatabaseName) + mariadb.SimulateMariaDBAccountCompleted(cell0.MariaDBAccountName) + infra.SimulateTransportURLReady(cell0.TransportURLName) + SimulateReadyOfNovaTopServices() + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + g.Expect(n.Status.Conditions.IsTrue(condition.ReadyCondition)).To(BeTrue()) + g.Expect(n.Status.ApplicationCredentialSecret).To(Equal(acSecretName)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + n.Spec.Auth.ApplicationCredentialSecret = "" + g.Expect(k8sClient.Update(ctx, n)).Should(Succeed()) + }, timeout, interval).Should(Succeed()) + + SimulateReadyOfNovaTopServices() + + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + g.Expect(n.Status.ApplicationCredentialSecret).To(Equal("")) + }, timeout, interval).Should(Succeed()) + }) + + It("should remove finalizers from both secrets when CR is deleted during in-flight rotation", func() { + // Wait for old AC secret to get its finalizer (happens early in + // reconcile, before full readiness is needed) + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + // Manually set Status.ApplicationCredentialSecret to simulate a + // previous successful reconcile. The controller can't reach line 810 + // without full readiness, so this value persists. + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + n.Status.ApplicationCredentialSecret = acSecretName + g.Expect(k8sClient.Status().Update(ctx, n)).Should(Succeed()) + }, timeout, interval).Should(Succeed()) + + newACSecretName := "ac-nova-inflight-secret" //nolint:gosec // G101 + newSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: novaNames.NovaName.Namespace, + Name: newACSecretName, + }, + Data: map[string][]byte{ + keystonev1.ACIDSecretKey: []byte("inflight-ac-id"), + keystonev1.ACSecretSecretKey: []byte("inflight-ac-secret"), + }, + } + DeferCleanup(k8sClient.Delete, ctx, newSecret) + Expect(k8sClient.Create(ctx, newSecret)).To(Succeed()) + + Eventually(func(g Gomega) { + n := GetNova(novaNames.NovaName) + n.Spec.Auth.ApplicationCredentialSecret = newACSecretName + g.Expect(k8sClient.Update(ctx, n)).Should(Succeed()) + }, timeout, interval).Should(Succeed()) + + // Wait for new secret to get the finalizer (rotation triggered) + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: newACSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + // Old secret still has finalizer (controller can't complete + // reconcile to remove it because services are not ready) + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).To( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + // Delete the Nova CR while rotation is in-flight + th.DeleteInstance(GetNova(novaNames.NovaName)) + + // Both secrets should have their finalizers removed + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: acSecretName, + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + + Eventually(func(g Gomega) { + secret := th.GetSecret(types.NamespacedName{ + Namespace: novaNames.NovaName.Namespace, + Name: newACSecretName, + }) + g.Expect(secret.Finalizers).NotTo( + ContainElement(nova.ACConsumerFinalizer)) + }, timeout, interval).Should(Succeed()) + }) + }) }) diff --git a/test/kuttl/test-suites/default/zz-appcred-tests/02-assert.yaml b/test/kuttl/test-suites/default/zz-appcred-tests/02-assert.yaml index dfe8d78b7..1c717bf52 100644 --- a/test/kuttl/test-suites/default/zz-appcred-tests/02-assert.yaml +++ b/test/kuttl/test-suites/default/zz-appcred-tests/02-assert.yaml @@ -16,16 +16,23 @@ commands: fi echo "ac-nova.status.acID=${ac_id}" - echo "Waiting for Secret ac-nova-secret..." + ac_secret_name=$(oc get -n "${NS}" appcred/ac-nova -o jsonpath='{.status.secretName}') + if [ -z "${ac_secret_name}" ]; then + echo "ERROR: appcred/ac-nova.status.secretName is empty" + exit 1 + fi + echo "AC secret name: ${ac_secret_name}" + + echo "Waiting for Secret ${ac_secret_name}..." for _ in $(seq 1 60); do - if oc get -n "${NS}" secret/ac-nova-secret >/dev/null 2>&1; then + if oc get -n "${NS}" secret/"${ac_secret_name}" >/dev/null 2>&1; then break fi sleep 2 done - oc get -n "${NS}" secret/ac-nova-secret >/dev/null + oc get -n "${NS}" secret/"${ac_secret_name}" >/dev/null - secret_ac_id=$(oc get -n "${NS}" secret/ac-nova-secret -o jsonpath='{.data.AC_ID}' | base64 -d) + secret_ac_id=$(oc get -n "${NS}" secret/"${ac_secret_name}" -o jsonpath='{.data.AC_ID}' | base64 -d) if [ "${secret_ac_id}" != "${ac_id}" ]; then echo "ERROR: Secret AC_ID (${secret_ac_id}) != appcred.status.acID (${ac_id})" exit 1 diff --git a/test/kuttl/test-suites/default/zz-appcred-tests/02-deploy-appcred.yaml b/test/kuttl/test-suites/default/zz-appcred-tests/02-deploy-appcred.yaml index 2d27f6ff3..cb6c4f9ea 100644 --- a/test/kuttl/test-suites/default/zz-appcred-tests/02-deploy-appcred.yaml +++ b/test/kuttl/test-suites/default/zz-appcred-tests/02-deploy-appcred.yaml @@ -26,5 +26,14 @@ commands: unrestricted: false EOF - # Ensure the service operator is configured to use the standard AC secret name. - oc patch nova nova-kuttl -n "${NS}" --type=merge -p '{"spec":{"auth":{"applicationCredentialSecret":"ac-nova-secret"}}}' + echo "Waiting for KeystoneApplicationCredential ac-nova to be Ready..." + oc wait -n "${NS}" appcred/ac-nova --for=condition=Ready --timeout=300s + + ac_secret_name=$(oc get -n "${NS}" appcred/ac-nova -o jsonpath='{.status.secretName}') + if [ -z "${ac_secret_name}" ]; then + echo "ERROR: appcred/ac-nova.status.secretName is empty" + exit 1 + fi + echo "AC secret name: ${ac_secret_name}" + + oc patch nova nova-kuttl -n "${NS}" --type=merge -p "{\"spec\":{\"auth\":{\"applicationCredentialSecret\":\"${ac_secret_name}\"}}}" diff --git a/test/kuttl/test-suites/default/zz-appcred-tests/03-assert.yaml b/test/kuttl/test-suites/default/zz-appcred-tests/03-assert.yaml index 5a1f4272f..0757a5629 100644 --- a/test/kuttl/test-suites/default/zz-appcred-tests/03-assert.yaml +++ b/test/kuttl/test-suites/default/zz-appcred-tests/03-assert.yaml @@ -29,7 +29,18 @@ commands: fi echo "✓ ACID rotated: ${prev_acid} -> ${ac_id}" - secret_ac_id=$(oc get -n "${NS}" secret/ac-nova-secret -o jsonpath='{.data.AC_ID}' | base64 -d) + ac_secret_name=$(oc get -n "${NS}" appcred/ac-nova -o jsonpath='{.status.secretName}') + if [ -z "${ac_secret_name}" ]; then + echo "ERROR: appcred/ac-nova.status.secretName is empty after rotation" + exit 1 + fi + echo "AC secret name after rotation: ${ac_secret_name}" + + # Patch the service CR with the new secret name + oc patch nova nova-kuttl -n "${NS}" --type=merge \ + -p "{\"spec\":{\"auth\":{\"applicationCredentialSecret\":\"${ac_secret_name}\"}}}" + + secret_ac_id=$(oc get -n "${NS}" secret/"${ac_secret_name}" -o jsonpath='{.data.AC_ID}' | base64 -d) if [ "${secret_ac_id}" != "${ac_id}" ]; then echo "ERROR: Secret AC_ID (${secret_ac_id}) != rotated appcred.status.acID (${ac_id})" exit 1 diff --git a/test/kuttl/test-suites/default/zz-appcred-tests/04-cleanup.yaml b/test/kuttl/test-suites/default/zz-appcred-tests/04-cleanup.yaml index bb2ec5938..56803be4e 100644 --- a/test/kuttl/test-suites/default/zz-appcred-tests/04-cleanup.yaml +++ b/test/kuttl/test-suites/default/zz-appcred-tests/04-cleanup.yaml @@ -21,10 +21,6 @@ delete: kind: KeystoneApplicationCredential name: ac-nova namespace: nova-kuttl-default - - apiVersion: v1 - kind: Secret - name: ac-nova-secret - namespace: nova-kuttl-default - apiVersion: v1 kind: ConfigMap name: appcred-nova-pre