Skip to content

[Bug] SandboxClaimReconciler reconcileExpired fails to find adopted sandboxes, causing resource leak on claim expiration #587

@shelwinnn

Description

@shelwinnn

Bug Description

When a SandboxClaim expires with ShutdownPolicy=Retain, the reconcileExpired method attempts to find and delete the associated Sandbox resource. However, it uses client.ObjectKeyFromObject(claim) to look up the Sandbox, which assumes the Sandbox has the same name as the Claim.

https://github.com/kubernetes-sigs/agent-sandbox/blob/main/extensions/controllers/sandboxclaim_controller.go#L277

func (r *SandboxClaimReconciler) reconcileExpired(ctx context.Context, claim *extensionsv1alpha1.SandboxClaim) (*v1alpha1.Sandbox, error) {
    sandbox := &v1alpha1.Sandbox{}
    if err := r.Get(ctx, client.ObjectKeyFromObject(claim), sandbox); err != nil {
        ...
    }
}

Why This Is a Problem

When a Sandbox is adopted from a WarmPool (via adoptSandboxFromCandidates), it retains its original name assigned by the WarmPool controller, which differs from the Claim name. In this case, reconcileExpired will get a NotFound error and return nil, nil (the "Sandbox is gone, life is good" path at line 279), without actually deleting the Sandbox.

This results in orphaned Sandbox resources that persist after their Claim has expired.

Steps to Reproduce

  1. Create a SandboxWarmPool to pre-provision sandboxes.
  2. Create a SandboxClaim with warmPool policy set to adopt from the pool.
  3. Set a short shutdownTime on the Claim so it expires quickly.
  4. Wait for the Claim to expire.
  5. Observe that the adopted Sandbox is not deleted.

Expected Behavior

reconcileExpired should be able to locate and delete the Sandbox regardless of whether it was created directly (same name as Claim) or adopted from a WarmPool (different name).

Suggested Fix

The lookup logic in reconcileExpired should mirror the approach used in getOrCreateSandbox:

  1. First check claim.Status.SandboxStatus.Name — this field records the actual Sandbox name after adoption.
  2. Fall back to listing Sandboxes in the namespace and filtering by ownership (metav1.IsControlledBy).

Alternatively, the existing Sandbox lookup logic in getOrCreateSandbox could be extracted into a shared helper and reused by both methods.

Environment

  • agent-sandbox version: main branch (commit 591c34a)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions