@@ -7,9 +7,16 @@ import (
77 "slices"
88 "testing"
99
10+ horizonv1 "github.com/openstack-k8s-operators/horizon-operator/api/v1beta1"
11+ keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
12+ "github.com/openstack-k8s-operators/lib-common/modules/common/tls"
13+ neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1"
14+ novav1 "github.com/openstack-k8s-operators/nova-operator/api/nova/v1beta1"
15+ telemetryv1 "github.com/openstack-k8s-operators/telemetry-operator/api/v1beta1"
1016 corev1 "k8s.io/api/core/v1"
1117 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1218 "k8s.io/apimachinery/pkg/runtime"
19+ "k8s.io/utils/ptr"
1320 "sigs.k8s.io/controller-runtime/pkg/client"
1421 "sigs.k8s.io/controller-runtime/pkg/client/fake"
1522
@@ -94,6 +101,125 @@ func TestAPIOverrideTLSSecretNames(t *testing.T) {
94101 }
95102}
96103
104+ // setTLSAPISecrets walks v and sets a unique secretName on both endpoints of
105+ // every tls.APIService found, collecting the names.
106+ func setTLSAPISecrets (v reflect.Value , names * []string ) {
107+ switch v .Kind () {
108+ case reflect .Struct :
109+ if v .Type () == tlsAPIServiceType {
110+ api := v .Addr ().Interface ().(* tls.APIService )
111+ for _ , svc := range []* tls.GenericService {& api .Public , & api .Internal } {
112+ name := fmt .Sprintf ("tls-secret-%d" , len (* names ))
113+ svc .SecretName = & name
114+ * names = append (* names , name )
115+ }
116+ return
117+ }
118+ for i := range v .NumField () {
119+ if v .Type ().Field (i ).IsExported () {
120+ setTLSAPISecrets (v .Field (i ), names )
121+ }
122+ }
123+ case reflect .Pointer :
124+ if v .IsNil () && v .Type ().Elem ().Kind () == reflect .Struct {
125+ v .Set (reflect .New (v .Type ().Elem ()))
126+ }
127+ if ! v .IsNil () {
128+ setTLSAPISecrets (v .Elem (), names )
129+ }
130+ case reflect .Map :
131+ if v .Type ().Elem ().Kind () == reflect .Struct {
132+ v .Set (reflect .MakeMap (v .Type ()))
133+ elem := reflect .New (v .Type ().Elem ()).Elem ()
134+ setTLSAPISecrets (elem , names )
135+ v .SetMapIndex (reflect .ValueOf ("key" ).Convert (v .Type ().Key ()), elem )
136+ }
137+ }
138+ }
139+
140+ // TestTLSServiceSecretNames verifies the tls.api secrets of every service in the
141+ // spec (incl. map values like glance) are collected.
142+ func TestTLSServiceSecretNames (t * testing.T ) {
143+ spec := & corev1beta1.OpenStackControlPlaneSpec {}
144+ if got := tlsServiceSecretNames (spec ); len (got ) != 0 {
145+ t .Fatalf ("expected no secrets for empty spec, got %v" , got )
146+ }
147+
148+ var want []string
149+ setTLSAPISecrets (reflect .ValueOf (spec ).Elem (), & want )
150+ if len (want ) == 0 {
151+ t .Fatal ("found no tls.api fields in the spec" )
152+ }
153+ got := tlsServiceSecretNames (spec )
154+ slices .Sort (got )
155+ slices .Sort (want )
156+ if ! slices .Equal (got , want ) {
157+ t .Errorf ("tls.api secret names not collected\n want: %v\n got: %v" , want , got )
158+ }
159+
160+ // empty secretName is ignored
161+ spec = & corev1beta1.OpenStackControlPlaneSpec {}
162+ spec .Keystone .Template = & keystonev1.KeystoneAPISpecCore {}
163+ spec .Keystone .Template .TLS .API .Public .SecretName = ptr .To ("" )
164+ if got := tlsServiceSecretNames (spec ); len (got ) != 0 {
165+ t .Errorf ("expected empty secretName to be ignored, got %v" , got )
166+ }
167+ }
168+
169+ // TestTLSServiceSecretNamesSimpleTLS verifies the simple TLS sections the operator
170+ // reads as bring-your-own are indexed, and the operator owned ones are not.
171+ func TestTLSServiceSecretNamesSimpleTLS (t * testing.T ) {
172+ spec := & corev1beta1.OpenStackControlPlaneSpec {}
173+ spec .Horizon .Template = & horizonv1.HorizonSpecCore {}
174+ spec .Horizon .Template .TLS .SecretName = ptr .To ("horizon-cert" )
175+ spec .Nova .Template = & novav1.NovaSpecCore {CellTemplates : map [string ]novav1.NovaCellTemplate {"cell1" : {}}}
176+ cell := spec .Nova .Template .CellTemplates ["cell1" ]
177+ cell .NoVNCProxyServiceTemplate .TLS .Service .SecretName = ptr .To ("novnc-cert" )
178+ cell .NoVNCProxyServiceTemplate .TLS .Vencrypt .SecretName = ptr .To ("vencrypt-operator-owned" )
179+ spec .Nova .Template .CellTemplates ["cell1" ] = cell
180+ spec .Telemetry .Template = & telemetryv1.TelemetrySpecCore {}
181+ spec .Telemetry .Template .MetricStorage .PrometheusTLS .SecretName = ptr .To ("prometheus-cert" )
182+ spec .Telemetry .Template .MetricStorage .AlertmanagerTLS .SecretName = ptr .To ("alertmanager-cert" )
183+ spec .Neutron .Template = & neutronv1.NeutronAPISpecCore {}
184+ spec .Neutron .Template .TLS .Ovn .SecretName = ptr .To ("ovn-operator-owned" )
185+
186+ got := tlsServiceSecretNames (spec )
187+ slices .Sort (got )
188+ want := []string {"alertmanager-cert" , "horizon-cert" , "novnc-cert" , "prometheus-cert" }
189+ if ! slices .Equal (got , want ) {
190+ t .Errorf ("want %v, got %v" , want , got )
191+ }
192+ }
193+
194+ // TestFindObjectsForSrcTLSServiceSecret verifies a change of a BYO service cert
195+ // secret enqueues the control plane referencing it.
196+ func TestFindObjectsForSrcTLSServiceSecret (t * testing.T ) {
197+ scheme := runtime .NewScheme ()
198+ if err := corev1beta1 .AddToScheme (scheme ); err != nil {
199+ t .Fatal (err )
200+ }
201+
202+ cp := & corev1beta1.OpenStackControlPlane {ObjectMeta : metav1.ObjectMeta {Name : "cp" , Namespace : "ns" }}
203+ cp .Spec .Keystone .Template = & keystonev1.KeystoneAPISpecCore {}
204+ cp .Spec .Keystone .Template .TLS .API .Internal .SecretName = ptr .To ("byo-cert" )
205+
206+ builder := fake .NewClientBuilder ().WithScheme (scheme ).WithObjects (cp )
207+ for field , indexer := range fieldIndexers {
208+ builder = builder .WithIndex (& corev1beta1.OpenStackControlPlane {}, field , indexer )
209+ }
210+ r := & OpenStackControlPlaneReconciler {Client : builder .Build ()}
211+
212+ secret := func (name string ) client.Object {
213+ return & corev1.Secret {ObjectMeta : metav1.ObjectMeta {Name : name , Namespace : "ns" }}
214+ }
215+ if got := r .findObjectsForSrc (context .Background (), secret ("byo-cert" )); len (got ) != 1 || got [0 ].Name != "cp" {
216+ t .Errorf ("expected control plane to be enqueued, got %v" , got )
217+ }
218+ if got := r .findObjectsForSrc (context .Background (), secret ("other" )); len (got ) != 0 {
219+ t .Errorf ("expected no requests for unrelated secret, got %v" , got )
220+ }
221+ }
222+
97223// TestFindObjectsForSrcAPIOverrideTLSSecret verifies a change of a custom
98224// route cert secret enqueues the control plane referencing it.
99225func TestFindObjectsForSrcAPIOverrideTLSSecret (t * testing.T ) {
0 commit comments