Skip to content

Commit 3fe6cb7

Browse files
author
柏存
committed
feat: Add sandbox claim label synchronization between Pod and Sandbox
Signed-off-by: 柏存 <guoxiongfeng.gxf@alibaba-inc.com>
1 parent 1390314 commit 3fe6cb7

File tree

7 files changed

+446
-6
lines changed

7 files changed

+446
-6
lines changed

api/v1alpha1/sandboxset_types.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import (
2323
const (
2424
InternalPrefix = "agents.kruise.io/"
2525

26-
LabelSandboxPool = InternalPrefix + "sandbox-pool"
27-
LabelTemplateHash = InternalPrefix + "template-hash"
26+
// LabelSandboxPool identifies which SandboxSet generated the sandbox
27+
LabelSandboxPool = InternalPrefix + "sandbox-pool"
28+
// LabelSandboxIsClaimed indicates whether the sandbox has been claimed by user
29+
LabelSandboxIsClaimed = InternalPrefix + "sandbox-claimed"
30+
LabelTemplateHash = InternalPrefix + "template-hash"
2831

2932
AnnotationLock = InternalPrefix + "lock"
3033
AnnotationOwner = InternalPrefix + "owner"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/google/uuid v1.6.0
1313
github.com/onsi/ginkgo/v2 v2.27.3
1414
github.com/onsi/gomega v1.38.2
15+
github.com/prometheus/client_golang v1.22.0
1516
github.com/spf13/pflag v1.0.6
1617
github.com/stretchr/testify v1.11.1
1718
google.golang.org/grpc v1.75.1
@@ -68,7 +69,6 @@ require (
6869
github.com/pkg/errors v0.9.1 // indirect
6970
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
7071
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
71-
github.com/prometheus/client_golang v1.22.0 // indirect
7272
github.com/prometheus/client_model v0.6.1 // indirect
7373
github.com/prometheus/common v0.62.0 // indirect
7474
github.com/prometheus/procfs v0.16.0 // indirect

pkg/controller/sandbox/core/common_control.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ package core
1818

1919
import (
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+
290420
func (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

Comments
 (0)