@@ -109,10 +109,12 @@ var _ = Describe("NemoCustomizer Controller", func() {
109109 Expect (err ).ToNot (HaveOccurred ())
110110 trainingCM := loadConfigMapFromFile (filepath .Join (testDataDir , "training_config.yaml" ))
111111 modelsCM := loadConfigMapFromFile (filepath .Join (testDataDir , "models_config.yaml" ))
112+ modelTargetsCM := loadConfigMapFromFile (filepath .Join (testDataDir , "models_config_targets.yaml" ))
112113
113114 // Register the test ConfigMaps in the cluster
114115 Expect (reconciler .GetClient ().Create (ctx , trainingCM )).To (Succeed ())
115116 Expect (reconciler .GetClient ().Create (ctx , modelsCM )).To (Succeed ())
117+ Expect (reconciler .GetClient ().Create (ctx , modelTargetsCM )).To (Succeed ())
116118
117119 nemoCustomizer = & appsv1alpha1.NemoCustomizer {
118120 ObjectMeta : metav1.ObjectMeta {
@@ -365,6 +367,13 @@ var _ = Describe("NemoCustomizer Controller", func() {
365367 if err := k8sClient .Get (ctx , modelsCMName , modelsCM ); err == nil {
366368 Expect (k8sClient .Delete (ctx , modelsCM )).To (Succeed ())
367369 }
370+
371+ // Delete the models targets config
372+ modelTargetsCMName := types.NamespacedName {Name : "nemo-model-target-config" , Namespace : "default" }
373+ modelTargetsCM := & corev1.ConfigMap {}
374+ if err := k8sClient .Get (ctx , modelTargetsCMName , modelTargetsCM ); err == nil {
375+ Expect (k8sClient .Delete (ctx , modelTargetsCM )).To (Succeed ())
376+ }
368377 })
369378
370379 Describe ("Reconcile" , func () {
@@ -575,6 +584,74 @@ var _ = Describe("NemoCustomizer Controller", func() {
575584 Expect (parsed ).To (HaveKeyWithValue ("mlflow_tracking_url" , "http://mlflow-tracking.nemo.svc.cluster.local:80" ))
576585 })
577586
587+ It ("should create customizer config with model targets and templates" , func () {
588+ namespacedName := types.NamespacedName {Name : nemoCustomizer .Name , Namespace : "default" }
589+
590+ // use models config map with targets and templates
591+ nemoCustomizer .Spec .Models .Name = "nemo-model-config-targets"
592+
593+ err := client .Create (context .TODO (), nemoCustomizer )
594+ Expect (err ).NotTo (HaveOccurred ())
595+ err = client .Create (context .TODO (), secrets )
596+ Expect (err ).NotTo (HaveOccurred ())
597+
598+ result , err := reconciler .Reconcile (context .TODO (), reconcile.Request {NamespacedName : namespacedName })
599+ Expect (err ).NotTo (HaveOccurred ())
600+ Expect (result ).To (Equal (ctrl.Result {}))
601+
602+ err = client .Get (ctx , namespacedName , nemoCustomizer )
603+ Expect (err ).ToNot (HaveOccurred ())
604+ Expect (nemoCustomizer .Finalizers ).To (ContainElement (NemoCustomizerFinalizer ))
605+
606+ // Check that the customizer config map was created
607+ configMap := & corev1.ConfigMap {}
608+ err = client .Get (context .TODO (), types.NamespacedName {
609+ Name : nemoCustomizer .Name ,
610+ Namespace : nemoCustomizer .Namespace ,
611+ }, configMap )
612+ Expect (err ).NotTo (HaveOccurred ())
613+
614+ // Verify metadata
615+ Expect (configMap .Name ).To (Equal (nemoCustomizer .GetName ()))
616+ Expect (configMap .Namespace ).To (Equal (nemoCustomizer .Namespace ))
617+ // Verify key exists
618+ Expect (configMap .Data ).To (HaveKey ("config.yaml" ))
619+ configData := configMap .Data ["config.yaml" ]
620+ Expect (configData ).NotTo (BeEmpty ())
621+
622+ // Verify presence of top-level keys
623+ Expect (configData ).To (ContainSubstring ("training:" ))
624+ Expect (configData ).To (ContainSubstring ("customizationTargets:" ))
625+ Expect (configData ).To (ContainSubstring ("customizationConfigTemplates:" ))
626+ Expect (configData ).ToNot (ContainSubstring ("models:" )) // "models" is deprecated from NMR v25.06
627+ Expect (configData ).To (ContainSubstring ("model_download_jobs:" ))
628+
629+ // Unmarshal the full merged config
630+ var parsed map [string ]interface {}
631+ err = yaml .Unmarshal ([]byte (configData ), & parsed )
632+ Expect (err ).NotTo (HaveOccurred ())
633+
634+ // Validate model download jobs config with new secret params from NMR v25.06
635+ training , ok := parsed ["model_download_jobs" ].(map [string ]interface {})
636+ Expect (ok ).To (BeTrue (), "expected 'training' to be a map" )
637+ Expect (training ).To (HaveKey ("ngcSecretName" ))
638+ Expect (training ).To (HaveKey ("ngcSecretKey" ))
639+
640+ // Validate model targets
641+ modelTargets , ok := parsed ["customizationTargets" ].(map [string ]interface {})
642+ Expect (ok ).To (BeTrue (), "expected 'modelTargets' to be a map" )
643+ Expect (modelTargets ).To (HaveKey ("targets" ))
644+
645+ // Validate model config templates
646+ configTemplates , ok := parsed ["customizationConfigTemplates" ].(map [string ]interface {})
647+ Expect (ok ).To (BeTrue (), "expected 'configTemplates' to be a map" )
648+ Expect (configTemplates ).To (HaveKey ("templates" ))
649+
650+ Expect (parsed ).To (HaveKeyWithValue ("entity_store_url" , "http://nemoentitystore-sample.nemo.svc.cluster.local:8000" ))
651+ Expect (parsed ).To (HaveKeyWithValue ("nemo_data_store_url" , "http://nemodatastore-sample.nemo.svc.cluster.local:8000" ))
652+ Expect (parsed ).To (HaveKeyWithValue ("mlflow_tracking_url" , "http://mlflow-tracking.nemo.svc.cluster.local:80" ))
653+ })
654+
578655 It ("should delete HPA when NemoCustomizer is updated" , func () {
579656 namespacedName := types.NamespacedName {Name : nemoCustomizer .Name , Namespace : "default" }
580657 err := client .Create (context .TODO (), nemoCustomizer )
0 commit comments