Skip to content

Commit ef2b361

Browse files
committed
record the csi mount info to sandbox annotations
Signed-off-by: jicheng.sk <jicheng.sk@alibaba-inc.com>
1 parent 28d7559 commit ef2b361

File tree

2 files changed

+188
-5
lines changed

2 files changed

+188
-5
lines changed

pkg/servers/e2b/create_test.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package e2b
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"reflect"
7+
"testing"
8+
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
11+
agentsv1alpha1 "github.com/openkruise/agents/api/v1alpha1"
12+
"github.com/openkruise/agents/pkg/sandbox-manager/infra/sandboxcr"
13+
"github.com/openkruise/agents/pkg/servers/e2b/models"
14+
)
15+
16+
// TestCsiMountOptionsConfigRecord tests the csiMountOptionsConfigRecord function
17+
func TestCsiMountOptionsConfigRecord(t *testing.T) {
18+
tests := []struct {
19+
name string
20+
request models.NewSandboxRequest
21+
initialAnnotations map[string]string
22+
expectedAnnotationKey string
23+
expectedAnnotationVal string
24+
shouldSet bool
25+
}{
26+
{
27+
name: "empty mount configs",
28+
request: models.NewSandboxRequest{
29+
Extensions: models.NewSandboxRequestExtension{
30+
CSIMount: models.CSIMountExtension{
31+
MountConfigs: []models.CSIMountConfig{},
32+
},
33+
},
34+
},
35+
shouldSet: false,
36+
},
37+
{
38+
name: "single mount config with all fields",
39+
request: models.NewSandboxRequest{
40+
Extensions: models.NewSandboxRequestExtension{
41+
CSIMount: models.CSIMountExtension{
42+
MountConfigs: []models.CSIMountConfig{
43+
{
44+
MountID: "mount-123",
45+
PvName: "pv-nas-001",
46+
MountPath: "/data",
47+
SubPath: "subdir",
48+
ReadOnly: true,
49+
},
50+
},
51+
},
52+
},
53+
Metadata: map[string]string{
54+
"user-id": "user-456",
55+
},
56+
},
57+
initialAnnotations: map[string]string{},
58+
expectedAnnotationKey: models.ExtensionKeyClaimWithCSIMount_MountConfig,
59+
expectedAnnotationVal: `[{"mountID":"mount-123","pvName":"pv-nas-001","mountPath":"/data","subPath":"subdir","readOnly":true}]`,
60+
shouldSet: true,
61+
},
62+
{
63+
name: "multiple mount configs with optional fields omitted",
64+
request: models.NewSandboxRequest{
65+
Extensions: models.NewSandboxRequestExtension{
66+
CSIMount: models.CSIMountExtension{
67+
MountConfigs: []models.CSIMountConfig{
68+
{
69+
PvName: "pv-nas-001",
70+
MountPath: "/data",
71+
},
72+
{
73+
PvName: "pv-oss-002",
74+
MountPath: "/models",
75+
ReadOnly: true,
76+
},
77+
},
78+
},
79+
},
80+
},
81+
initialAnnotations: map[string]string{"existing-key": "existing-val"},
82+
expectedAnnotationKey: models.ExtensionKeyClaimWithCSIMount_MountConfig,
83+
expectedAnnotationVal: `[{"pvName":"pv-nas-001","mountPath":"/data"},{"pvName":"pv-oss-002","mountPath":"/models","readOnly":true}]`,
84+
shouldSet: true,
85+
},
86+
{
87+
name: "with metadata merging",
88+
request: models.NewSandboxRequest{
89+
Extensions: models.NewSandboxRequestExtension{
90+
CSIMount: models.CSIMountExtension{
91+
MountConfigs: []models.CSIMountConfig{
92+
{
93+
PvName: "pv-test",
94+
MountPath: "/workspace",
95+
},
96+
},
97+
},
98+
},
99+
Metadata: map[string]string{
100+
"team": "ai-lab",
101+
"priority": "high",
102+
},
103+
},
104+
initialAnnotations: map[string]string{
105+
"old-key": "old-val",
106+
},
107+
expectedAnnotationKey: models.ExtensionKeyClaimWithCSIMount_MountConfig,
108+
expectedAnnotationVal: `[{"pvName":"pv-test","mountPath":"/workspace"}]`,
109+
shouldSet: true,
110+
},
111+
}
112+
113+
for _, tt := range tests {
114+
t.Run(tt.name, func(t *testing.T) {
115+
// Create mock sandbox
116+
mockSbx := &sandboxcr.Sandbox{
117+
Sandbox: &agentsv1alpha1.Sandbox{
118+
ObjectMeta: metav1.ObjectMeta{
119+
Name: "test-sandbox",
120+
Namespace: "default",
121+
Annotations: tt.initialAnnotations,
122+
},
123+
},
124+
}
125+
126+
// Create controller instance
127+
ctrl := &Controller{}
128+
129+
// Call the function
130+
ctx := context.Background()
131+
ctrl.csiMountOptionsConfigRecord(ctx, mockSbx, tt.request)
132+
133+
// Verify results
134+
annotations := mockSbx.GetAnnotations()
135+
136+
if !tt.shouldSet {
137+
// Should not set any annotation when mount configs are empty
138+
if len(annotations) != len(tt.initialAnnotations) {
139+
t.Errorf("expected no annotations to be added, got %d", len(annotations))
140+
}
141+
return
142+
}
143+
144+
// Check if expected annotation is set
145+
val, exists := annotations[tt.expectedAnnotationKey]
146+
if !exists {
147+
t.Errorf("expected annotation %q to exist", tt.expectedAnnotationKey)
148+
return
149+
}
150+
151+
// Verify the annotation value (parse JSON for comparison to avoid ordering issues)
152+
var expectedConfigs, actualConfigs []models.CSIMountConfig
153+
if err := json.Unmarshal([]byte(tt.expectedAnnotationVal), &expectedConfigs); err != nil {
154+
t.Fatalf("failed to unmarshal expected value: %v", err)
155+
}
156+
if err := json.Unmarshal([]byte(val), &actualConfigs); err != nil {
157+
t.Fatalf("failed to unmarshal actual value: %v", err)
158+
}
159+
160+
if !reflect.DeepEqual(expectedConfigs, actualConfigs) {
161+
t.Errorf("csi mount config mismatch:\nexpected: %#v\ngot: %#v", expectedConfigs, actualConfigs)
162+
}
163+
164+
// Verify metadata is merged
165+
if tt.request.Metadata != nil {
166+
for k, v := range tt.request.Metadata {
167+
if annotations[k] != v {
168+
t.Errorf("expected metadata %q=%q, got %q", k, v, annotations[k])
169+
}
170+
}
171+
}
172+
173+
// Verify existing annotations are preserved
174+
if tt.initialAnnotations != nil {
175+
for k, v := range tt.initialAnnotations {
176+
if annotations[k] != v {
177+
t.Errorf("expected existing annotation %q=%q, got %q", k, v, annotations[k])
178+
}
179+
}
180+
}
181+
})
182+
}
183+
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package models
22

33
type CSIMountConfig struct {
4-
MountID string `json:"mountID"` // mount id
5-
PvName string `json:"pvName"` // persistent volume name for mounting
6-
MountPath string `json:"mountPath"` // mount target in container to mount the persistent volume
7-
SubPath string `json:"subPath"` // sub path address in persistent volume
8-
ReadOnly bool `json:"readOnly"` // whether to mount the persistent volume as read-only
4+
MountID string `json:"mountID,omitempty"` // mount id
5+
PvName string `json:"pvName"` // persistent volume name for mounting
6+
MountPath string `json:"mountPath"` // mount target in container to mount the persistent volume
7+
SubPath string `json:"subPath,omitempty"` // sub path address in persistent volume
8+
ReadOnly bool `json:"readOnly,omitempty"` // whether to mount the persistent volume as read-only
99
}

0 commit comments

Comments
 (0)