@@ -17,6 +17,7 @@ import (
1717 appsv1 "k8s.io/api/apps/v1"
1818 apierrors "k8s.io/apimachinery/pkg/api/errors"
1919 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+ "k8s.io/apimachinery/pkg/fields"
2021 "k8s.io/apimachinery/pkg/runtime"
2122 "k8s.io/apimachinery/pkg/types"
2223 ctrl "sigs.k8s.io/controller-runtime"
@@ -38,6 +39,10 @@ const (
3839 buildIDLabel = "temporal.io/build-id"
3940 // TemporalWorkerDeploymentFinalizer is the finalizer used to ensure proper cleanup of resources
4041 TemporalWorkerDeploymentFinalizer = "temporal.io/temporal-worker-deployment-finalizer"
42+
43+ // Cleanup timeout and polling constants
44+ cleanupTimeout = 2 * time .Minute
45+ cleanupPollInterval = 5 * time .Second
4146)
4247
4348// TemporalWorkerDeploymentReconciler reconciles a TemporalWorkerDeployment object
@@ -238,18 +243,29 @@ func (r *TemporalWorkerDeploymentReconciler) handleDeletion(ctx context.Context,
238243func (r * TemporalWorkerDeploymentReconciler ) cleanupManagedResources (ctx context.Context , l logr.Logger , workerDeploy * temporaliov1alpha1.TemporalWorkerDeployment ) error {
239244 l .Info ("Cleaning up managed resources" )
240245
241- // List all deployments owned by this TemporalWorkerDeployment
242- deploymentList := & appsv1. DeploymentList {}
246+ // Try to use field selector for efficient querying of owned deployments
247+ // Fall back to listing all deployments if field selector is not available (e.g., in tests)
243248 listOpts := & client.ListOptions {
244- Namespace : workerDeploy .Namespace ,
249+ Namespace : workerDeploy .Namespace ,
250+ FieldSelector : fields .OneTermEqualSelector (deployOwnerKey , workerDeploy .Name ),
245251 }
246252
247- if err := r .List (ctx , deploymentList , listOpts ); err != nil {
248- return fmt .Errorf ("failed to list deployments: %w" , err )
253+ deploymentList := & appsv1.DeploymentList {}
254+ err := r .List (ctx , deploymentList , listOpts )
255+ if err != nil {
256+ // If field selector fails (common in tests), fall back to listing all deployments
257+ l .Info ("Field selector not available, falling back to listing all deployments" , "error" , err .Error ())
258+ listOpts = & client.ListOptions {
259+ Namespace : workerDeploy .Namespace ,
260+ }
261+ if err := r .List (ctx , deploymentList , listOpts ); err != nil {
262+ return fmt .Errorf ("failed to list deployments: %w" , err )
263+ }
249264 }
250265
251- // Filter deployments owned by this TemporalWorkerDeployment and delete them
266+ // Delete all owned deployments
252267 for _ , deployment := range deploymentList .Items {
268+ // Check ownership for all deployments when not using field selector
253269 if r .isOwnedByWorkerDeployment (& deployment , workerDeploy ) {
254270 l .Info ("Deleting managed deployment" , "deployment" , deployment .Name )
255271 if err := r .Delete (ctx , & deployment ); err != nil && ! apierrors .IsNotFound (err ) {
@@ -258,29 +274,64 @@ func (r *TemporalWorkerDeploymentReconciler) cleanupManagedResources(ctx context
258274 }
259275 }
260276
261- // Wait for all owned deployments to be deleted
262- for _ , deployment := range deploymentList .Items {
263- if r .isOwnedByWorkerDeployment (& deployment , workerDeploy ) {
264- // Check if deployment still exists
265- currentDeployment := & appsv1.Deployment {}
266- err := r .Get (ctx , types.NamespacedName {
267- Namespace : deployment .Namespace ,
268- Name : deployment .Name ,
269- }, currentDeployment )
270-
271- if err == nil {
272- // Deployment still exists, requeue to wait for deletion
273- l .Info ("Waiting for deployment to be deleted" , "deployment" , deployment .Name )
274- return fmt .Errorf ("still waiting for deployment %s to be deleted" , deployment .Name )
275- } else if ! apierrors .IsNotFound (err ) {
276- return fmt .Errorf ("failed to check deployment status %s: %w" , deployment .Name , err )
277+ // Wait for all owned deployments to be deleted with proper polling
278+ return r .waitForOwnedDeploymentsToBeDeleted (ctx , l , workerDeploy )
279+ }
280+
281+ // waitForOwnedDeploymentsToBeDeleted waits for all owned deployments to be deleted with proper polling and timeout
282+ func (r * TemporalWorkerDeploymentReconciler ) waitForOwnedDeploymentsToBeDeleted (ctx context.Context , l logr.Logger , workerDeploy * temporaliov1alpha1.TemporalWorkerDeployment ) error {
283+ // Create a timeout context for cleanup operations
284+ cleanupCtx , cancel := context .WithTimeout (ctx , cleanupTimeout )
285+ defer cancel ()
286+
287+ ticker := time .NewTicker (cleanupPollInterval )
288+ defer ticker .Stop ()
289+
290+ l .Info ("Waiting for owned deployments to be deleted" , "timeout" , cleanupTimeout )
291+
292+ for {
293+ select {
294+ case <- cleanupCtx .Done ():
295+ if cleanupCtx .Err () == context .DeadlineExceeded {
296+ return fmt .Errorf ("timeout waiting for deployments to be deleted after %v" , cleanupTimeout )
297+ }
298+ return fmt .Errorf ("context cancelled while waiting for deployments to be deleted: %w" , cleanupCtx .Err ())
299+
300+ case <- ticker .C :
301+ // Try to use field selector for efficient querying, with fallback
302+ listOpts := & client.ListOptions {
303+ Namespace : workerDeploy .Namespace ,
304+ FieldSelector : fields .OneTermEqualSelector (deployOwnerKey , workerDeploy .Name ),
305+ }
306+
307+ deploymentList := & appsv1.DeploymentList {}
308+ err := r .List (cleanupCtx , deploymentList , listOpts )
309+ if err != nil {
310+ // If field selector fails (common in tests), fall back to listing all deployments
311+ listOpts = & client.ListOptions {
312+ Namespace : workerDeploy .Namespace ,
313+ }
314+ if err := r .List (cleanupCtx , deploymentList , listOpts ); err != nil {
315+ return fmt .Errorf ("failed to list deployments during cleanup: %w" , err )
316+ }
317+ }
318+
319+ // Check if any owned deployments still exist
320+ hasOwnedDeployments := false
321+ for _ , deployment := range deploymentList .Items {
322+ if r .isOwnedByWorkerDeployment (& deployment , workerDeploy ) {
323+ hasOwnedDeployments = true
324+ l .Info ("Still waiting for deployment to be deleted" , "deployment" , deployment .Name )
325+ break
326+ }
327+ }
328+
329+ if ! hasOwnedDeployments {
330+ l .Info ("All owned deployments have been deleted" )
331+ return nil
277332 }
278- // IsNotFound error means deployment was successfully deleted
279333 }
280334 }
281-
282- l .Info ("All managed resources have been cleaned up" )
283- return nil
284335}
285336
286337// isOwnedByWorkerDeployment checks if a deployment is owned by the given TemporalWorkerDeployment
0 commit comments