@@ -53,6 +53,18 @@ func FromConfig(resultPath, crdKind, controllerOutDir, indexerOutDir, typesPath
5353 return fmt .Errorf ("failed to generate indexers: %w" , err )
5454 }
5555
56+ // Parse reference fields for watch generation
57+ referenceFields , err := ParseReferenceFields (resultPath , crdKind )
58+ if err != nil {
59+ return fmt .Errorf ("failed to parse reference fields: %w" , err )
60+ }
61+
62+ // Group references by target kind
63+ refsByKind := make (map [string ][]ReferenceField )
64+ for _ , ref := range referenceFields {
65+ refsByKind [ref .ReferencedKind ] = append (refsByKind [ref .ReferencedKind ], ref )
66+ }
67+
5668 baseControllerDir := filepath .Join (controllerOutDir , strings .ToLower (resourceName ))
5769
5870 controllerName := resourceName
@@ -66,7 +78,7 @@ func FromConfig(resultPath, crdKind, controllerOutDir, indexerOutDir, typesPath
6678 return fmt .Errorf ("failed to generate controller file: %w" , err )
6779 }
6880
69- if err := generateMainHandlerFile (controllerDir , controllerName , resourceName , typesPath , parsedConfig .Mappings ); err != nil {
81+ if err := generateMainHandlerFile (controllerDir , controllerName , resourceName , typesPath , parsedConfig .Mappings , refsByKind ); err != nil {
7082 return fmt .Errorf ("failed to generate main handler file: %w" , err )
7183 }
7284
@@ -193,7 +205,7 @@ func generateControllerFileWithMultipleVersions(dir, controllerName, resourceNam
193205 return f .Save (fileName )
194206}
195207
196- func generateMainHandlerFile (dir , controllerName , resourceName , typesPath string , mappings []MappingWithConfig ) error {
208+ func generateMainHandlerFile (dir , controllerName , resourceName , typesPath string , mappings []MappingWithConfig , refsByKind map [ string ][] ReferenceField ) error {
197209 atlasResourceName := strings .ToLower (resourceName )
198210 apiPkg := typesPath
199211
@@ -231,13 +243,13 @@ func generateMainHandlerFile(dir, controllerName, resourceName, typesPath string
231243 jen .Return (jen .Id ("selectedHandler" ).Op ("," ).Nil ()),
232244 )
233245
234- generateDelegatingStateHandlers (f , controllerName , resourceName , apiPkg )
246+ generateDelegatingStateHandlers (f , controllerName , resourceName , apiPkg , refsByKind )
235247
236248 fileName := filepath .Join (dir , "handler.go" )
237249 return f .Save (fileName )
238250}
239251
240- func generateDelegatingStateHandlers (f * jen.File , controllerName , resourceName , apiPkg string ) {
252+ func generateDelegatingStateHandlers (f * jen.File , controllerName , resourceName , apiPkg string , refsByKind map [ string ][] ReferenceField ) {
241253 handlers := []string {
242254 "HandleInitial" ,
243255 "HandleImportRequested" ,
@@ -280,19 +292,9 @@ func generateDelegatingStateHandlers(f *jen.File, controllerName, resourceName,
280292 jen .Return (jen .Id ("obj" ), jen .Qual ("sigs.k8s.io/controller-runtime/pkg/builder" , "WithPredicates" ).Call ()),
281293 )
282294
283- f .Comment ("SetupWithManager sets up the controller with the Manager" )
284- f .Func ().Params (jen .Id ("h" ).Op ("*" ).Id (controllerName + "Handler" )).Id ("SetupWithManager" ).Params (
285- jen .Id ("mgr" ).Qual ("sigs.k8s.io/controller-runtime" , "Manager" ),
286- jen .Id ("rec" ).Qual ("sigs.k8s.io/controller-runtime/pkg/reconcile" , "Reconciler" ),
287- jen .Id ("defaultOptions" ).Qual ("sigs.k8s.io/controller-runtime/pkg/controller" , "Options" ),
288- ).Error ().Block (
289- jen .Id ("h" ).Dot ("Client" ).Op ("=" ).Id ("mgr" ).Dot ("GetClient" ).Call (),
290- jen .Return (jen .Qual ("sigs.k8s.io/controller-runtime" , "NewControllerManagedBy" ).Call (jen .Id ("mgr" )).
291- Dot ("Named" ).Call (jen .Lit (resourceName )).
292- Dot ("For" ).Call (jen .Id ("h" ).Dot ("For" ).Call ()).
293- Dot ("WithOptions" ).Call (jen .Id ("defaultOptions" )).
294- Dot ("Complete" ).Call (jen .Id ("rec" ))),
295- )
295+ generateMapperFunctions (f , controllerName , resourceName , apiPkg , refsByKind )
296+
297+ generateSetupWithManager (f , controllerName , resourceName , refsByKind )
296298}
297299
298300func generateVersionHandlerFile (dir , controllerName , resourceName , typesPath string , mapping MappingWithConfig ) error {
@@ -393,3 +395,152 @@ func generateVersionInterfaceMethods(f *jen.File, controllerName, resourceName,
393395 jen .Return (jen .Nil ()),
394396 )
395397}
398+
399+ func generateSetupWithManager (f * jen.File , controllerName , resourceName string , refsByKind map [string ][]ReferenceField ) {
400+ f .Comment ("SetupWithManager sets up the controller with the Manager" )
401+
402+ setupChain := jen .Qual ("sigs.k8s.io/controller-runtime" , "NewControllerManagedBy" ).Call (jen .Id ("mgr" )).
403+ Dot ("Named" ).Call (jen .Lit (resourceName )).
404+ Dot ("For" ).Call (jen .Id ("h" ).Dot ("For" ).Call ())
405+
406+ // Add Watches()
407+ for kind := range refsByKind {
408+ watchedTypeInstance := getWatchedTypeInstance (kind )
409+ mapperFuncName := fmt .Sprintf ("%sFor%sMapFunc" , strings .ToLower (resourceName ), kind )
410+
411+ setupChain = setupChain .
412+ Dot ("Watches" ).Call (
413+ watchedTypeInstance ,
414+ jen .Qual ("sigs.k8s.io/controller-runtime/pkg/handler" , "EnqueueRequestsFromMapFunc" ).Call (
415+ jen .Id ("h" ).Dot (mapperFuncName ).Call (),
416+ ),
417+ jen .Qual ("sigs.k8s.io/controller-runtime/pkg/builder" , "WithPredicates" ).Call (
418+ jen .Qual ("sigs.k8s.io/controller-runtime/pkg/predicate" , "ResourceVersionChangedPredicate" ).Values (),
419+ ),
420+ )
421+ }
422+
423+ setupChain = setupChain .
424+ Dot ("WithOptions" ).Call (jen .Id ("defaultOptions" )).
425+ Dot ("Complete" ).Call (jen .Id ("rec" ))
426+
427+ f .Func ().Params (jen .Id ("h" ).Op ("*" ).Id (controllerName + "Handler" )).Id ("SetupWithManager" ).Params (
428+ jen .Id ("mgr" ).Qual ("sigs.k8s.io/controller-runtime" , "Manager" ),
429+ jen .Id ("rec" ).Qual ("sigs.k8s.io/controller-runtime/pkg/reconcile" , "Reconciler" ),
430+ jen .Id ("defaultOptions" ).Qual ("sigs.k8s.io/controller-runtime/pkg/controller" , "Options" ),
431+ ).Error ().Block (
432+ jen .Id ("h" ).Dot ("Client" ).Op ("=" ).Id ("mgr" ).Dot ("GetClient" ).Call (),
433+ jen .Return (setupChain ),
434+ )
435+ }
436+
437+ func generateMapperFunctions (f * jen.File , controllerName , resourceName , apiPkg string , refsByKind map [string ][]ReferenceField ) {
438+ for kind , refs := range refsByKind {
439+ if len (refs ) == 0 {
440+ continue
441+ }
442+
443+ indexerType := refs [0 ].IndexerType
444+ mapperFuncName := fmt .Sprintf ("%sFor%sMapFunc" , strings .ToLower (resourceName ), kind )
445+
446+ switch indexerType {
447+ case "project" :
448+ generateIndexerBasedMapperFunction (f , controllerName , resourceName , apiPkg , kind , mapperFuncName , "ProjectsIndexMapperFunc" )
449+ case "credentials" :
450+ generateIndexerBasedMapperFunction (f , controllerName , resourceName , apiPkg , kind , mapperFuncName , "CredentialsIndexMapperFunc" )
451+ case "resource" :
452+ generateResourceMapperFunction (f , controllerName , resourceName , apiPkg , kind , mapperFuncName , refs )
453+ }
454+ }
455+ }
456+
457+ // For Group or Secrets
458+ func generateIndexerBasedMapperFunction (f * jen.File , controllerName , resourceName , apiPkg , referencedKind , mapperFuncName , indexerHelperFunc string ) {
459+ indexName := fmt .Sprintf ("%sBy%sIndex" , resourceName , referencedKind )
460+ listTypeName := fmt .Sprintf ("%sList" , resourceName )
461+ requestsFuncName := fmt .Sprintf ("%sRequestsFrom%s" , resourceName , referencedKind )
462+
463+ f .Func ().Params (jen .Id ("h" ).Op ("*" ).Id (controllerName + "Handler" )).Id (mapperFuncName ).Params ().Qual ("sigs.k8s.io/controller-runtime/pkg/handler" , "MapFunc" ).Block (
464+ jen .Return (jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/indexer" , indexerHelperFunc ).Call (
465+ jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/generated/indexer" , indexName ),
466+ jen .Func ().Params ().Op ("*" ).Qual (apiPkg , listTypeName ).Block (
467+ jen .Return (jen .Op ("&" ).Qual (apiPkg , listTypeName ).Values ()),
468+ ),
469+ jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/generated/indexer" , requestsFuncName ),
470+ jen .Id ("h" ).Dot ("Client" ),
471+ jen .Id ("h" ).Dot ("Log" ),
472+ )),
473+ )
474+ }
475+
476+ func generateResourceMapperFunction (f * jen.File , controllerName , resourceName , apiPkg , referencedKind , mapperFuncName string , refs []ReferenceField ) {
477+ indexName := fmt .Sprintf ("%sBy%sIndex" , resourceName , referencedKind )
478+ listTypeName := fmt .Sprintf ("%sList" , resourceName )
479+ watchedType := getWatchedType (referencedKind )
480+
481+ f .Func ().Params (jen .Id ("h" ).Op ("*" ).Id (controllerName + "Handler" )).Id (mapperFuncName ).Params ().Qual ("sigs.k8s.io/controller-runtime/pkg/handler" , "MapFunc" ).Block (
482+ jen .Return (jen .Func ().Params (
483+ jen .Id ("ctx" ).Qual ("context" , "Context" ),
484+ jen .Id ("obj" ).Qual ("sigs.k8s.io/controller-runtime/pkg/client" , "Object" ),
485+ ).Index ().Qual ("sigs.k8s.io/controller-runtime/pkg/reconcile" , "Request" ).Block (
486+
487+ jen .List (jen .Id ("refObj" ), jen .Id ("ok" )).Op (":=" ).Id ("obj" ).Assert (jen .Op ("*" ).Add (watchedType )),
488+ jen .If (jen .Op ("!" ).Id ("ok" )).Block (
489+ jen .Id ("h" ).Dot ("Log" ).Dot ("Warnf" ).Call (
490+ jen .Lit (fmt .Sprintf ("watching %s but got %%T" , referencedKind )),
491+ jen .Id ("obj" ),
492+ ),
493+ jen .Return (jen .Nil ()),
494+ ),
495+
496+ jen .Id ("listOpts" ).Op (":=" ).Op ("&" ).Qual ("sigs.k8s.io/controller-runtime/pkg/client" , "ListOptions" ).Values (jen.Dict {
497+ jen .Id ("FieldSelector" ): jen .Qual ("k8s.io/apimachinery/pkg/fields" , "OneTermEqualSelector" ).Call (
498+ jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/generated/indexer" , indexName ),
499+ jen .Qual ("sigs.k8s.io/controller-runtime/pkg/client" , "ObjectKeyFromObject" ).Call (jen .Id ("refObj" )).Dot ("String" ).Call (),
500+ ),
501+ }),
502+ jen .Id ("list" ).Op (":=" ).Op ("&" ).Qual (apiPkg , listTypeName ).Values (),
503+ jen .Id ("err" ).Op (":=" ).Id ("h" ).Dot ("Client" ).Dot ("List" ).Call (jen .Id ("ctx" ), jen .Id ("list" ), jen .Id ("listOpts" )),
504+ jen .If (jen .Id ("err" ).Op ("!=" ).Nil ()).Block (
505+ jen .Id ("h" ).Dot ("Log" ).Dot ("Errorf" ).Call (
506+ jen .Lit (fmt .Sprintf ("failed to list from indexer %s: %%v" , indexName )),
507+ jen .Id ("err" ),
508+ ),
509+ jen .Return (jen .Nil ()),
510+ ),
511+
512+ jen .Id ("requests" ).Op (":=" ).Make (jen .Index ().Qual ("sigs.k8s.io/controller-runtime/pkg/reconcile" , "Request" ), jen .Lit (0 ), jen .Len (jen .Id ("list" ).Dot ("Items" ))),
513+ jen .For (jen .List (jen .Id ("_" ), jen .Id ("item" )).Op (":=" ).Range ().Id ("list" ).Dot ("Items" )).Block (
514+ jen .Id ("requests" ).Op ("=" ).Append (jen .Id ("requests" ), jen .Qual ("sigs.k8s.io/controller-runtime/pkg/reconcile" , "Request" ).Values (jen.Dict {
515+ jen .Id ("NamespacedName" ): jen .Qual ("k8s.io/apimachinery/pkg/types" , "NamespacedName" ).Values (jen.Dict {
516+ jen .Id ("Name" ): jen .Id ("item" ).Dot ("Name" ),
517+ jen .Id ("Namespace" ): jen .Id ("item" ).Dot ("Namespace" ),
518+ }),
519+ })),
520+ ),
521+ jen .Return (jen .Id ("requests" )),
522+ )),
523+ )
524+ }
525+
526+ func getWatchedTypeInstance (kind string ) * jen.Statement {
527+ switch kind {
528+ case "Secret" :
529+ return jen .Op ("&" ).Qual ("k8s.io/api/core/v1" , "Secret" ).Values ()
530+ case "Group" :
531+ return jen .Op ("&" ).Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1" , "Group" ).Values ()
532+ default :
533+ return jen .Op ("&" ).Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1" , kind ).Values ()
534+ }
535+ }
536+
537+ func getWatchedType (kind string ) * jen.Statement {
538+ switch kind {
539+ case "Secret" :
540+ return jen .Qual ("k8s.io/api/core/v1" , "Secret" )
541+ case "Group" :
542+ return jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1" , "Group" )
543+ default :
544+ return jen .Qual ("github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1" , kind )
545+ }
546+ }
0 commit comments