@@ -216,4 +216,295 @@ var _ = Describe("KeystoneAPI Webhook", func() {
216216 "spec.topologyRef.namespace: Invalid value: \" namespace\" : Customizing namespace field is not supported" ),
217217 )
218218 })
219+
220+ When ("ExternalKeystoneAPI validation" , func () {
221+ It ("rejects ExternalKeystoneAPI=true with nil service override" , func () {
222+ spec := GetDefaultKeystoneAPISpec ()
223+ spec ["externalKeystoneAPI" ] = true
224+ // Don't set any override
225+
226+ raw := map [string ]any {
227+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
228+ "kind" : "KeystoneAPI" ,
229+ "metadata" : map [string ]any {
230+ "name" : keystoneAPIName .Name ,
231+ "namespace" : keystoneAPIName .Namespace ,
232+ },
233+ "spec" : spec ,
234+ }
235+
236+ unstructuredObj := & unstructured.Unstructured {Object : raw }
237+ _ , err := controllerutil .CreateOrPatch (
238+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
239+ Expect (err ).To (HaveOccurred ())
240+ // The validation now checks for missing endpoints rather than missing override
241+ Expect (err .Error ()).To (
242+ Or (
243+ ContainSubstring ("external Keystone API requires public endpoint to be defined" ),
244+ ContainSubstring ("external Keystone API requires internal endpoint to be defined" ),
245+ ),
246+ )
247+ })
248+
249+ It ("rejects ExternalKeystoneAPI=true with empty service override" , func () {
250+ spec := GetDefaultKeystoneAPISpec ()
251+ spec ["externalKeystoneAPI" ] = true
252+ spec ["override" ] = map [string ]any {
253+ "service" : map [string ]any {},
254+ }
255+
256+ raw := map [string ]any {
257+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
258+ "kind" : "KeystoneAPI" ,
259+ "metadata" : map [string ]any {
260+ "name" : keystoneAPIName .Name ,
261+ "namespace" : keystoneAPIName .Namespace ,
262+ },
263+ "spec" : spec ,
264+ }
265+
266+ unstructuredObj := & unstructured.Unstructured {Object : raw }
267+ _ , err := controllerutil .CreateOrPatch (
268+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
269+ Expect (err ).To (HaveOccurred ())
270+ // The validation now checks for missing endpoints rather than missing override
271+ Expect (err .Error ()).To (
272+ Or (
273+ ContainSubstring ("external Keystone API requires public endpoint to be defined" ),
274+ ContainSubstring ("external Keystone API requires internal endpoint to be defined" ),
275+ ),
276+ )
277+ })
278+
279+ It ("rejects ExternalKeystoneAPI=true with service override but no endpoints" , func () {
280+ spec := GetDefaultKeystoneAPISpec ()
281+ spec ["externalKeystoneAPI" ] = true
282+ spec ["override" ] = map [string ]any {
283+ "service" : map [string ]any {
284+ "admin" : map [string ]any {
285+ "metadata" : map [string ]any {
286+ "labels" : map [string ]any {
287+ "custom" : "label" ,
288+ },
289+ },
290+ },
291+ },
292+ }
293+
294+ raw := map [string ]any {
295+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
296+ "kind" : "KeystoneAPI" ,
297+ "metadata" : map [string ]any {
298+ "name" : keystoneAPIName .Name ,
299+ "namespace" : keystoneAPIName .Namespace ,
300+ },
301+ "spec" : spec ,
302+ }
303+
304+ unstructuredObj := & unstructured.Unstructured {Object : raw }
305+ _ , err := controllerutil .CreateOrPatch (
306+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
307+ Expect (err ).To (HaveOccurred ())
308+ Expect (err .Error ()).To (
309+ ContainSubstring (
310+ "external Keystone API requires public endpoint to be defined" ),
311+ )
312+ })
313+
314+ It ("rejects ExternalKeystoneAPI=true with missing public endpoint" , func () {
315+ spec := GetDefaultKeystoneAPISpec ()
316+ spec ["externalKeystoneAPI" ] = true
317+ spec ["override" ] = map [string ]any {
318+ "service" : map [string ]any {
319+ "internal" : map [string ]any {
320+ "endpointURL" : "http://internal.keystone.example.com:5000" ,
321+ },
322+ },
323+ }
324+
325+ raw := map [string ]any {
326+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
327+ "kind" : "KeystoneAPI" ,
328+ "metadata" : map [string ]any {
329+ "name" : keystoneAPIName .Name ,
330+ "namespace" : keystoneAPIName .Namespace ,
331+ },
332+ "spec" : spec ,
333+ }
334+
335+ unstructuredObj := & unstructured.Unstructured {Object : raw }
336+ _ , err := controllerutil .CreateOrPatch (
337+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
338+ Expect (err ).To (HaveOccurred ())
339+ Expect (err .Error ()).To (
340+ ContainSubstring (
341+ "external Keystone API requires public endpoint to be defined" ),
342+ )
343+ })
344+
345+ It ("rejects ExternalKeystoneAPI=true with missing internal endpoint" , func () {
346+ spec := GetDefaultKeystoneAPISpec ()
347+ spec ["externalKeystoneAPI" ] = true
348+ spec ["override" ] = map [string ]any {
349+ "service" : map [string ]any {
350+ "public" : map [string ]any {
351+ "endpointURL" : "http://public.keystone.example.com:5000" ,
352+ },
353+ },
354+ }
355+
356+ raw := map [string ]any {
357+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
358+ "kind" : "KeystoneAPI" ,
359+ "metadata" : map [string ]any {
360+ "name" : keystoneAPIName .Name ,
361+ "namespace" : keystoneAPIName .Namespace ,
362+ },
363+ "spec" : spec ,
364+ }
365+
366+ unstructuredObj := & unstructured.Unstructured {Object : raw }
367+ _ , err := controllerutil .CreateOrPatch (
368+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
369+ Expect (err ).To (HaveOccurred ())
370+ Expect (err .Error ()).To (
371+ ContainSubstring (
372+ "external Keystone API requires internal endpoint to be defined" ),
373+ )
374+ })
375+
376+ It ("rejects ExternalKeystoneAPI=true with empty endpointURL" , func () {
377+ spec := GetDefaultKeystoneAPISpec ()
378+ spec ["externalKeystoneAPI" ] = true
379+ spec ["override" ] = map [string ]any {
380+ "service" : map [string ]any {
381+ "public" : map [string ]any {
382+ "endpointURL" : "" ,
383+ },
384+ "internal" : map [string ]any {
385+ "endpointURL" : "http://internal.keystone.example.com:5000" ,
386+ },
387+ },
388+ }
389+
390+ raw := map [string ]any {
391+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
392+ "kind" : "KeystoneAPI" ,
393+ "metadata" : map [string ]any {
394+ "name" : keystoneAPIName .Name ,
395+ "namespace" : keystoneAPIName .Namespace ,
396+ },
397+ "spec" : spec ,
398+ }
399+
400+ unstructuredObj := & unstructured.Unstructured {Object : raw }
401+ _ , err := controllerutil .CreateOrPatch (
402+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
403+ Expect (err ).To (HaveOccurred ())
404+ Expect (err .Error ()).To (
405+ ContainSubstring (
406+ "external Keystone API requires endpointURL to be set for public endpoint" ),
407+ )
408+ })
409+
410+ It ("rejects ExternalKeystoneAPI=true with invalid URL format for public endpoint" , func () {
411+ spec := GetDefaultKeystoneAPISpec ()
412+ spec ["externalKeystoneAPI" ] = true
413+ spec ["override" ] = map [string ]any {
414+ "service" : map [string ]any {
415+ "public" : map [string ]any {
416+ "endpointURL" : "not-a-valid-url" ,
417+ },
418+ "internal" : map [string ]any {
419+ "endpointURL" : "http://internal.keystone.example.com:5000" ,
420+ },
421+ },
422+ }
423+
424+ raw := map [string ]any {
425+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
426+ "kind" : "KeystoneAPI" ,
427+ "metadata" : map [string ]any {
428+ "name" : keystoneAPIName .Name ,
429+ "namespace" : keystoneAPIName .Namespace ,
430+ },
431+ "spec" : spec ,
432+ }
433+
434+ unstructuredObj := & unstructured.Unstructured {Object : raw }
435+ _ , err := controllerutil .CreateOrPatch (
436+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
437+ Expect (err ).To (HaveOccurred ())
438+ Expect (err .Error ()).To (
439+ ContainSubstring (
440+ "URL must include a scheme" ),
441+ )
442+ })
443+
444+ It ("rejects ExternalKeystoneAPI=true with invalid URL format for internal endpoint" , func () {
445+ spec := GetDefaultKeystoneAPISpec ()
446+ spec ["externalKeystoneAPI" ] = true
447+ spec ["override" ] = map [string ]any {
448+ "service" : map [string ]any {
449+ "public" : map [string ]any {
450+ "endpointURL" : "http://public.keystone.example.com:5000" ,
451+ },
452+ "internal" : map [string ]any {
453+ "endpointURL" : "://invalid-url" ,
454+ },
455+ },
456+ }
457+
458+ raw := map [string ]any {
459+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
460+ "kind" : "KeystoneAPI" ,
461+ "metadata" : map [string ]any {
462+ "name" : keystoneAPIName .Name ,
463+ "namespace" : keystoneAPIName .Namespace ,
464+ },
465+ "spec" : spec ,
466+ }
467+
468+ unstructuredObj := & unstructured.Unstructured {Object : raw }
469+ _ , err := controllerutil .CreateOrPatch (
470+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
471+ Expect (err ).To (HaveOccurred ())
472+ Expect (err .Error ()).To (
473+ Or (
474+ ContainSubstring ("invalid URL format" ),
475+ ContainSubstring ("URL must include a scheme" ),
476+ ),
477+ )
478+ })
479+
480+ It ("accepts ExternalKeystoneAPI=true with valid endpoints" , func () {
481+ spec := GetDefaultKeystoneAPISpec ()
482+ spec ["externalKeystoneAPI" ] = true
483+ spec ["override" ] = map [string ]any {
484+ "service" : map [string ]any {
485+ "public" : map [string ]any {
486+ "endpointURL" : "http://public.keystone.example.com:5000" ,
487+ },
488+ "internal" : map [string ]any {
489+ "endpointURL" : "http://internal.keystone.example.com:5000" ,
490+ },
491+ },
492+ }
493+
494+ raw := map [string ]any {
495+ "apiVersion" : "keystone.openstack.org/v1beta1" ,
496+ "kind" : "KeystoneAPI" ,
497+ "metadata" : map [string ]any {
498+ "name" : keystoneAPIName .Name ,
499+ "namespace" : keystoneAPIName .Namespace ,
500+ },
501+ "spec" : spec ,
502+ }
503+
504+ unstructuredObj := & unstructured.Unstructured {Object : raw }
505+ _ , err := controllerutil .CreateOrPatch (
506+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
507+ Expect (err ).NotTo (HaveOccurred ())
508+ })
509+ })
219510})
0 commit comments