@@ -50,7 +50,7 @@ const (
5050 ControllerName = "initagent-target-controller"
5151)
5252
53- type NewInitControllerFunc func (remoteManager mcmanager.Manager , targetProvider initcontroller.InitTargetProvider , initializer kcpcorev1alpha1.LogicalClusterInitializer ) error
53+ type NewInitControllerFunc func (remoteManager mcmanager.Manager , targetProvider initcontroller.InitTargetsProvider , initializer kcpcorev1alpha1.LogicalClusterInitializer ) error
5454
5555type Reconciler struct {
5656 // Choose to break good practice of never storing a context in a struct,
@@ -65,8 +65,10 @@ type Reconciler struct {
6565 newInitController NewInitControllerFunc
6666
6767 // A map of cancel funcs for the multicluster managers
68- // that we launch for each InitTarget .
68+ // that we launch for each WorkspaceType .
6969 ctrlCancels map [string ]context.CancelCauseFunc
70+ // Tracks which InitTarget names belong to each WorkspaceType key.
71+ ctrlTargets map [string ]map [string ]bool
7072 ctrlLock sync.Mutex
7173}
7274
@@ -86,6 +88,7 @@ func Add(
8688 clusterClient : clusterClient ,
8789 newInitController : newInitController ,
8890 ctrlCancels : map [string ]context.CancelCauseFunc {},
91+ ctrlTargets : map [string ]map [string ]bool {},
8992 ctrlLock : sync.Mutex {},
9093 }
9194
@@ -125,10 +128,17 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
125128func (r * Reconciler ) ensureInitController (ctx context.Context , log * zap.SugaredLogger , target * initializationv1alpha1.InitTarget ) (reconcile.Result , error ) {
126129 key := getInitTargetKey (target )
127130
128- // controller already exists
131+ r . ctrlLock . Lock ()
129132 if _ , exists := r .ctrlCancels [key ]; exists {
133+ // Controller already exists for this WorkspaceType, just track the target.
134+ if r .ctrlTargets [key ] == nil {
135+ r .ctrlTargets [key ] = map [string ]bool {}
136+ }
137+ r.ctrlTargets [key ][target.Name ] = true
138+ r .ctrlLock .Unlock ()
130139 return reconcile.Result {}, nil
131140 }
141+ r .ctrlLock .Unlock ()
132142
133143 ctrlog := log .With ("ctrlkey" , key , "name" , target .Name )
134144
@@ -148,15 +158,21 @@ func (r *Reconciler) ensureInitController(ctx context.Context, log *zap.SugaredL
148158 return reconcile.Result {}, fmt .Errorf ("failed to create multicluster manager: %w" , err )
149159 }
150160
151- if err := r .newInitController (mgr , r .newInitTargetProvider ( target . Name ), initializer ); err != nil {
161+ if err := r .newInitController (mgr , r .newInitTargetsProvider ( key ), initializer ); err != nil {
152162 return reconcile.Result {}, fmt .Errorf ("failed to create init controller: %w" , err )
153163 }
154164
155165 // Use the global app context so this provider is independent of the reconcile
156166 // context, which might get cancelled right after Reconcile() is done.
157167 ctrlCtx , ctrlCancel := context .WithCancelCause (r .ctx )
158168
169+ r .ctrlLock .Lock ()
159170 r .ctrlCancels [key ] = ctrlCancel
171+ if r .ctrlTargets [key ] == nil {
172+ r .ctrlTargets [key ] = map [string ]bool {}
173+ }
174+ r.ctrlTargets [key ][target.Name ] = true
175+ r .ctrlLock .Unlock ()
160176
161177 // cleanup when the context is done
162178 go func () {
@@ -166,6 +182,7 @@ func (r *Reconciler) ensureInitController(ctx context.Context, log *zap.SugaredL
166182 defer r .ctrlLock .Unlock ()
167183
168184 delete (r .ctrlCancels , key )
185+ delete (r .ctrlTargets , key )
169186 }()
170187
171188 // time to start the manager
@@ -181,15 +198,22 @@ func (r *Reconciler) ensureInitController(ctx context.Context, log *zap.SugaredL
181198
182199func (r * Reconciler ) cleanupController (log * zap.SugaredLogger , target * initializationv1alpha1.InitTarget ) error {
183200 key := getInitTargetKey (target )
184- log .Infow ("Stopping init controller…" , "ctrlkey" , key )
201+ log .Infow ("Removing InitTarget from controller…" , "ctrlkey" , key , "target" , target . Name )
185202
186203 r .ctrlLock .Lock ()
187204 defer r .ctrlLock .Unlock ()
188205
189- cancel , ok := r .ctrlCancels [key ]
190- if ok {
191- cancel (errors .New ("controller is no longer needed" ))
192- delete (r .ctrlCancels , key )
206+ if targets , ok := r .ctrlTargets [key ]; ok {
207+ delete (targets , target .Name )
208+ if len (targets ) == 0 {
209+ // Last target removed, stop the controller.
210+ log .Infow ("Stopping init controller (last InitTarget removed)…" , "ctrlkey" , key )
211+ if cancel , ok := r .ctrlCancels [key ]; ok {
212+ cancel (errors .New ("controller is no longer needed" ))
213+ delete (r .ctrlCancels , key )
214+ }
215+ delete (r .ctrlTargets , key )
216+ }
193217 }
194218
195219 return nil
@@ -250,17 +274,32 @@ func (r *Reconciler) createMulticlusterManager(wst *kcptenancyv1alpha1.Workspace
250274 return mgr , nil
251275}
252276
253- func (r * Reconciler ) newInitTargetProvider (name string ) initcontroller.InitTargetProvider {
254- return func (ctx context.Context ) (* initializationv1alpha1.InitTarget , error ) {
255- target := & initializationv1alpha1.InitTarget {}
256- if err := r .localClient .Get (ctx , types.NamespacedName {Name : name }, target ); err != nil {
257- return nil , err
277+ func (r * Reconciler ) newInitTargetsProvider (wstKey string ) initcontroller.InitTargetsProvider {
278+ return func (ctx context.Context ) ([]* initializationv1alpha1.InitTarget , error ) {
279+ r .ctrlLock .Lock ()
280+ targetNames := r .ctrlTargets [wstKey ]
281+ names := make ([]string , 0 , len (targetNames ))
282+ for name := range targetNames {
283+ names = append (names , name )
258284 }
259-
260- return target , nil
285+ r .ctrlLock .Unlock ()
286+
287+ var targets []* initializationv1alpha1.InitTarget
288+ for _ , name := range names {
289+ target := & initializationv1alpha1.InitTarget {}
290+ if err := r .localClient .Get (ctx , types.NamespacedName {Name : name }, target ); err != nil {
291+ if ctrlruntimeclient .IgnoreNotFound (err ) == nil {
292+ continue // target was deleted
293+ }
294+ return nil , err
295+ }
296+ targets = append (targets , target )
297+ }
298+ return targets , nil
261299 }
262300}
263301
264302func getInitTargetKey (target * initializationv1alpha1.InitTarget ) string {
265- return string (target .UID )
303+ ref := target .Spec .WorkspaceTypeReference
304+ return ref .Path + ":" + ref .Name
266305}
0 commit comments