@@ -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