Skip to content

Commit 53dcbb3

Browse files
committed
update labels when claiming
Signed-off-by: furykerry <shouchen.zz@alibaba-inc.com>
1 parent 4aeb62a commit 53dcbb3

13 files changed

Lines changed: 356 additions & 29 deletions

File tree

api/v1alpha1/annotations.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const (
1010
// E2B annotations
1111

1212
const (
13-
E2BPrefix = "e2b." + InternalPrefix
13+
E2BPrefix = "e2b." + InternalPrefix
14+
E2BLabelPrefix = "label:"
1415

1516
AnnotationEnvdAccessToken = E2BPrefix + "envd-access-token"
1617
AnnotationEnvdURL = E2BPrefix + "envd-url"

pkg/controller/sandbox/core/common_inplace_update_handler.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,18 @@ func handleInPlaceUpdateCommon(
4343
) (bool, error) {
4444
logger := handler.GetLogger(ctx, box)
4545

46-
_, hashWithoutImageAndResource := HashSandbox(box)
46+
_, hashWithoutImageResourceMetadata, hashWithoutImageAndResource := HashSandbox(box)
4747
// old Pod do not include Labels[pod-template-hash] and do not support inplace update.
4848
// Check if inplace update is supported
4949
if pod.Labels[agentsv1alpha1.PodLabelTemplateHash] == "" {
5050
return true, nil
5151
// todo, update inplaceupdate condition
52-
} else if box.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources] != hashWithoutImageAndResource {
52+
} else if box.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources] != hashWithoutImageAndResource &&
53+
box.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources] != hashWithoutImageResourceMetadata {
54+
// SandboxHashWithoutImageAndResources computation is switching
55+
// from hashWithoutImageAndResource to hashWithoutImageResourceMetadata
56+
//TODO: drop hashWithoutImageAndResource comparison once we're sure no
57+
// sandboxes with old hashWithoutImageAndResource exists
5358
logger.Info("sandbox hash-without-image-resources changed, and does not permit in-place upgrades",
5459
"old hash", box.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources],
5560
"new hash", hashWithoutImageAndResource)

pkg/controller/sandbox/core/util.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import (
3636
)
3737

3838
// HashSandbox calculates the hash value using sandbox.spec.template
39-
func HashSandbox(box *agentsv1alpha1.Sandbox) (string, string) {
39+
func HashSandbox(box *agentsv1alpha1.Sandbox) (string, string, string) {
4040
// hash using sandbox.spec.template
4141
by, _ := json.Marshal(*box.Spec.Template)
4242
hash := utils.HashData(by)
@@ -55,7 +55,13 @@ func HashSandbox(box *agentsv1alpha1.Sandbox) (string, string) {
5555
}
5656
by, _ = json.Marshal(*tempClone)
5757
hashWithoutImageResources := utils.HashData(by)
58-
return hash, hashWithoutImageResources
58+
59+
// hash using sandbox.spec.template without image, resources and metadata
60+
tempClone.ObjectMeta.Annotations = map[string]string{}
61+
tempClone.ObjectMeta.Labels = map[string]string{}
62+
by, _ = json.Marshal(*tempClone)
63+
hashWithoutImageResourcesMetadata := utils.HashData(by)
64+
return hash, hashWithoutImageResources, hashWithoutImageResourcesMetadata
5965
}
6066

6167
// GeneratePVCName generates a persistent volume claim name from template name and sandbox name

pkg/controller/sandbox/core/util_test.go

Lines changed: 112 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestHashSandbox(t *testing.T) {
3939
expectedHash string
4040
expectedHashWithoutImageResources string
4141
validateDifferentHashes bool
42+
validateDifferentMetadataHashes bool
4243
}{
4344
{
4445
name: "basic sandbox with containers",
@@ -165,11 +166,6 @@ func TestHashSandbox(t *testing.T) {
165166
Spec: agentsv1alpha1.SandboxSpec{
166167
EmbeddedSandboxTemplate: agentsv1alpha1.EmbeddedSandboxTemplate{
167168
Template: &corev1.PodTemplateSpec{
168-
ObjectMeta: metav1.ObjectMeta{
169-
Labels: map[string]string{
170-
"app": "test",
171-
},
172-
},
173169
Spec: corev1.PodSpec{
174170
Containers: []corev1.Container{
175171
{
@@ -195,11 +191,37 @@ func TestHashSandbox(t *testing.T) {
195191
},
196192
validateDifferentHashes: true,
197193
},
194+
{
195+
name: "sandbox with metadata",
196+
sandbox: &agentsv1alpha1.Sandbox{
197+
Spec: agentsv1alpha1.SandboxSpec{
198+
EmbeddedSandboxTemplate: agentsv1alpha1.EmbeddedSandboxTemplate{
199+
Template: &corev1.PodTemplateSpec{
200+
ObjectMeta: metav1.ObjectMeta{
201+
Labels: map[string]string{
202+
"app": "test",
203+
},
204+
},
205+
Spec: corev1.PodSpec{
206+
Containers: []corev1.Container{
207+
{
208+
Name: "test-container",
209+
Image: "nginx:latest",
210+
},
211+
},
212+
},
213+
},
214+
},
215+
},
216+
},
217+
validateDifferentHashes: true,
218+
validateDifferentMetadataHashes: true,
219+
},
198220
}
199221

200222
for _, tt := range tests {
201223
t.Run(tt.name, func(t *testing.T) {
202-
hash, hashWithoutImageResources := HashSandbox(tt.sandbox)
224+
hash, hashWithoutImageResources, hashWithoutImageResourcesMetadata := HashSandbox(tt.sandbox)
203225

204226
// Verify both hashes are not empty
205227
if hash == "" {
@@ -210,12 +232,18 @@ func TestHashSandbox(t *testing.T) {
210232
}
211233

212234
// Verify consistency - same input should always produce same output
213-
hash2, hashWithoutImageResources2 := HashSandbox(tt.sandbox)
235+
hash2, hashWithoutImageResources2, hashWithoutImageResourcesMetadata2 := HashSandbox(tt.sandbox)
214236
if hash != hash2 {
215237
t.Errorf("HashSandbox() is not consistent for hash: got %s, want %s", hash, hash2)
216238
}
217239
if hashWithoutImageResources != hashWithoutImageResources2 {
218-
t.Errorf("HashSandbox() is not consistent for hashWithoutImageResources: got %s, want %s", hashWithoutImageResources, hashWithoutImageResources2)
240+
t.Errorf("HashSandbox() is not consistent for hashWithoutImageResources: got %s, want %s",
241+
hashWithoutImageResources, hashWithoutImageResources2)
242+
}
243+
244+
if hashWithoutImageResourcesMetadata != hashWithoutImageResourcesMetadata2 {
245+
t.Errorf("HashSandbox() is not consistent for hashWithoutImageResourcesMetadata: got %s, want %s",
246+
hashWithoutImageResourcesMetadata, hashWithoutImageResourcesMetadata2)
219247
}
220248

221249
// Validate that hashes have expected format (from HashData function)
@@ -228,10 +256,16 @@ func TestHashSandbox(t *testing.T) {
228256
if hash == hashWithoutImageResources {
229257
t.Errorf("Expected different hashes when image/resources are present, but got same: %s", hash)
230258
}
231-
} else {
232-
if hash != hashWithoutImageResources {
233-
t.Errorf("Expected same hashes when no image/resources differences, but got different: %s vs %s", hash, hashWithoutImageResources)
259+
} else if hash != hashWithoutImageResources {
260+
t.Errorf("Expected same hashes when no image/resources differences, but got different: %s vs %s", hash, hashWithoutImageResources)
261+
}
262+
263+
if tt.validateDifferentMetadataHashes {
264+
if hashWithoutImageResources == hashWithoutImageResourcesMetadata {
265+
t.Errorf("Expected different hashes when sandbox metadata are present, but got same: %s", hashWithoutImageResources)
234266
}
267+
} else if hashWithoutImageResourcesMetadata != hashWithoutImageResources {
268+
t.Errorf("Expected same hashes when no metadata differences, but got different: %s vs %s", hash, hashWithoutImageResources)
235269
}
236270
})
237271
}
@@ -273,8 +307,8 @@ func TestHashSandboxWithDifferentImages(t *testing.T) {
273307
},
274308
}
275309

276-
hash1, hashWithoutImageResources1 := HashSandbox(sandbox1)
277-
hash2, hashWithoutImageResources2 := HashSandbox(sandbox2)
310+
hash1, hashWithoutImageResources1, _ := HashSandbox(sandbox1)
311+
hash2, hashWithoutImageResources2, _ := HashSandbox(sandbox2)
278312

279313
// Full hashes should be different because images are different
280314
if hash1 == hash2 {
@@ -288,6 +322,69 @@ func TestHashSandboxWithDifferentImages(t *testing.T) {
288322
}
289323
}
290324

325+
func TestHashSandboxWithDifferentHash(t *testing.T) {
326+
// Test that changing only image results in different full hash but same hash without image/resources
327+
sandbox1 := &agentsv1alpha1.Sandbox{
328+
Spec: agentsv1alpha1.SandboxSpec{
329+
EmbeddedSandboxTemplate: agentsv1alpha1.EmbeddedSandboxTemplate{
330+
Template: &corev1.PodTemplateSpec{
331+
ObjectMeta: metav1.ObjectMeta{
332+
Labels: map[string]string{
333+
"app": "test",
334+
},
335+
},
336+
Spec: corev1.PodSpec{
337+
Containers: []corev1.Container{
338+
{
339+
Name: "test-container",
340+
Image: "nginx:1.19", // Different image
341+
},
342+
},
343+
},
344+
},
345+
},
346+
},
347+
}
348+
349+
sandbox2 := &agentsv1alpha1.Sandbox{
350+
Spec: agentsv1alpha1.SandboxSpec{
351+
EmbeddedSandboxTemplate: agentsv1alpha1.EmbeddedSandboxTemplate{
352+
Template: &corev1.PodTemplateSpec{
353+
Spec: corev1.PodSpec{
354+
Containers: []corev1.Container{
355+
{
356+
Name: "test-container",
357+
Image: "nginx:1.20", // Different image
358+
},
359+
},
360+
},
361+
},
362+
},
363+
},
364+
}
365+
366+
hash1, hashWithoutImageResources1, hashWithoutImageResourcesMetadata1 := HashSandbox(sandbox1)
367+
hash2, hashWithoutImageResources2, hashWithoutImageResourcesMetadata2 := HashSandbox(sandbox2)
368+
369+
// Full hashes should be different because images are different
370+
if hash1 == hash2 {
371+
t.Errorf("Expected different full hashes for different images, but got same: %s", hash1)
372+
}
373+
374+
// Hashes without images/resources should be the different
375+
if hashWithoutImageResources1 == hashWithoutImageResources2 {
376+
t.Errorf("Expected different hashes without images/resources, but got same: %s",
377+
hashWithoutImageResources1)
378+
}
379+
380+
// Hashes without images/resources/metadata should be the same
381+
if hashWithoutImageResourcesMetadata1 != hashWithoutImageResourcesMetadata2 {
382+
t.Errorf("Expected same hashes without images/resources/metadata, but got different:"+
383+
"%s vs %s",
384+
hashWithoutImageResourcesMetadata1, hashWithoutImageResourcesMetadata2)
385+
}
386+
}
387+
291388
func TestHashSandboxWithDifferentResources(t *testing.T) {
292389
// Test that changing only resources results in different full hash but same hash without image/resources
293390
sandbox1 := &agentsv1alpha1.Sandbox{
@@ -336,8 +433,8 @@ func TestHashSandboxWithDifferentResources(t *testing.T) {
336433
},
337434
}
338435

339-
hash1, hashWithoutImageResources1 := HashSandbox(sandbox1)
340-
hash2, hashWithoutImageResources2 := HashSandbox(sandbox2)
436+
hash1, hashWithoutImageResources1, _ := HashSandbox(sandbox1)
437+
hash2, hashWithoutImageResources2, _ := HashSandbox(sandbox2)
341438

342439
// Full hashes should be different because resources are different
343440
if hash1 == hash2 {

pkg/controller/sandbox/sandbox_controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ func (r *SandboxReconciler) addSandboxFinalizerAndHash(ctx context.Context, box
252252
if originObj.Annotations == nil {
253253
originObj.Annotations = make(map[string]string)
254254
}
255-
_, hashWithoutImageAndResource := core.HashSandbox(box)
256-
originObj.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources] = hashWithoutImageAndResource
255+
_, _, hashNotUpgradable := core.HashSandbox(box)
256+
originObj.Annotations[agentsv1alpha1.SandboxHashWithoutImageAndResources] = hashNotUpgradable
257257
if err := client.IgnoreNotFound(r.Patch(ctx, originObj, patch)); err != nil {
258258
logger.Error(err, "failed to patch sandbox finalizer and hash")
259259
return nil, fmt.Errorf("failed to patch finalizer: %w", err)
@@ -286,7 +286,7 @@ func calculateStatus(args core.EnsureFuncArgs) (*agentsv1alpha1.SandboxStatus, b
286286
pod, box, newStatus := args.Pod, args.Box, args.NewStatus
287287
logger := logf.FromContext(context.TODO()).WithValues("sandbox", klog.KObj(box))
288288

289-
hash, _ := core.HashSandbox(box)
289+
hash, _, _ := core.HashSandbox(box)
290290
newStatus.ObservedGeneration = box.Generation
291291
newStatus.UpdateRevision = hash
292292
if newStatus.Phase == "" {

pkg/sandbox-manager/infra/sandboxcr/claim.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"math/rand/v2"
1111
"net/http"
12+
"strings"
1213
"sync"
1314
"time"
1415

@@ -423,6 +424,17 @@ func modifyPickedSandbox(sbx *Sandbox, lockType infra.LockType, opts infra.Claim
423424
labels[v1alpha1.LabelSandboxIsClaimed] = v1alpha1.True
424425
sbx.SetLabels(labels)
425426

427+
// propagate labels to podtemplates
428+
for k, v := range labels {
429+
if strings.HasPrefix(k, v1alpha1.InternalPrefix) {
430+
continue
431+
}
432+
if sbx.Spec.Template.Labels == nil {
433+
sbx.Spec.Template.Labels = make(map[string]string, len(labels))
434+
}
435+
sbx.Spec.Template.Labels[k] = v
436+
}
437+
426438
annotations := sbx.GetAnnotations()
427439
if annotations == nil {
428440
annotations = make(map[string]string, 1)

pkg/servers/e2b/create.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,12 @@ func (sc *Controller) basicSandboxCreateModifier(ctx context.Context, sbx infra.
257257
annotations[k] = v
258258
}
259259
sbx.SetAnnotations(annotations)
260+
261+
labels := sbx.GetLabels()
262+
for k, v := range request.Extensions.Labels {
263+
labels[k] = v
264+
}
265+
sbx.SetLabels(labels)
260266
}
261267

262268
func (sc *Controller) csiMountOptionsConfigRecord(ctx context.Context, sbx infra.Sandbox, request models.NewSandboxRequest) {

pkg/servers/e2b/models/extensions.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import (
55
"fmt"
66
"net/http"
77
"strconv"
8+
"strings"
89
"time"
910

1011
"github.com/distribution/reference"
12+
"k8s.io/apimachinery/pkg/util/validation"
1113
"k8s.io/utils/ptr"
1214

1315
"github.com/openkruise/agents/api/v1alpha1"
@@ -74,6 +76,34 @@ func (r *NewSandboxRequest) parseCommonExtensions() error {
7476
if r.Extensions.WaitReadySeconds, err = r.parseAndRemoveIntExtension(ExtensionKeyWaitReadyTimeout); err != nil {
7577
return err
7678
}
79+
80+
if err = r.parseExtensionLabels(); err != nil {
81+
return err
82+
}
83+
return nil
84+
}
85+
86+
func (r *NewSandboxRequest) parseExtensionLabels() error {
87+
for k, v := range r.Metadata {
88+
key := strings.TrimPrefix(k, v1alpha1.E2BLabelPrefix)
89+
if key == k {
90+
// not a label
91+
continue
92+
}
93+
if r.Extensions.Labels == nil {
94+
r.Extensions.Labels = make(map[string]string)
95+
}
96+
if len(validation.IsQualifiedName(key)) != 0 {
97+
return fmt.Errorf("invalid label name [%s]", key)
98+
}
99+
100+
if len(validation.IsValidLabelValue(v)) != 0 {
101+
return fmt.Errorf("invalid label value [%s]", v)
102+
}
103+
104+
r.Extensions.Labels[key] = v
105+
delete(r.Metadata, k)
106+
}
77107
return nil
78108
}
79109

0 commit comments

Comments
 (0)