1
16
17 package state
18
19 import (
20 "os"
21 "reflect"
22 "strings"
23 "testing"
24
25 "github.com/stretchr/testify/require"
26 "k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
27 "k8s.io/kubernetes/pkg/kubelet/cm/containermap"
28 testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
29 "k8s.io/utils/cpuset"
30 )
31
32 const testingCheckpoint = "cpumanager_checkpoint_test"
33
34 func TestCheckpointStateRestore(t *testing.T) {
35 testCases := []struct {
36 description string
37 checkpointContent string
38 policyName string
39 initialContainers containermap.ContainerMap
40 expectedError string
41 expectedState *stateMemory
42 }{
43 {
44 "Restore non-existing checkpoint",
45 "",
46 "none",
47 containermap.ContainerMap{},
48 "",
49 &stateMemory{},
50 },
51 {
52 "Restore default cpu set",
53 `{
54 "policyName": "none",
55 "defaultCPUSet": "4-6",
56 "entries": {},
57 "checksum": 354655845
58 }`,
59 "none",
60 containermap.ContainerMap{},
61 "",
62 &stateMemory{
63 defaultCPUSet: cpuset.New(4, 5, 6),
64 },
65 },
66 {
67 "Restore valid checkpoint",
68 `{
69 "policyName": "none",
70 "defaultCPUSet": "1-3",
71 "entries": {
72 "pod": {
73 "container1": "4-6",
74 "container2": "1-3"
75 }
76 },
77 "checksum": 3610638499
78 }`,
79 "none",
80 containermap.ContainerMap{},
81 "",
82 &stateMemory{
83 assignments: ContainerCPUAssignments{
84 "pod": map[string]cpuset.CPUSet{
85 "container1": cpuset.New(4, 5, 6),
86 "container2": cpuset.New(1, 2, 3),
87 },
88 },
89 defaultCPUSet: cpuset.New(1, 2, 3),
90 },
91 },
92 {
93 "Restore checkpoint with invalid checksum",
94 `{
95 "policyName": "none",
96 "defaultCPUSet": "4-6",
97 "entries": {},
98 "checksum": 1337
99 }`,
100 "none",
101 containermap.ContainerMap{},
102 "checkpoint is corrupted",
103 &stateMemory{},
104 },
105 {
106 "Restore checkpoint with invalid JSON",
107 `{`,
108 "none",
109 containermap.ContainerMap{},
110 "unexpected end of JSON input",
111 &stateMemory{},
112 },
113 {
114 "Restore checkpoint with invalid policy name",
115 `{
116 "policyName": "other",
117 "defaultCPUSet": "1-3",
118 "entries": {},
119 "checksum": 1394507217
120 }`,
121 "none",
122 containermap.ContainerMap{},
123 `configured policy "none" differs from state checkpoint policy "other"`,
124 &stateMemory{},
125 },
126 {
127 "Restore checkpoint with unparsable default cpu set",
128 `{
129 "policyName": "none",
130 "defaultCPUSet": "1.3",
131 "entries": {},
132 "checksum": 3021697696
133 }`,
134 "none",
135 containermap.ContainerMap{},
136 `could not parse default cpu set "1.3": strconv.Atoi: parsing "1.3": invalid syntax`,
137 &stateMemory{},
138 },
139 {
140 "Restore checkpoint with unparsable assignment entry",
141 `{
142 "policyName": "none",
143 "defaultCPUSet": "1-3",
144 "entries": {
145 "pod": {
146 "container1": "4-6",
147 "container2": "asd"
148 }
149 },
150 "checksum": 962272150
151 }`,
152 "none",
153 containermap.ContainerMap{},
154 `could not parse cpuset "asd" for container "container2" in pod "pod": strconv.Atoi: parsing "asd": invalid syntax`,
155 &stateMemory{},
156 },
157 {
158 "Restore checkpoint from checkpoint with v1 checksum",
159 `{
160 "policyName": "none",
161 "defaultCPUSet": "1-3",
162 "checksum": 1694838852
163 }`,
164 "none",
165 containermap.ContainerMap{},
166 "",
167 &stateMemory{
168 defaultCPUSet: cpuset.New(1, 2, 3),
169 },
170 },
171 {
172 "Restore checkpoint with migration",
173 `{
174 "policyName": "none",
175 "defaultCPUSet": "1-3",
176 "entries": {
177 "containerID1": "4-6",
178 "containerID2": "1-3"
179 },
180 "checksum": 3680390589
181 }`,
182 "none",
183 func() containermap.ContainerMap {
184 cm := containermap.NewContainerMap()
185 cm.Add("pod", "container1", "containerID1")
186 cm.Add("pod", "container2", "containerID2")
187 return cm
188 }(),
189 "",
190 &stateMemory{
191 assignments: ContainerCPUAssignments{
192 "pod": map[string]cpuset.CPUSet{
193 "container1": cpuset.New(4, 5, 6),
194 "container2": cpuset.New(1, 2, 3),
195 },
196 },
197 defaultCPUSet: cpuset.New(1, 2, 3),
198 },
199 },
200 }
201
202
203 testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
204 require.NoError(t, err)
205 defer os.RemoveAll(testingDir)
206
207 cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
208 require.NoErrorf(t, err, "could not create testing checkpoint manager: %v", err)
209
210 for _, tc := range testCases {
211 t.Run(tc.description, func(t *testing.T) {
212
213 cpm.RemoveCheckpoint(testingCheckpoint)
214
215
216 if strings.TrimSpace(tc.checkpointContent) != "" {
217 checkpoint := &testutil.MockCheckpoint{Content: tc.checkpointContent}
218 err = cpm.CreateCheckpoint(testingCheckpoint, checkpoint)
219 require.NoErrorf(t, err, "could not create testing checkpoint: %v", err)
220 }
221
222 restoredState, err := NewCheckpointState(testingDir, testingCheckpoint, tc.policyName, tc.initialContainers)
223 if strings.TrimSpace(tc.expectedError) == "" {
224 require.NoError(t, err)
225 } else {
226 require.Error(t, err)
227 require.Contains(t, err.Error(), "could not restore state from checkpoint")
228 require.Contains(t, err.Error(), tc.expectedError)
229 return
230 }
231
232
233 AssertStateEqual(t, restoredState, tc.expectedState)
234 })
235 }
236 }
237
238 func TestCheckpointStateStore(t *testing.T) {
239 testCases := []struct {
240 description string
241 expectedState *stateMemory
242 }{
243 {
244 "Store default cpu set",
245 &stateMemory{defaultCPUSet: cpuset.New(1, 2, 3)},
246 },
247 {
248 "Store assignments",
249 &stateMemory{
250 assignments: map[string]map[string]cpuset.CPUSet{
251 "pod": {
252 "container1": cpuset.New(1, 5, 8),
253 },
254 },
255 },
256 },
257 }
258
259
260 testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
261 if err != nil {
262 t.Fatal(err)
263 }
264 defer os.RemoveAll(testingDir)
265
266 cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
267 if err != nil {
268 t.Fatalf("could not create testing checkpoint manager: %v", err)
269 }
270
271 for _, tc := range testCases {
272 t.Run(tc.description, func(t *testing.T) {
273
274 cpm.RemoveCheckpoint(testingCheckpoint)
275
276 cs1, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
277 if err != nil {
278 t.Fatalf("could not create testing checkpointState instance: %v", err)
279 }
280
281
282 cs1.SetDefaultCPUSet(tc.expectedState.defaultCPUSet)
283 cs1.SetCPUAssignments(tc.expectedState.assignments)
284
285
286 cs2, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
287 if err != nil {
288 t.Fatalf("could not create testing checkpointState instance: %v", err)
289 }
290
291 AssertStateEqual(t, cs2, tc.expectedState)
292 })
293 }
294 }
295
296 func TestCheckpointStateHelpers(t *testing.T) {
297 testCases := []struct {
298 description string
299 defaultCPUset cpuset.CPUSet
300 assignments map[string]map[string]cpuset.CPUSet
301 }{
302 {
303 description: "One container",
304 defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
305 assignments: map[string]map[string]cpuset.CPUSet{
306 "pod": {
307 "c1": cpuset.New(0, 1),
308 },
309 },
310 },
311 {
312 description: "Two containers",
313 defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
314 assignments: map[string]map[string]cpuset.CPUSet{
315 "pod": {
316 "c1": cpuset.New(0, 1),
317 "c2": cpuset.New(2, 3, 4, 5),
318 },
319 },
320 },
321 {
322 description: "Container without assigned cpus",
323 defaultCPUset: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8),
324 assignments: map[string]map[string]cpuset.CPUSet{
325 "pod": {
326 "c1": cpuset.New(),
327 },
328 },
329 },
330 }
331
332
333 testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
334 if err != nil {
335 t.Fatal(err)
336 }
337 defer os.RemoveAll(testingDir)
338
339 cpm, err := checkpointmanager.NewCheckpointManager(testingDir)
340 if err != nil {
341 t.Fatalf("could not create testing checkpoint manager: %v", err)
342 }
343
344 for _, tc := range testCases {
345 t.Run(tc.description, func(t *testing.T) {
346
347 cpm.RemoveCheckpoint(testingCheckpoint)
348
349 state, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
350 if err != nil {
351 t.Fatalf("could not create testing checkpointState instance: %v", err)
352 }
353 state.SetDefaultCPUSet(tc.defaultCPUset)
354
355 for pod := range tc.assignments {
356 for container, set := range tc.assignments[pod] {
357 state.SetCPUSet(pod, container, set)
358 if cpus, _ := state.GetCPUSet(pod, container); !cpus.Equals(set) {
359 t.Fatalf("state inconsistent, got %q instead of %q", set, cpus)
360 }
361
362 state.Delete(pod, container)
363 if _, ok := state.GetCPUSet(pod, container); ok {
364 t.Fatal("deleted container still existing in state")
365 }
366 }
367 }
368 })
369 }
370 }
371
372 func TestCheckpointStateClear(t *testing.T) {
373 testCases := []struct {
374 description string
375 defaultCPUset cpuset.CPUSet
376 assignments map[string]map[string]cpuset.CPUSet
377 }{
378 {
379 "Valid state",
380 cpuset.New(1, 5, 10),
381 map[string]map[string]cpuset.CPUSet{
382 "pod": {
383 "container1": cpuset.New(1, 4),
384 },
385 },
386 },
387 }
388
389 for _, tc := range testCases {
390 t.Run(tc.description, func(t *testing.T) {
391
392 testingDir, err := os.MkdirTemp("", "cpumanager_state_test")
393 if err != nil {
394 t.Fatal(err)
395 }
396 defer os.RemoveAll(testingDir)
397
398 state, err := NewCheckpointState(testingDir, testingCheckpoint, "none", nil)
399 if err != nil {
400 t.Fatalf("could not create testing checkpointState instance: %v", err)
401 }
402
403 state.SetDefaultCPUSet(tc.defaultCPUset)
404 state.SetCPUAssignments(tc.assignments)
405
406 state.ClearState()
407 if !cpuset.New().Equals(state.GetDefaultCPUSet()) {
408 t.Fatal("cleared state with non-empty default cpu set")
409 }
410 for pod := range tc.assignments {
411 for container := range tc.assignments[pod] {
412 if _, ok := state.GetCPUSet(pod, container); ok {
413 t.Fatalf("container %q in pod %q with non-default cpu set in cleared state", container, pod)
414 }
415 }
416 }
417 })
418 }
419 }
420
421 func AssertStateEqual(t *testing.T, sf State, sm State) {
422 cpusetSf := sf.GetDefaultCPUSet()
423 cpusetSm := sm.GetDefaultCPUSet()
424 if !cpusetSf.Equals(cpusetSm) {
425 t.Errorf("State CPUSet mismatch. Have %v, want %v", cpusetSf, cpusetSm)
426 }
427
428 cpuassignmentSf := sf.GetCPUAssignments()
429 cpuassignmentSm := sm.GetCPUAssignments()
430 if !reflect.DeepEqual(cpuassignmentSf, cpuassignmentSm) {
431 t.Errorf("State CPU assignments mismatch. Have %s, want %s", cpuassignmentSf, cpuassignmentSm)
432 }
433 }
434
View as plain text