@@ -20,6 +20,7 @@ import (
2020	"fmt" 
2121	"hash/fnv" 
2222	"reflect" 
23+ 	"time" 
2324
2425	corev1 "k8s.io/api/core/v1" 
2526	k8serrors "k8s.io/apimachinery/pkg/api/errors" 
@@ -33,6 +34,7 @@ import (
3334	"sigs.k8s.io/controller-runtime/pkg/handler" 
3435	"sigs.k8s.io/controller-runtime/pkg/log" 
3536	"sigs.k8s.io/controller-runtime/pkg/predicate" 
37+ 	"sigs.k8s.io/controller-runtime/pkg/reconcile" 
3638
3739	sandboxv1alpha1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" 
3840)
@@ -98,12 +100,15 @@ func (r *SandboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
98100	readyCondition  :=  r .computeReadyCondition (sandbox .Generation , allErrors , svc , pod )
99101	meta .SetStatusCondition (& sandbox .Status .Conditions , readyCondition )
100102
103+ 	result , err  :=  r .reconcileSandboxExpiry (ctx , sandbox )
104+ 	allErrors  =  errors .Join (allErrors , err )
105+ 
101106	// Update status 
102107	err  =  r .updateStatus (ctx , oldStatus , sandbox )
103108	allErrors  =  errors .Join (allErrors , err )
104109
105110	// return errors seen 
106- 	return  ctrl. Result {} , allErrors 
111+ 	return  result , allErrors 
107112}
108113
109114func  (r  * SandboxReconciler ) computeReadyCondition (generation  int64 , err  error , svc  * corev1.Service , pod  * corev1.Pod ) metav1.Condition  {
@@ -280,6 +285,36 @@ func (r *SandboxReconciler) reconcilePod(ctx context.Context, sandbox *sandboxv1
280285	return  pod , nil 
281286}
282287
288+ // reconcileSandboxExpiry will check if a sandbox has expired, and if so, delete it. 
289+ // If the sandbox has not expired, it will requeue the request for the remaining time. 
290+ 
291+ func  (r  * SandboxReconciler ) reconcileSandboxExpiry (ctx  context.Context , sandbox  * sandboxv1alpha1.Sandbox ) (ctrl.Result , error ) {
292+ 	log  :=  log .FromContext (ctx )
293+ 
294+ 	var  expiryTime  time.Time 
295+ 	if  sandbox .Spec .ShutdownTime  ==  nil  {
296+ 		log .Info ("Sandbox ShutdownTime is not set, skipping." )
297+ 		return  reconcile.Result {}, nil 
298+ 	}
299+ 
300+ 	expiryTime  =  sandbox .Spec .ShutdownTime .Time 
301+ 
302+ 	// Calculate remaining time 
303+ 	remainingTime  :=  time .Until (expiryTime )
304+ 	if  remainingTime  <=  0  {
305+ 		log .Info ("Sandbox has expired, deleting" )
306+ 		if  err  :=  r .Delete (ctx , sandbox ); err  !=  nil  {
307+ 			return  ctrl.Result {}, fmt .Errorf ("failed to delete sandbox: %w" , err )
308+ 		}
309+ 		return  ctrl.Result {}, nil 
310+ 	}
311+ 
312+ 	requeueAfter  :=  max (remainingTime / 2 , 2 * time .Second ) // Requeue at most every 2 seconds 
313+ 	log .Info ("Requeuing sandbox for shutdown" , "remaining time" , remainingTime , "requeue after" , requeueAfter ,
314+ 		"expiry time" , expiryTime )
315+ 	return  reconcile.Result {RequeueAfter : requeueAfter }, nil 
316+ }
317+ 
283318// SetupWithManager sets up the controller with the Manager. 
284319func  (r  * SandboxReconciler ) SetupWithManager (mgr  ctrl.Manager ) error  {
285320	labelSelectorPredicate , err  :=  predicate .LabelSelectorPredicate (metav1.LabelSelector {
0 commit comments