@@ -18,11 +18,13 @@ package core
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "fmt"
2223
2324 corev1 "k8s.io/api/core/v1"
2425 "k8s.io/apimachinery/pkg/api/errors"
2526 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+ "k8s.io/apimachinery/pkg/types"
2628 "k8s.io/client-go/tools/record"
2729 "k8s.io/klog/v2"
2830 "k8s.io/utils/ptr"
@@ -104,6 +106,11 @@ func (r *commonControl) EnsureSandboxUpdated(ctx context.Context, args EnsureFun
104106 PodUID : pod .UID ,
105107 }
106108 logger .Info ("sandbox newStatus" , "newStatus" , utils .DumpJson (newStatus ))
109+
110+ // update pod label
111+ if err := r .handleClaimSandbox (ctx , args ); err != nil {
112+ return err
113+ }
107114 // inplace update
108115 done , err := r .handleInplaceUpdateSandbox (ctx , args )
109116 if err != nil {
@@ -258,6 +265,15 @@ func (r *commonControl) createPod(ctx context.Context, box *agentsv1alpha1.Sandb
258265 }
259266 // todo, when resume, create Pod based on the revision from the paused state.
260267 pod .Labels [agentsv1alpha1 .PodLabelTemplateHash ] = newStatus .UpdateRevision
268+ // Ensure SandboxSet can retrieve these pods using label selector
269+ if box .GetLabels () != nil {
270+ if value , exists := box .GetLabels ()[agentsv1alpha1 .LabelSandboxPool ]; exists {
271+ pod .Labels [agentsv1alpha1 .LabelSandboxPool ] = value
272+ }
273+ if value , exists := box .GetLabels ()[agentsv1alpha1 .LabelSandboxIsClaimed ]; exists {
274+ pod .Labels [agentsv1alpha1 .LabelSandboxIsClaimed ] = value
275+ }
276+ }
261277
262278 volumes := make ([]corev1.Volume , 0 , len (box .Spec .VolumeClaimTemplates ))
263279 for _ , template := range box .Spec .VolumeClaimTemplates {
@@ -287,6 +303,120 @@ func (r *commonControl) createPod(ctx context.Context, box *agentsv1alpha1.Sandb
287303 return pod , nil
288304}
289305
306+ // handleClaimSandbox synchronizes specific labels between sandbox and pod.
307+ // 1. If sandbox has a label but pod doesn't, add it to pod
308+ // 2. If sandbox doesn't have a label but pod does, delete it from pod
309+ // 3. If both have the label but values differ, update pod's value to match sandbox
310+ func (r * commonControl ) handleClaimSandbox (ctx context.Context , args EnsureFuncArgs ) error {
311+ pod , box := args .Pod , args .Box
312+ logger := logf .FromContext (ctx ).WithValues ("sandbox" , klog .KObj (box ))
313+
314+ // If pod doesn't exist, no action needed
315+ if pod == nil {
316+ return nil
317+ }
318+
319+ // Define label keys to sync
320+ labelsToSync := []string {
321+ agentsv1alpha1 .LabelSandboxPool ,
322+ agentsv1alpha1 .LabelSandboxIsClaimed ,
323+ }
324+
325+ // Get labels from both sides (safe nil handling)
326+ boxLabels := box .GetLabels ()
327+ podLabels := pod .GetLabels ()
328+
329+ // Initialize pod labels map if nil
330+ if podLabels == nil {
331+ podLabels = make (map [string ]string )
332+ }
333+
334+ // Collect labels to update (including add and modify)
335+ labelsToUpdate := make (map [string ]string )
336+ // Collect labels to delete
337+ labelsToDelete := []string {}
338+
339+ // Iterate through label keys that need to be synced
340+ for _ , key := range labelsToSync {
341+ boxValue , boxExists := "" , false
342+ if boxLabels != nil {
343+ boxValue , boxExists = boxLabels [key ]
344+ }
345+
346+ podValue , podExists := podLabels [key ]
347+
348+ // Case 1: sandbox has it, pod doesn't -> add to pod
349+ if boxExists && ! podExists {
350+ labelsToUpdate [key ] = boxValue
351+ logger .Info ("label missing in pod, will add" ,
352+ "key" , key ,
353+ "boxValue" , boxValue )
354+ } else if ! boxExists && podExists {
355+ // Case 2: sandbox doesn't have it, pod does -> delete from pod
356+ labelsToDelete = append (labelsToDelete , key )
357+ logger .Info ("label not in box, will delete from pod" ,
358+ "key" , key ,
359+ "podValue" , podValue )
360+ } else if boxExists && podExists && boxValue != podValue {
361+ // Case 3: both have it but values differ -> update pod's value
362+ labelsToUpdate [key ] = boxValue
363+ logger .Info ("label value mismatch, will update pod" ,
364+ "key" , key ,
365+ "boxValue" , boxValue ,
366+ "podValue" , podValue )
367+ }
368+ // Case 4: both don't have it or both have same value -> no action needed
369+ }
370+
371+ // If nothing needs to be changed, return early
372+ if len (labelsToUpdate ) == 0 && len (labelsToDelete ) == 0 {
373+ logger .V (utils .DebugLogLevel ).Info ("pod labels already in sync, no action needed" )
374+ return nil
375+ }
376+
377+ // Build patch data using Strategic Merge Patch:
378+ // - Add/Update: directly set key: value
379+ // - Delete: set key: null
380+ labels := make (map [string ]interface {})
381+
382+ // Add/Update operations
383+ for k , v := range labelsToUpdate {
384+ labels [k ] = v
385+ }
386+
387+ // Delete operations: set to null
388+ for _ , k := range labelsToDelete {
389+ labels [k ] = nil
390+ }
391+
392+ patchData := map [string ]interface {}{
393+ "metadata" : map [string ]interface {}{
394+ "labels" : labels ,
395+ },
396+ }
397+
398+ patchBytes , err := json .Marshal (patchData )
399+ if err != nil {
400+ logger .Error (err , "failed to marshal patch data" )
401+ return err
402+ }
403+
404+ // Execute patch operation
405+ // Note: using null to delete only affects specified keys, won't impact labels managed by other components
406+ podCopy := pod .DeepCopy ()
407+ if err = r .Patch (ctx , podCopy , client .RawPatch (types .StrategicMergePatchType , patchBytes )); err != nil {
408+ logger .Error (err , "failed to patch pod labels" ,
409+ "labelsToUpdate" , labelsToUpdate ,
410+ "labelsToDelete" , labelsToDelete )
411+ return err
412+ }
413+
414+ logger .Info ("successfully synced pod labels" ,
415+ "labelsUpdated" , labelsToUpdate ,
416+ "labelsDeleted" , labelsToDelete )
417+ return nil
418+ }
419+
290420func (r * commonControl ) handleInplaceUpdateSandbox (ctx context.Context , args EnsureFuncArgs ) (bool , error ) {
291421 pod , box , newStatus := args .Pod , args .Box , args .NewStatus
292422 logger := logf .FromContext (ctx ).WithValues ("sandbox" , klog .KObj (box ))
0 commit comments