@@ -199,6 +199,10 @@ type DynamicController struct {
199199 // queue is the work queue used to process items received via watches.
200200 // The queue is shared between all informers and is used to propagate events to the handlers.
201201 queue workqueue.TypedRateLimitingInterface [ObjectIdentifiers ]
202+
203+ // syncedGVRs receives notifications when parent informers finish syncing.
204+ // This channel is passed to parent LazyInformers and exposed via SyncEvents().
205+ syncedGVRs chan schema.GroupVersionResource
202206}
203207
204208// NewDynamicController creates a new DynamicController.
@@ -221,7 +225,26 @@ func NewDynamicController(
221225 workqueue .NewTypedItemExponentialFailureRateLimiter [ObjectIdentifiers ](config .MinRetryDelay , config .MaxRetryDelay ),
222226 & workqueue.TypedBucketRateLimiter [ObjectIdentifiers ]{Limiter : rate .NewLimiter (rate .Limit (config .RateLimit ), config .BurstLimit )},
223227 ), workqueue.TypedRateLimitingQueueConfig [ObjectIdentifiers ]{Name : "dynamic-controller-queue" }),
228+ syncedGVRs : make (chan schema.GroupVersionResource , 100 ),
229+ }
230+ }
231+
232+ // SyncEvents returns a channel that receives GVRs when their informers finish syncing.
233+ // Consumers should use this to watch for GVR sync completion.
234+ func (dc * DynamicController ) SyncEvents () <- chan schema.GroupVersionResource {
235+ return dc .syncedGVRs
236+ }
237+
238+ // HasSynced returns true if the informer for the given GVR has synced.
239+ func (dc * DynamicController ) HasSynced (gvr schema.GroupVersionResource ) bool {
240+ dc .mu .Lock ()
241+ defer dc .mu .Unlock ()
242+
243+ w , exists := dc .watches [gvr ]
244+ if ! exists {
245+ return false
224246 }
247+ return w .HasSynced ()
225248}
226249
227250// Start starts workers and blocks until ctx.Done().
@@ -434,13 +457,14 @@ func (dc *DynamicController) Deregister(_ context.Context, parent schema.GroupVe
434457
435458func (dc * DynamicController ) ensureWatchLocked (
436459 gvr schema.GroupVersionResource ,
460+ syncedCh chan <- schema.GroupVersionResource ,
437461) * internal.LazyInformer {
438462 if w , ok := dc .watches [gvr ]; ok {
439463 return w
440464 }
441465
442466 // Create per-GVR watch wrapper (informer created lazily on first handler)
443- w := internal .NewLazyInformer (dc .client , gvr , dc .config .ResyncPeriod , nil , dc .log )
467+ w := internal .NewLazyInformer (dc .client , gvr , dc .config .ResyncPeriod , nil , dc .log , syncedCh )
444468 dc .watches [gvr ] = w
445469 return w
446470}
@@ -471,8 +495,8 @@ func (dc *DynamicController) reconcileParentLocked(
471495 return nil
472496 }
473497
474- // ensure watch
475- w := dc .ensureWatchLocked (parent )
498+ // ensure watch - parent gets sync notifications
499+ w := dc .ensureWatchLocked (parent , dc . syncedGVRs )
476500
477501 // create handler if missing
478502 if reg .parentHandlerID == "" {
@@ -533,7 +557,7 @@ func (dc *DynamicController) reconcileChildrenLocked(
533557 if _ , exists := reg .childHandlerIDs [child ]; exists {
534558 continue
535559 }
536- w := dc .ensureWatchLocked (child )
560+ w := dc .ensureWatchLocked (child , nil ) // children don't need sync notifications
537561 childHandlerID := childHandlerID (parent , child )
538562 if err := w .AddHandler (dc .ctx , childHandlerID , dc .handlerForChildGVR (parent , child )); err != nil {
539563 return fmt .Errorf ("add child handler %s: %w" , child , err )
0 commit comments