@@ -39,6 +39,7 @@ import (
3939 "sigs.k8s.io/controller-runtime/pkg/client"
4040 "sigs.k8s.io/controller-runtime/pkg/reconcile"
4141
42+ infrav1alpha1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha1"
4243 infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1beta1"
4344 "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking"
4445 "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope"
@@ -148,6 +149,134 @@ var _ = Describe("OpenStackCluster controller", func() {
148149 framework .DeleteNamespace (ctx , input )
149150 })
150151
152+ It ("should create OpenStackClusterIdentity (CRD present)" , func () {
153+ err := k8sClient .Create (ctx , testCluster )
154+ Expect (err ).To (BeNil ())
155+ err = k8sClient .Create (ctx , capiCluster )
156+ Expect (err ).To (BeNil ())
157+
158+ id := & infrav1alpha1.OpenStackClusterIdentity {
159+ ObjectMeta : metav1.ObjectMeta {
160+ Name : fmt .Sprintf ("id-%d" , GinkgoRandomSeed ()),
161+ },
162+ Spec : infrav1alpha1.OpenStackClusterIdentitySpec {
163+ SecretRef : infrav1alpha1.OpenStackCredentialSecretReference {
164+ Name : "creds" ,
165+ Namespace : "capo-system" ,
166+ },
167+ },
168+ }
169+ Expect (k8sClient .Create (ctx , id )).To (Succeed ())
170+
171+ // Cleanup cluster-scoped resource since it won't be deleted with namespace
172+ DeferCleanup (func () {
173+ Expect (k8sClient .Delete (ctx , id )).To (Succeed ())
174+ })
175+ })
176+
177+ It ("should successfully create OpenStackCluster with valid identityRef" , func () {
178+ err := k8sClient .Create (ctx , testCluster )
179+ Expect (err ).To (BeNil ())
180+ err = k8sClient .Create (ctx , capiCluster )
181+ Expect (err ).To (BeNil ())
182+
183+ c := & infrav1.OpenStackCluster {
184+ ObjectMeta : metav1.ObjectMeta {
185+ Name : "cluster-valid-identity" ,
186+ Namespace : testNamespace ,
187+ },
188+ Spec : infrav1.OpenStackClusterSpec {
189+ IdentityRef : infrav1.OpenStackIdentityReference {
190+ Name : "creds" ,
191+ CloudName : "openstack" ,
192+ // Type should default to "Secret"
193+ },
194+ },
195+ }
196+ err = k8sClient .Create (ctx , c )
197+ Expect (err ).To (Succeed ())
198+
199+ // Verify the object was created and Type was defaulted
200+ created := & infrav1.OpenStackCluster {}
201+ err = k8sClient .Get (ctx , client.ObjectKey {Name : c .Name , Namespace : c .Namespace }, created )
202+ Expect (err ).To (Succeed ())
203+ Expect (created .Spec .IdentityRef .Type ).To (Equal ("Secret" ))
204+ Expect (created .Spec .IdentityRef .Name ).To (Equal ("creds" ))
205+ Expect (created .Spec .IdentityRef .CloudName ).To (Equal ("openstack" ))
206+ })
207+
208+ It ("should successfully create OpenStackCluster with ClusterIdentity type" , func () {
209+ err := k8sClient .Create (ctx , testCluster )
210+ Expect (err ).To (BeNil ())
211+ err = k8sClient .Create (ctx , capiCluster )
212+ Expect (err ).To (BeNil ())
213+
214+ c := & infrav1.OpenStackCluster {
215+ ObjectMeta : metav1.ObjectMeta {
216+ Name : "cluster-clusteridentity-type" ,
217+ Namespace : testNamespace ,
218+ },
219+ Spec : infrav1.OpenStackClusterSpec {
220+ IdentityRef : infrav1.OpenStackIdentityReference {
221+ Type : "ClusterIdentity" ,
222+ Name : "global-creds" ,
223+ CloudName : "openstack" ,
224+ Region : "RegionOne" ,
225+ },
226+ },
227+ }
228+ err = k8sClient .Create (ctx , c )
229+ Expect (err ).To (Succeed ())
230+
231+ // Verify all fields are preserved
232+ created := & infrav1.OpenStackCluster {}
233+ err = k8sClient .Get (ctx , client.ObjectKey {Name : c .Name , Namespace : c .Namespace }, created )
234+ Expect (err ).To (Succeed ())
235+ Expect (created .Spec .IdentityRef .Type ).To (Equal ("ClusterIdentity" ))
236+ Expect (created .Spec .IdentityRef .Name ).To (Equal ("global-creds" ))
237+ Expect (created .Spec .IdentityRef .CloudName ).To (Equal ("openstack" ))
238+ Expect (created .Spec .IdentityRef .Region ).To (Equal ("RegionOne" ))
239+ })
240+
241+ It ("should accept cluster and default identityRef.type to Secret" , func () {
242+ testCluster .Spec = infrav1.OpenStackClusterSpec {
243+ IdentityRef : infrav1.OpenStackIdentityReference {
244+ Name : "creds" ,
245+ CloudName : "openstack" ,
246+ // Type omitted -> should default to Secret
247+ },
248+ }
249+ err := k8sClient .Create (ctx , testCluster )
250+ Expect (err ).To (BeNil ())
251+ err = k8sClient .Create (ctx , capiCluster )
252+ Expect (err ).To (BeNil ())
253+
254+ fetched := & infrav1.OpenStackCluster {}
255+ Expect (k8sClient .Get (ctx , types.NamespacedName {Name : testClusterName , Namespace : testNamespace }, fetched )).To (Succeed ())
256+ Expect (fetched .Spec .IdentityRef .Type ).To (Equal ("Secret" ))
257+ })
258+
259+ It ("should reject updates that modify identityRef.region (immutable)" , func () {
260+ testCluster .Spec = infrav1.OpenStackClusterSpec {
261+ IdentityRef : infrav1.OpenStackIdentityReference {
262+ Type : "Secret" ,
263+ Name : "creds" ,
264+ CloudName : "openstack" ,
265+ Region : "RegionOne" ,
266+ },
267+ }
268+ err := k8sClient .Create (ctx , testCluster )
269+ Expect (err ).To (BeNil ())
270+ err = k8sClient .Create (ctx , capiCluster )
271+ Expect (err ).To (BeNil ())
272+
273+ // Try to update region
274+ fetched := & infrav1.OpenStackCluster {}
275+ Expect (k8sClient .Get (ctx , types.NamespacedName {Name : testClusterName , Namespace : testNamespace }, fetched )).To (Succeed ())
276+ fetched .Spec .IdentityRef .Region = "RegionTwo"
277+ Expect (k8sClient .Update (ctx , fetched )).ToNot (Succeed ())
278+ })
279+
151280 It ("should do nothing when owner is missing" , func () {
152281 testCluster .SetName ("missing-owner" )
153282 testCluster .SetOwnerReferences ([]metav1.OwnerReference {})
0 commit comments