1
16
17 package memorymanager
18
19 import (
20 "context"
21 "fmt"
22 "os"
23 "reflect"
24 "strings"
25 "testing"
26
27 "k8s.io/klog/v2"
28
29 cadvisorapi "github.com/google/cadvisor/info/v1"
30 "github.com/stretchr/testify/assert"
31
32 v1 "k8s.io/api/core/v1"
33 "k8s.io/apimachinery/pkg/api/resource"
34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35 "k8s.io/apimachinery/pkg/types"
36 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
37 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
38 "k8s.io/kubernetes/pkg/kubelet/cm/containermap"
39 "k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
40 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
41 )
42
43 const (
44 hugepages2M = "hugepages-2Mi"
45 hugepages1G = "hugepages-1Gi"
46 )
47
48 const policyTypeMock policyType = "mock"
49
50 type testMemoryManager struct {
51 description string
52 machineInfo cadvisorapi.MachineInfo
53 assignments state.ContainerMemoryAssignments
54 expectedAssignments state.ContainerMemoryAssignments
55 machineState state.NUMANodeMap
56 expectedMachineState state.NUMANodeMap
57 expectedError error
58 expectedAllocateError error
59 expectedAddContainerError error
60 updateError error
61 removeContainerID string
62 nodeAllocatableReservation v1.ResourceList
63 policyName policyType
64 affinity topologymanager.Store
65 systemReservedMemory []kubeletconfig.MemoryReservation
66 expectedHints map[string][]topologymanager.TopologyHint
67 expectedReserved systemReservedMemory
68 reserved systemReservedMemory
69 podAllocate *v1.Pod
70 firstPod *v1.Pod
71 activePods []*v1.Pod
72 }
73
74 func returnPolicyByName(testCase testMemoryManager) Policy {
75 switch testCase.policyName {
76 case policyTypeMock:
77 return &mockPolicy{
78 err: fmt.Errorf("fake reg error"),
79 }
80 case policyTypeStatic:
81 policy, _ := NewPolicyStatic(&testCase.machineInfo, testCase.reserved, topologymanager.NewFakeManager())
82 return policy
83 case policyTypeNone:
84 return NewPolicyNone()
85 }
86 return nil
87 }
88
89 type mockPolicy struct {
90 err error
91 }
92
93 func (p *mockPolicy) Name() string {
94 return string(policyTypeMock)
95 }
96
97 func (p *mockPolicy) Start(s state.State) error {
98 return p.err
99 }
100
101 func (p *mockPolicy) Allocate(s state.State, pod *v1.Pod, container *v1.Container) error {
102 return p.err
103 }
104
105 func (p *mockPolicy) RemoveContainer(s state.State, podUID string, containerName string) {
106 }
107
108 func (p *mockPolicy) GetTopologyHints(s state.State, pod *v1.Pod, container *v1.Container) map[string][]topologymanager.TopologyHint {
109 return nil
110 }
111
112 func (p *mockPolicy) GetPodTopologyHints(s state.State, pod *v1.Pod) map[string][]topologymanager.TopologyHint {
113 return nil
114 }
115
116
117 func (p *mockPolicy) GetAllocatableMemory(s state.State) []state.Block {
118 return []state.Block{}
119 }
120
121 type mockRuntimeService struct {
122 err error
123 }
124
125 func (rt mockRuntimeService) UpdateContainerResources(_ context.Context, id string, resources *runtimeapi.ContainerResources) error {
126 return rt.err
127 }
128
129 type mockPodStatusProvider struct {
130 podStatus v1.PodStatus
131 found bool
132 }
133
134 func (psp mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
135 return psp.podStatus, psp.found
136 }
137
138 func getPod(podUID string, containerName string, requirements *v1.ResourceRequirements) *v1.Pod {
139 return &v1.Pod{
140 ObjectMeta: metav1.ObjectMeta{
141 UID: types.UID(podUID),
142 },
143 Spec: v1.PodSpec{
144 Containers: []v1.Container{
145 {
146 Name: containerName,
147 Resources: *requirements,
148 },
149 },
150 },
151 }
152 }
153
154 func getPodWithInitContainers(podUID string, containers []v1.Container, initContainers []v1.Container) *v1.Pod {
155 return &v1.Pod{
156 ObjectMeta: metav1.ObjectMeta{
157 UID: types.UID(podUID),
158 },
159 Spec: v1.PodSpec{
160 InitContainers: initContainers,
161 Containers: containers,
162 },
163 }
164 }
165
166 func TestValidateReservedMemory(t *testing.T) {
167 machineInfo := &cadvisorapi.MachineInfo{
168 Topology: []cadvisorapi.Node{
169 {Id: 0},
170 {Id: 1},
171 },
172 }
173 const msgNotEqual = "the total amount %q of type %q is not equal to the value %q determined by Node Allocatable feature"
174 testCases := []struct {
175 description string
176 nodeAllocatableReservation v1.ResourceList
177 machineInfo *cadvisorapi.MachineInfo
178 systemReservedMemory []kubeletconfig.MemoryReservation
179 expectedError string
180 }{
181 {
182 "Node Allocatable not set, reserved not set",
183 v1.ResourceList{},
184 machineInfo,
185 []kubeletconfig.MemoryReservation{},
186 "",
187 },
188 {
189 "Node Allocatable set to zero, reserved set to zero",
190 v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI)},
191 machineInfo,
192 []kubeletconfig.MemoryReservation{
193 {
194 NumaNode: 0,
195 Limits: v1.ResourceList{
196 v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI),
197 },
198 },
199 },
200 "",
201 },
202 {
203 "Node Allocatable not set (equal zero), reserved set",
204 v1.ResourceList{},
205 machineInfo,
206 []kubeletconfig.MemoryReservation{
207 {
208 NumaNode: 0,
209 Limits: v1.ResourceList{
210 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
211 },
212 },
213 },
214 fmt.Sprintf(msgNotEqual, "12", v1.ResourceMemory, "0"),
215 },
216 {
217 "Node Allocatable set, reserved not set",
218 v1.ResourceList{hugepages2M: *resource.NewQuantity(5, resource.DecimalSI)},
219 machineInfo,
220 []kubeletconfig.MemoryReservation{},
221 fmt.Sprintf(msgNotEqual, "0", hugepages2M, "5"),
222 },
223 {
224 "Reserved not equal to Node Allocatable",
225 v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI)},
226 machineInfo,
227 []kubeletconfig.MemoryReservation{
228 {
229 NumaNode: 0,
230 Limits: v1.ResourceList{
231 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
232 },
233 },
234 },
235 fmt.Sprintf(msgNotEqual, "12", v1.ResourceMemory, "5"),
236 },
237 {
238 "Reserved contains the NUMA node that does not exist under the machine",
239 v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI)},
240 machineInfo,
241 []kubeletconfig.MemoryReservation{
242 {
243 NumaNode: 0,
244 Limits: v1.ResourceList{
245 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
246 },
247 },
248 {
249 NumaNode: 2,
250 Limits: v1.ResourceList{
251 v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
252 },
253 },
254 },
255 "the reserved memory configuration references a NUMA node 2 that does not exist on this machine",
256 },
257 {
258 "Reserved total equal to Node Allocatable",
259 v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
260 hugepages2M: *resource.NewQuantity(77, resource.DecimalSI),
261 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
262 machineInfo,
263 []kubeletconfig.MemoryReservation{
264 {
265 NumaNode: 0,
266 Limits: v1.ResourceList{
267 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
268 hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
269 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI),
270 },
271 },
272 {
273 NumaNode: 1,
274 Limits: v1.ResourceList{
275 v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
276 hugepages2M: *resource.NewQuantity(7, resource.DecimalSI),
277 },
278 },
279 },
280 "",
281 },
282 {
283 "Reserved total hugapages-2M not equal to Node Allocatable",
284 v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
285 hugepages2M: *resource.NewQuantity(14, resource.DecimalSI),
286 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
287 machineInfo,
288 []kubeletconfig.MemoryReservation{
289 {
290 NumaNode: 0,
291 Limits: v1.ResourceList{
292 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
293 hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
294 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI),
295 },
296 },
297 {
298 NumaNode: 1,
299 Limits: v1.ResourceList{
300 v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
301 hugepages2M: *resource.NewQuantity(7, resource.DecimalSI),
302 },
303 },
304 },
305
306 fmt.Sprintf(msgNotEqual, "77", hugepages2M, "14"),
307 },
308 }
309
310 for _, tc := range testCases {
311 t.Run(tc.description, func(t *testing.T) {
312 err := validateReservedMemory(tc.machineInfo, tc.nodeAllocatableReservation, tc.systemReservedMemory)
313 if strings.TrimSpace(tc.expectedError) != "" {
314 assert.Error(t, err)
315 assert.Equal(t, tc.expectedError, err.Error())
316 }
317 })
318 }
319 }
320
321 func TestConvertPreReserved(t *testing.T) {
322 machineInfo := cadvisorapi.MachineInfo{
323 Topology: []cadvisorapi.Node{
324 {Id: 0},
325 {Id: 1},
326 },
327 }
328
329 testCases := []struct {
330 description string
331 systemReserved []kubeletconfig.MemoryReservation
332 systemReservedExpected systemReservedMemory
333 expectedError string
334 }{
335 {
336 "Empty",
337 []kubeletconfig.MemoryReservation{},
338 systemReservedMemory{
339 0: map[v1.ResourceName]uint64{},
340 1: map[v1.ResourceName]uint64{},
341 },
342 "",
343 },
344 {
345 "Single NUMA node is reserved",
346 []kubeletconfig.MemoryReservation{
347 {
348 NumaNode: 0,
349 Limits: v1.ResourceList{
350 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
351 hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
352 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI),
353 },
354 },
355 },
356 systemReservedMemory{
357 0: map[v1.ResourceName]uint64{
358 v1.ResourceMemory: 12,
359 hugepages2M: 70,
360 hugepages1G: 13,
361 },
362 1: map[v1.ResourceName]uint64{},
363 },
364 "",
365 },
366 {
367 "Both NUMA nodes are reserved",
368 []kubeletconfig.MemoryReservation{
369 {
370 NumaNode: 0,
371 Limits: v1.ResourceList{
372 v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
373 hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
374 hugepages1G: *resource.NewQuantity(13, resource.DecimalSI),
375 },
376 },
377 {
378 NumaNode: 1,
379 Limits: v1.ResourceList{
380 v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
381 hugepages2M: *resource.NewQuantity(7, resource.DecimalSI),
382 },
383 },
384 },
385 systemReservedMemory{
386 0: map[v1.ResourceName]uint64{
387 v1.ResourceMemory: 12,
388 hugepages2M: 70,
389 hugepages1G: 13,
390 },
391 1: map[v1.ResourceName]uint64{
392 v1.ResourceMemory: 5,
393 hugepages2M: 7,
394 },
395 },
396 "",
397 },
398 }
399
400 for _, tc := range testCases {
401 t.Run(tc.description, func(t *testing.T) {
402 reserved, _ := convertReserved(&machineInfo, tc.systemReserved)
403 if !reflect.DeepEqual(reserved, tc.systemReservedExpected) {
404 t.Errorf("got %v, expected %v", reserved, tc.systemReservedExpected)
405 }
406 })
407 }
408 }
409
410 func TestGetSystemReservedMemory(t *testing.T) {
411 machineInfo := returnMachineInfo()
412 testCases := []testMemoryManager{
413 {
414 description: "Should return empty map when reservation is not done",
415 nodeAllocatableReservation: v1.ResourceList{},
416 systemReservedMemory: []kubeletconfig.MemoryReservation{},
417 expectedReserved: systemReservedMemory{
418 0: {},
419 1: {},
420 },
421 expectedError: nil,
422 machineInfo: machineInfo,
423 },
424 {
425 description: "Should return error when Allocatable reservation is not equal to the reserved memory",
426 nodeAllocatableReservation: v1.ResourceList{},
427 systemReservedMemory: []kubeletconfig.MemoryReservation{
428 {
429 NumaNode: 0,
430 Limits: v1.ResourceList{
431 v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI),
432 },
433 },
434 },
435 expectedReserved: nil,
436 expectedError: fmt.Errorf("the total amount \"1Gi\" of type \"memory\" is not equal to the value \"0\" determined by Node Allocatable feature"),
437 machineInfo: machineInfo,
438 },
439 {
440 description: "Reserved should be equal to systemReservedMemory",
441 nodeAllocatableReservation: v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(2*gb, resource.BinarySI)},
442 systemReservedMemory: []kubeletconfig.MemoryReservation{
443 {
444 NumaNode: 0,
445 Limits: v1.ResourceList{
446 v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI),
447 },
448 },
449 {
450 NumaNode: 1,
451 Limits: v1.ResourceList{
452 v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI),
453 },
454 },
455 },
456 expectedReserved: systemReservedMemory{
457 0: map[v1.ResourceName]uint64{
458 v1.ResourceMemory: 1 * gb,
459 },
460 1: map[v1.ResourceName]uint64{
461 v1.ResourceMemory: 1 * gb,
462 },
463 },
464 expectedError: nil,
465 machineInfo: machineInfo,
466 },
467 }
468
469 for _, testCase := range testCases {
470 t.Run(testCase.description, func(t *testing.T) {
471 res, err := getSystemReservedMemory(&testCase.machineInfo, testCase.nodeAllocatableReservation, testCase.systemReservedMemory)
472
473 if !reflect.DeepEqual(res, testCase.expectedReserved) {
474 t.Errorf("Memory Manager getReservedMemory() error, expected reserved %+v, but got: %+v",
475 testCase.expectedReserved, res)
476 }
477 if !reflect.DeepEqual(err, testCase.expectedError) {
478 t.Errorf("Memory Manager getReservedMemory() error, expected error %v, but got: %v",
479 testCase.expectedError, err)
480 }
481
482 })
483 }
484 }
485
486 func TestRemoveStaleState(t *testing.T) {
487 machineInfo := returnMachineInfo()
488 testCases := []testMemoryManager{
489 {
490 description: "Should fail - policy returns an error",
491 policyName: policyTypeMock,
492 machineInfo: machineInfo,
493 reserved: systemReservedMemory{
494 0: map[v1.ResourceName]uint64{
495 v1.ResourceMemory: 1 * gb,
496 },
497 1: map[v1.ResourceName]uint64{
498 v1.ResourceMemory: 1 * gb,
499 },
500 },
501 assignments: state.ContainerMemoryAssignments{
502 "fakePod1": map[string][]state.Block{
503 "fakeContainer1": {
504 {
505 NUMAAffinity: []int{0},
506 Type: v1.ResourceMemory,
507 Size: 1 * gb,
508 },
509 {
510 NUMAAffinity: []int{0},
511 Type: hugepages1Gi,
512 Size: 1 * gb,
513 },
514 },
515 "fakeContainer2": {
516 {
517 NUMAAffinity: []int{0},
518 Type: v1.ResourceMemory,
519 Size: 1 * gb,
520 },
521 {
522 NUMAAffinity: []int{0},
523 Type: hugepages1Gi,
524 Size: 1 * gb,
525 },
526 },
527 },
528 },
529 expectedAssignments: state.ContainerMemoryAssignments{
530 "fakePod1": map[string][]state.Block{
531 "fakeContainer1": {
532 {
533 NUMAAffinity: []int{0},
534 Type: v1.ResourceMemory,
535 Size: 1 * gb,
536 },
537 {
538 NUMAAffinity: []int{0},
539 Type: hugepages1Gi,
540 Size: 1 * gb,
541 },
542 },
543 "fakeContainer2": {
544 {
545 NUMAAffinity: []int{0},
546 Type: v1.ResourceMemory,
547 Size: 1 * gb,
548 },
549 {
550 NUMAAffinity: []int{0},
551 Type: hugepages1Gi,
552 Size: 1 * gb,
553 },
554 },
555 },
556 },
557 machineState: state.NUMANodeMap{
558 0: &state.NUMANodeState{
559 Cells: []int{0},
560 NumberOfAssignments: 4,
561 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
562 v1.ResourceMemory: {
563 Allocatable: 9 * gb,
564 Free: 7 * gb,
565 Reserved: 2 * gb,
566 SystemReserved: 1 * gb,
567 TotalMemSize: 10 * gb,
568 },
569 hugepages1Gi: {
570 Allocatable: 5 * gb,
571 Free: 3 * gb,
572 Reserved: 2 * gb,
573 SystemReserved: 0 * gb,
574 TotalMemSize: 5 * gb,
575 },
576 },
577 },
578 1: &state.NUMANodeState{
579 Cells: []int{1},
580 NumberOfAssignments: 0,
581 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
582 v1.ResourceMemory: {
583 Allocatable: 9 * gb,
584 Free: 9 * gb,
585 Reserved: 0 * gb,
586 SystemReserved: 1 * gb,
587 TotalMemSize: 10 * gb,
588 },
589 hugepages1Gi: {
590 Allocatable: 5 * gb,
591 Free: 5 * gb,
592 Reserved: 0,
593 SystemReserved: 0,
594 TotalMemSize: 5 * gb,
595 },
596 },
597 },
598 },
599 expectedMachineState: state.NUMANodeMap{
600 0: &state.NUMANodeState{
601 Cells: []int{0},
602 NumberOfAssignments: 4,
603 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
604 v1.ResourceMemory: {
605 Allocatable: 9 * gb,
606 Free: 7 * gb,
607 Reserved: 2 * gb,
608 SystemReserved: 1 * gb,
609 TotalMemSize: 10 * gb,
610 },
611 hugepages1Gi: {
612 Allocatable: 5 * gb,
613 Free: 3 * gb,
614 Reserved: 2 * gb,
615 SystemReserved: 0 * gb,
616 TotalMemSize: 5 * gb,
617 },
618 },
619 },
620 1: &state.NUMANodeState{
621 Cells: []int{1},
622 NumberOfAssignments: 0,
623 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
624 v1.ResourceMemory: {
625 Allocatable: 9 * gb,
626 Free: 9 * gb,
627 Reserved: 0 * gb,
628 SystemReserved: 1 * gb,
629 TotalMemSize: 10 * gb,
630 },
631 hugepages1Gi: {
632 Allocatable: 5 * gb,
633 Free: 5 * gb,
634 Reserved: 0,
635 SystemReserved: 0,
636 TotalMemSize: 5 * gb,
637 },
638 },
639 },
640 },
641 },
642 {
643 description: "Stale state successfully removed, without multi NUMA assignments",
644 policyName: policyTypeStatic,
645 machineInfo: machineInfo,
646 reserved: systemReservedMemory{
647 0: map[v1.ResourceName]uint64{
648 v1.ResourceMemory: 1 * gb,
649 },
650 1: map[v1.ResourceName]uint64{
651 v1.ResourceMemory: 1 * gb,
652 },
653 },
654 assignments: state.ContainerMemoryAssignments{
655 "fakePod1": map[string][]state.Block{
656 "fakeContainer1": {
657 {
658 NUMAAffinity: []int{0},
659 Type: v1.ResourceMemory,
660 Size: 1 * gb,
661 },
662 {
663 NUMAAffinity: []int{0},
664 Type: hugepages1Gi,
665 Size: 1 * gb,
666 },
667 },
668 "fakeContainer2": {
669 {
670 NUMAAffinity: []int{0},
671 Type: v1.ResourceMemory,
672 Size: 1 * gb,
673 },
674 {
675 NUMAAffinity: []int{0},
676 Type: hugepages1Gi,
677 Size: 1 * gb,
678 },
679 },
680 },
681 },
682 expectedAssignments: state.ContainerMemoryAssignments{},
683 machineState: state.NUMANodeMap{
684 0: &state.NUMANodeState{
685 Cells: []int{0},
686 NumberOfAssignments: 4,
687 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
688 v1.ResourceMemory: {
689 Allocatable: 9 * gb,
690 Free: 7 * gb,
691 Reserved: 2 * gb,
692 SystemReserved: 1 * gb,
693 TotalMemSize: 10 * gb,
694 },
695 hugepages1Gi: {
696 Allocatable: 5 * gb,
697 Free: 3 * gb,
698 Reserved: 2 * gb,
699 SystemReserved: 0 * gb,
700 TotalMemSize: 5 * gb,
701 },
702 },
703 },
704 1: &state.NUMANodeState{
705 Cells: []int{1},
706 NumberOfAssignments: 0,
707 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
708 v1.ResourceMemory: {
709 Allocatable: 9 * gb,
710 Free: 9 * gb,
711 Reserved: 0 * gb,
712 SystemReserved: 1 * gb,
713 TotalMemSize: 10 * gb,
714 },
715 hugepages1Gi: {
716 Allocatable: 5 * gb,
717 Free: 5 * gb,
718 Reserved: 0,
719 SystemReserved: 0,
720 TotalMemSize: 5 * gb,
721 },
722 },
723 },
724 },
725 expectedMachineState: state.NUMANodeMap{
726 0: &state.NUMANodeState{
727 Cells: []int{0},
728 NumberOfAssignments: 0,
729 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
730 v1.ResourceMemory: {
731 Allocatable: 9 * gb,
732 Free: 9 * gb,
733 Reserved: 0 * gb,
734 SystemReserved: 1 * gb,
735 TotalMemSize: 10 * gb,
736 },
737 hugepages1Gi: {
738 Allocatable: 5 * gb,
739 Free: 5 * gb,
740 Reserved: 0,
741 SystemReserved: 0,
742 TotalMemSize: 5 * gb,
743 },
744 },
745 },
746 1: &state.NUMANodeState{
747 Cells: []int{1},
748 NumberOfAssignments: 0,
749 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
750 v1.ResourceMemory: {
751 Allocatable: 9 * gb,
752 Free: 9 * gb,
753 Reserved: 0 * gb,
754 SystemReserved: 1 * gb,
755 TotalMemSize: 10 * gb,
756 },
757 hugepages1Gi: {
758 Allocatable: 5 * gb,
759 Free: 5 * gb,
760 Reserved: 0,
761 SystemReserved: 0,
762 TotalMemSize: 5 * gb,
763 },
764 },
765 },
766 },
767 },
768 {
769 description: "Stale state successfully removed, with multi NUMA assignments",
770 policyName: policyTypeStatic,
771 machineInfo: machineInfo,
772 reserved: systemReservedMemory{
773 0: map[v1.ResourceName]uint64{
774 v1.ResourceMemory: 1 * gb,
775 },
776 1: map[v1.ResourceName]uint64{
777 v1.ResourceMemory: 1 * gb,
778 },
779 },
780 assignments: state.ContainerMemoryAssignments{
781 "fakePod1": map[string][]state.Block{
782 "fakeContainer1": {
783 {
784 NUMAAffinity: []int{0, 1},
785 Type: v1.ResourceMemory,
786 Size: 12 * gb,
787 },
788 {
789 NUMAAffinity: []int{0, 1},
790 Type: hugepages1Gi,
791 Size: 1 * gb,
792 },
793 },
794 "fakeContainer2": {
795 {
796 NUMAAffinity: []int{0, 1},
797 Type: v1.ResourceMemory,
798 Size: 1 * gb,
799 },
800 {
801 NUMAAffinity: []int{0, 1},
802 Type: hugepages1Gi,
803 Size: 1 * gb,
804 },
805 },
806 },
807 },
808 expectedAssignments: state.ContainerMemoryAssignments{},
809 machineState: state.NUMANodeMap{
810 0: &state.NUMANodeState{
811 Cells: []int{0, 1},
812 NumberOfAssignments: 4,
813 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
814 v1.ResourceMemory: {
815 Allocatable: 9 * gb,
816 Free: 0 * gb,
817 Reserved: 9 * gb,
818 SystemReserved: 1 * gb,
819 TotalMemSize: 10 * gb,
820 },
821 hugepages1Gi: {
822 Allocatable: 5 * gb,
823 Free: 4 * gb,
824 Reserved: 1 * gb,
825 SystemReserved: 0 * gb,
826 TotalMemSize: 5 * gb,
827 },
828 },
829 },
830 1: &state.NUMANodeState{
831 Cells: []int{0, 1},
832 NumberOfAssignments: 4,
833 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
834 v1.ResourceMemory: {
835 Allocatable: 9 * gb,
836 Free: 5 * gb,
837 Reserved: 4 * gb,
838 SystemReserved: 1 * gb,
839 TotalMemSize: 10 * gb,
840 },
841 hugepages1Gi: {
842 Allocatable: 5 * gb,
843 Free: 4 * gb,
844 Reserved: 1 * gb,
845 SystemReserved: 0,
846 TotalMemSize: 5 * gb,
847 },
848 },
849 },
850 },
851 expectedMachineState: state.NUMANodeMap{
852 0: &state.NUMANodeState{
853 Cells: []int{0},
854 NumberOfAssignments: 0,
855 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
856 v1.ResourceMemory: {
857 Allocatable: 9 * gb,
858 Free: 9 * gb,
859 Reserved: 0 * gb,
860 SystemReserved: 1 * gb,
861 TotalMemSize: 10 * gb,
862 },
863 hugepages1Gi: {
864 Allocatable: 5 * gb,
865 Free: 5 * gb,
866 Reserved: 0,
867 SystemReserved: 0,
868 TotalMemSize: 5 * gb,
869 },
870 },
871 },
872 1: &state.NUMANodeState{
873 Cells: []int{1},
874 NumberOfAssignments: 0,
875 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
876 v1.ResourceMemory: {
877 Allocatable: 9 * gb,
878 Free: 9 * gb,
879 Reserved: 0 * gb,
880 SystemReserved: 1 * gb,
881 TotalMemSize: 10 * gb,
882 },
883 hugepages1Gi: {
884 Allocatable: 5 * gb,
885 Free: 5 * gb,
886 Reserved: 0,
887 SystemReserved: 0,
888 TotalMemSize: 5 * gb,
889 },
890 },
891 },
892 },
893 },
894 }
895 for _, testCase := range testCases {
896 t.Run(testCase.description, func(t *testing.T) {
897 mgr := &manager{
898 policy: returnPolicyByName(testCase),
899 state: state.NewMemoryState(),
900 containerMap: containermap.NewContainerMap(),
901 containerRuntime: mockRuntimeService{
902 err: nil,
903 },
904 activePods: func() []*v1.Pod { return nil },
905 podStatusProvider: mockPodStatusProvider{},
906 }
907 mgr.sourcesReady = &sourcesReadyStub{}
908 mgr.state.SetMemoryAssignments(testCase.assignments)
909 mgr.state.SetMachineState(testCase.machineState)
910
911 mgr.removeStaleState()
912
913 if !areContainerMemoryAssignmentsEqual(t, mgr.state.GetMemoryAssignments(), testCase.expectedAssignments) {
914 t.Errorf("Memory Manager removeStaleState() error, expected assignments %v, but got: %v",
915 testCase.expectedAssignments, mgr.state.GetMemoryAssignments())
916 }
917 if !areMachineStatesEqual(mgr.state.GetMachineState(), testCase.expectedMachineState) {
918 t.Fatalf("The actual machine state: %v is different from the expected one: %v", mgr.state.GetMachineState(), testCase.expectedMachineState)
919 }
920 })
921
922 }
923 }
924
925 func TestAddContainer(t *testing.T) {
926 machineInfo := returnMachineInfo()
927 reserved := systemReservedMemory{
928 0: map[v1.ResourceName]uint64{
929 v1.ResourceMemory: 1 * gb,
930 },
931 1: map[v1.ResourceName]uint64{
932 v1.ResourceMemory: 1 * gb,
933 },
934 }
935 pod := getPod("fakePod1", "fakeContainer1", requirementsGuaranteed)
936 testCases := []testMemoryManager{
937 {
938 description: "Correct allocation and adding container on NUMA 0",
939 policyName: policyTypeStatic,
940 machineInfo: machineInfo,
941 reserved: reserved,
942 machineState: state.NUMANodeMap{
943 0: &state.NUMANodeState{
944 Cells: []int{0},
945 NumberOfAssignments: 0,
946 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
947 v1.ResourceMemory: {
948 Allocatable: 9 * gb,
949 Free: 9 * gb,
950 Reserved: 0 * gb,
951 SystemReserved: 1 * gb,
952 TotalMemSize: 10 * gb,
953 },
954 hugepages1Gi: {
955 Allocatable: 5 * gb,
956 Free: 5 * gb,
957 Reserved: 0,
958 SystemReserved: 0,
959 TotalMemSize: 5 * gb,
960 },
961 },
962 },
963 1: &state.NUMANodeState{
964 Cells: []int{1},
965 NumberOfAssignments: 0,
966 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
967 v1.ResourceMemory: {
968 Allocatable: 9 * gb,
969 Free: 9 * gb,
970 Reserved: 0 * gb,
971 SystemReserved: 1 * gb,
972 TotalMemSize: 10 * gb,
973 },
974 hugepages1Gi: {
975 Allocatable: 5 * gb,
976 Free: 5 * gb,
977 Reserved: 0,
978 SystemReserved: 0,
979 TotalMemSize: 5 * gb,
980 },
981 },
982 },
983 },
984 expectedMachineState: state.NUMANodeMap{
985 0: &state.NUMANodeState{
986 Cells: []int{0},
987 NumberOfAssignments: 2,
988 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
989 v1.ResourceMemory: {
990 Allocatable: 9 * gb,
991 Free: 8 * gb,
992 Reserved: 1 * gb,
993 SystemReserved: 1 * gb,
994 TotalMemSize: 10 * gb,
995 },
996 hugepages1Gi: {
997 Allocatable: 5 * gb,
998 Free: 4 * gb,
999 Reserved: 1 * gb,
1000 SystemReserved: 0,
1001 TotalMemSize: 5 * gb,
1002 },
1003 },
1004 },
1005 1: &state.NUMANodeState{
1006 Cells: []int{1},
1007 NumberOfAssignments: 0,
1008 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1009 v1.ResourceMemory: {
1010 Allocatable: 9 * gb,
1011 Free: 9 * gb,
1012 Reserved: 0 * gb,
1013 SystemReserved: 1 * gb,
1014 TotalMemSize: 10 * gb,
1015 },
1016 hugepages1Gi: {
1017 Allocatable: 5 * gb,
1018 Free: 5 * gb,
1019 Reserved: 0,
1020 SystemReserved: 0,
1021 TotalMemSize: 5 * gb,
1022 },
1023 },
1024 },
1025 },
1026 expectedAllocateError: nil,
1027 expectedAddContainerError: nil,
1028 updateError: nil,
1029 podAllocate: pod,
1030 assignments: state.ContainerMemoryAssignments{},
1031 activePods: nil,
1032 },
1033 {
1034 description: "Shouldn't return any error when policy is set as None",
1035 updateError: nil,
1036 policyName: policyTypeNone,
1037 machineInfo: machineInfo,
1038 reserved: reserved,
1039 machineState: state.NUMANodeMap{},
1040 expectedMachineState: state.NUMANodeMap{},
1041 expectedAllocateError: nil,
1042 expectedAddContainerError: nil,
1043 podAllocate: pod,
1044 assignments: state.ContainerMemoryAssignments{},
1045 activePods: nil,
1046 },
1047 {
1048 description: "Allocation should fail if policy returns an error",
1049 updateError: nil,
1050 policyName: policyTypeMock,
1051 machineInfo: machineInfo,
1052 reserved: reserved,
1053 machineState: state.NUMANodeMap{
1054 0: &state.NUMANodeState{
1055 Cells: []int{0},
1056 NumberOfAssignments: 0,
1057 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1058 v1.ResourceMemory: {
1059 Allocatable: 9 * gb,
1060 Free: 9 * gb,
1061 Reserved: 0 * gb,
1062 SystemReserved: 1 * gb,
1063 TotalMemSize: 10 * gb,
1064 },
1065 hugepages1Gi: {
1066 Allocatable: 5 * gb,
1067 Free: 5 * gb,
1068 Reserved: 0,
1069 SystemReserved: 0,
1070 TotalMemSize: 5 * gb,
1071 },
1072 },
1073 },
1074 1: &state.NUMANodeState{
1075 Cells: []int{1},
1076 NumberOfAssignments: 0,
1077 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1078 v1.ResourceMemory: {
1079 Allocatable: 9 * gb,
1080 Free: 9 * gb,
1081 Reserved: 0 * gb,
1082 SystemReserved: 1 * gb,
1083 TotalMemSize: 10 * gb,
1084 },
1085 hugepages1Gi: {
1086 Allocatable: 5 * gb,
1087 Free: 5 * gb,
1088 Reserved: 0,
1089 SystemReserved: 0,
1090 TotalMemSize: 5 * gb,
1091 },
1092 },
1093 },
1094 },
1095 expectedMachineState: state.NUMANodeMap{
1096 0: &state.NUMANodeState{
1097 Cells: []int{0},
1098 NumberOfAssignments: 0,
1099 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1100 v1.ResourceMemory: {
1101 Allocatable: 9 * gb,
1102 Free: 9 * gb,
1103 Reserved: 0 * gb,
1104 SystemReserved: 1 * gb,
1105 TotalMemSize: 10 * gb,
1106 },
1107 hugepages1Gi: {
1108 Allocatable: 5 * gb,
1109 Free: 5 * gb,
1110 Reserved: 0,
1111 SystemReserved: 0,
1112 TotalMemSize: 5 * gb,
1113 },
1114 },
1115 },
1116 1: &state.NUMANodeState{
1117 Cells: []int{1},
1118 NumberOfAssignments: 0,
1119 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1120 v1.ResourceMemory: {
1121 Allocatable: 9 * gb,
1122 Free: 9 * gb,
1123 Reserved: 0 * gb,
1124 SystemReserved: 1 * gb,
1125 TotalMemSize: 10 * gb,
1126 },
1127 hugepages1Gi: {
1128 Allocatable: 5 * gb,
1129 Free: 5 * gb,
1130 Reserved: 0,
1131 SystemReserved: 0,
1132 TotalMemSize: 5 * gb,
1133 },
1134 },
1135 },
1136 },
1137 expectedAllocateError: fmt.Errorf("fake reg error"),
1138 expectedAddContainerError: nil,
1139 podAllocate: pod,
1140 assignments: state.ContainerMemoryAssignments{},
1141 activePods: nil,
1142 },
1143 {
1144 description: "Correct allocation of container requiring amount of memory higher than capacity of one NUMA node",
1145 policyName: policyTypeStatic,
1146 machineInfo: machineInfo,
1147 reserved: reserved,
1148 machineState: state.NUMANodeMap{
1149 0: &state.NUMANodeState{
1150 Cells: []int{0},
1151 NumberOfAssignments: 0,
1152 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1153 v1.ResourceMemory: {
1154 Allocatable: 9 * gb,
1155 Free: 9 * gb,
1156 Reserved: 0 * gb,
1157 SystemReserved: 1 * gb,
1158 TotalMemSize: 10 * gb,
1159 },
1160 hugepages1Gi: {
1161 Allocatable: 5 * gb,
1162 Free: 5 * gb,
1163 Reserved: 0,
1164 SystemReserved: 0,
1165 TotalMemSize: 5 * gb,
1166 },
1167 },
1168 },
1169 1: &state.NUMANodeState{
1170 Cells: []int{1},
1171 NumberOfAssignments: 0,
1172 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1173 v1.ResourceMemory: {
1174 Allocatable: 9 * gb,
1175 Free: 9 * gb,
1176 Reserved: 0 * gb,
1177 SystemReserved: 1 * gb,
1178 TotalMemSize: 10 * gb,
1179 },
1180 hugepages1Gi: {
1181 Allocatable: 5 * gb,
1182 Free: 5 * gb,
1183 Reserved: 0,
1184 SystemReserved: 0,
1185 TotalMemSize: 5 * gb,
1186 },
1187 },
1188 },
1189 },
1190 expectedMachineState: state.NUMANodeMap{
1191 0: &state.NUMANodeState{
1192 Cells: []int{0, 1},
1193 NumberOfAssignments: 2,
1194 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1195 v1.ResourceMemory: {
1196 Allocatable: 9 * gb,
1197 Free: 0 * gb,
1198 Reserved: 9 * gb,
1199 SystemReserved: 1 * gb,
1200 TotalMemSize: 10 * gb,
1201 },
1202 hugepages1Gi: {
1203 Allocatable: 5 * gb,
1204 Free: 1 * gb,
1205 Reserved: 4 * gb,
1206 SystemReserved: 0,
1207 TotalMemSize: 5 * gb,
1208 },
1209 },
1210 },
1211 1: &state.NUMANodeState{
1212 Cells: []int{0, 1},
1213 NumberOfAssignments: 2,
1214 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1215 v1.ResourceMemory: {
1216 Allocatable: 9 * gb,
1217 Free: 6 * gb,
1218 Reserved: 3 * gb,
1219 SystemReserved: 1 * gb,
1220 TotalMemSize: 10 * gb,
1221 },
1222 hugepages1Gi: {
1223 Allocatable: 5 * gb,
1224 Free: 5 * gb,
1225 Reserved: 0,
1226 SystemReserved: 0,
1227 TotalMemSize: 5 * gb,
1228 },
1229 },
1230 },
1231 },
1232 expectedAllocateError: nil,
1233 expectedAddContainerError: nil,
1234 podAllocate: getPod("fakePod1", "fakeContainer1", &v1.ResourceRequirements{
1235 Limits: v1.ResourceList{
1236 v1.ResourceCPU: resource.MustParse("1000Mi"),
1237 v1.ResourceMemory: resource.MustParse("12Gi"),
1238 hugepages1Gi: resource.MustParse("4Gi"),
1239 },
1240 Requests: v1.ResourceList{
1241 v1.ResourceCPU: resource.MustParse("1000Mi"),
1242 v1.ResourceMemory: resource.MustParse("12Gi"),
1243 hugepages1Gi: resource.MustParse("4Gi"),
1244 },
1245 }),
1246 assignments: state.ContainerMemoryAssignments{},
1247 activePods: nil,
1248 },
1249 {
1250 description: "Should fail if try to allocate container requiring amount of memory higher than capacity of one NUMA node but a small pod is already allocated",
1251 policyName: policyTypeStatic,
1252 machineInfo: machineInfo,
1253 firstPod: pod,
1254 reserved: reserved,
1255 machineState: state.NUMANodeMap{
1256 0: &state.NUMANodeState{
1257 Cells: []int{0},
1258 NumberOfAssignments: 2,
1259 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1260 v1.ResourceMemory: {
1261 Allocatable: 9 * gb,
1262 Free: 8 * gb,
1263 Reserved: 1 * gb,
1264 SystemReserved: 1 * gb,
1265 TotalMemSize: 10 * gb,
1266 },
1267 hugepages1Gi: {
1268 Allocatable: 5 * gb,
1269 Free: 4 * gb,
1270 Reserved: 1 * gb,
1271 SystemReserved: 0,
1272 TotalMemSize: 5 * gb,
1273 },
1274 },
1275 },
1276 1: &state.NUMANodeState{
1277 Cells: []int{1},
1278 NumberOfAssignments: 0,
1279 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1280 v1.ResourceMemory: {
1281 Allocatable: 9 * gb,
1282 Free: 9 * gb,
1283 Reserved: 0 * gb,
1284 SystemReserved: 1 * gb,
1285 TotalMemSize: 10 * gb,
1286 },
1287 hugepages1Gi: {
1288 Allocatable: 5 * gb,
1289 Free: 5 * gb,
1290 Reserved: 0,
1291 SystemReserved: 0,
1292 TotalMemSize: 5 * gb,
1293 },
1294 },
1295 },
1296 },
1297 assignments: state.ContainerMemoryAssignments{
1298 "fakePod1": map[string][]state.Block{
1299 "fakeContainer1": {
1300 {
1301 NUMAAffinity: []int{0},
1302 Type: v1.ResourceMemory,
1303 Size: 1 * gb,
1304 },
1305 {
1306 NUMAAffinity: []int{0},
1307 Type: hugepages1Gi,
1308 Size: 1 * gb,
1309 },
1310 },
1311 },
1312 },
1313 expectedMachineState: state.NUMANodeMap{
1314 0: &state.NUMANodeState{
1315 Cells: []int{0},
1316 NumberOfAssignments: 2,
1317 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1318 v1.ResourceMemory: {
1319 Allocatable: 9 * gb,
1320 Free: 8 * gb,
1321 Reserved: 1 * gb,
1322 SystemReserved: 1 * gb,
1323 TotalMemSize: 10 * gb,
1324 },
1325 hugepages1Gi: {
1326 Allocatable: 5 * gb,
1327 Free: 4 * gb,
1328 Reserved: 1 * gb,
1329 SystemReserved: 0,
1330 TotalMemSize: 5 * gb,
1331 },
1332 },
1333 },
1334 1: &state.NUMANodeState{
1335 Cells: []int{1},
1336 NumberOfAssignments: 0,
1337 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1338 v1.ResourceMemory: {
1339 Allocatable: 9 * gb,
1340 Free: 9 * gb,
1341 Reserved: 0 * gb,
1342 SystemReserved: 1 * gb,
1343 TotalMemSize: 10 * gb,
1344 },
1345 hugepages1Gi: {
1346 Allocatable: 5 * gb,
1347 Free: 5 * gb,
1348 Reserved: 0,
1349 SystemReserved: 0,
1350 TotalMemSize: 5 * gb,
1351 },
1352 },
1353 },
1354 },
1355 expectedAllocateError: fmt.Errorf("[memorymanager] failed to get the default NUMA affinity, no NUMA nodes with enough memory is available"),
1356 expectedAddContainerError: nil,
1357 podAllocate: getPod("fakePod2", "fakeContainer2", &v1.ResourceRequirements{
1358 Limits: v1.ResourceList{
1359 v1.ResourceCPU: resource.MustParse("1000Mi"),
1360 v1.ResourceMemory: resource.MustParse("12Gi"),
1361 hugepages1Gi: resource.MustParse("4Gi"),
1362 },
1363 Requests: v1.ResourceList{
1364 v1.ResourceCPU: resource.MustParse("1000Mi"),
1365 v1.ResourceMemory: resource.MustParse("12Gi"),
1366 hugepages1Gi: resource.MustParse("4Gi"),
1367 },
1368 }),
1369 activePods: []*v1.Pod{
1370 {
1371 ObjectMeta: metav1.ObjectMeta{
1372 UID: types.UID("fakePod1"),
1373 },
1374 Spec: v1.PodSpec{
1375 Containers: []v1.Container{
1376 {
1377 Name: "fakeContainer1",
1378 Resources: *requirementsGuaranteed,
1379 },
1380 },
1381 },
1382 },
1383 },
1384 },
1385 }
1386
1387 for _, testCase := range testCases {
1388 t.Run(testCase.description, func(t *testing.T) {
1389 mgr := &manager{
1390 policy: returnPolicyByName(testCase),
1391 state: state.NewMemoryState(),
1392 containerMap: containermap.NewContainerMap(),
1393 containerRuntime: mockRuntimeService{
1394 err: testCase.updateError,
1395 },
1396 activePods: func() []*v1.Pod { return testCase.activePods },
1397 podStatusProvider: mockPodStatusProvider{},
1398 }
1399 mgr.sourcesReady = &sourcesReadyStub{}
1400 mgr.state.SetMachineState(testCase.machineState)
1401 mgr.state.SetMemoryAssignments(testCase.assignments)
1402 if testCase.firstPod != nil {
1403 mgr.containerMap.Add(testCase.firstPod.Name, testCase.firstPod.Spec.Containers[0].Name, "fakeID0")
1404 }
1405 pod := testCase.podAllocate
1406 container := &pod.Spec.Containers[0]
1407 err := mgr.Allocate(pod, container)
1408 if !reflect.DeepEqual(err, testCase.expectedAllocateError) {
1409 t.Errorf("Memory Manager Allocate() error (%v), expected error: %v, but got: %v",
1410 testCase.description, testCase.expectedAllocateError, err)
1411 }
1412 mgr.AddContainer(pod, container, "fakeID")
1413 _, _, err = mgr.containerMap.GetContainerRef("fakeID")
1414 if !reflect.DeepEqual(err, testCase.expectedAddContainerError) {
1415 t.Errorf("Memory Manager AddContainer() error (%v), expected error: %v, but got: %v",
1416 testCase.description, testCase.expectedAddContainerError, err)
1417 }
1418
1419 if !areMachineStatesEqual(mgr.state.GetMachineState(), testCase.expectedMachineState) {
1420 t.Errorf("[test] %+v", mgr.state.GetMemoryAssignments())
1421 t.Fatalf("The actual machine state: %v is different from the expected one: %v", mgr.state.GetMachineState(), testCase.expectedMachineState)
1422 }
1423
1424 })
1425 }
1426 }
1427
1428 func TestRemoveContainer(t *testing.T) {
1429 machineInfo := returnMachineInfo()
1430 reserved := systemReservedMemory{
1431 0: map[v1.ResourceName]uint64{
1432 v1.ResourceMemory: 1 * gb,
1433 },
1434 1: map[v1.ResourceName]uint64{
1435 v1.ResourceMemory: 1 * gb,
1436 },
1437 }
1438 testCases := []testMemoryManager{
1439 {
1440 description: "Correct removing of a container",
1441 removeContainerID: "fakeID2",
1442 policyName: policyTypeStatic,
1443 machineInfo: machineInfo,
1444 reserved: reserved,
1445 assignments: state.ContainerMemoryAssignments{
1446 "fakePod1": map[string][]state.Block{
1447 "fakeContainer1": {
1448 {
1449 NUMAAffinity: []int{0},
1450 Type: v1.ResourceMemory,
1451 Size: 1 * gb,
1452 },
1453 {
1454 NUMAAffinity: []int{0},
1455 Type: hugepages1Gi,
1456 Size: 1 * gb,
1457 },
1458 },
1459 "fakeContainer2": {
1460 {
1461 NUMAAffinity: []int{0},
1462 Type: v1.ResourceMemory,
1463 Size: 1 * gb,
1464 },
1465 {
1466 NUMAAffinity: []int{0},
1467 Type: hugepages1Gi,
1468 Size: 1 * gb,
1469 },
1470 },
1471 },
1472 },
1473 expectedAssignments: state.ContainerMemoryAssignments{
1474 "fakePod1": map[string][]state.Block{
1475 "fakeContainer1": {
1476 {
1477 NUMAAffinity: []int{0},
1478 Type: v1.ResourceMemory,
1479 Size: 1 * gb,
1480 },
1481 {
1482 NUMAAffinity: []int{0},
1483 Type: hugepages1Gi,
1484 Size: 1 * gb,
1485 },
1486 },
1487 },
1488 },
1489 machineState: state.NUMANodeMap{
1490 0: &state.NUMANodeState{
1491 Cells: []int{0},
1492 NumberOfAssignments: 4,
1493 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1494 v1.ResourceMemory: {
1495 Allocatable: 9 * gb,
1496 Free: 7 * gb,
1497 Reserved: 2 * gb,
1498 SystemReserved: 1 * gb,
1499 TotalMemSize: 10 * gb,
1500 },
1501 hugepages1Gi: {
1502 Allocatable: 5 * gb,
1503 Free: 3 * gb,
1504 Reserved: 2 * gb,
1505 SystemReserved: 0 * gb,
1506 TotalMemSize: 5 * gb,
1507 },
1508 },
1509 },
1510 1: &state.NUMANodeState{
1511 Cells: []int{1},
1512 NumberOfAssignments: 0,
1513 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1514 v1.ResourceMemory: {
1515 Allocatable: 9 * gb,
1516 Free: 9 * gb,
1517 Reserved: 0 * gb,
1518 SystemReserved: 1 * gb,
1519 TotalMemSize: 10 * gb,
1520 },
1521 hugepages1Gi: {
1522 Allocatable: 5 * gb,
1523 Free: 5 * gb,
1524 Reserved: 0,
1525 SystemReserved: 0,
1526 TotalMemSize: 5 * gb,
1527 },
1528 },
1529 },
1530 },
1531 expectedMachineState: state.NUMANodeMap{
1532 0: &state.NUMANodeState{
1533 Cells: []int{0},
1534 NumberOfAssignments: 2,
1535 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1536 v1.ResourceMemory: {
1537 Allocatable: 9 * gb,
1538 Free: 8 * gb,
1539 Reserved: 1 * gb,
1540 SystemReserved: 1 * gb,
1541 TotalMemSize: 10 * gb,
1542 },
1543 hugepages1Gi: {
1544 Allocatable: 5 * gb,
1545 Free: 4 * gb,
1546 Reserved: 1 * gb,
1547 SystemReserved: 0,
1548 TotalMemSize: 5 * gb,
1549 },
1550 },
1551 },
1552 1: &state.NUMANodeState{
1553 Cells: []int{1},
1554 NumberOfAssignments: 0,
1555 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1556 v1.ResourceMemory: {
1557 Allocatable: 9 * gb,
1558 Free: 9 * gb,
1559 Reserved: 0 * gb,
1560 SystemReserved: 1 * gb,
1561 TotalMemSize: 10 * gb,
1562 },
1563 hugepages1Gi: {
1564 Allocatable: 5 * gb,
1565 Free: 5 * gb,
1566 Reserved: 0,
1567 SystemReserved: 0,
1568 TotalMemSize: 5 * gb,
1569 },
1570 },
1571 },
1572 },
1573 expectedError: nil,
1574 },
1575 {
1576 description: "Correct removing of a multi NUMA container",
1577 removeContainerID: "fakeID2",
1578 policyName: policyTypeStatic,
1579 machineInfo: machineInfo,
1580 reserved: reserved,
1581 assignments: state.ContainerMemoryAssignments{
1582 "fakePod1": map[string][]state.Block{
1583 "fakeContainer1": {
1584 {
1585 NUMAAffinity: []int{0, 1},
1586 Type: v1.ResourceMemory,
1587 Size: 1 * gb,
1588 },
1589 {
1590 NUMAAffinity: []int{0, 1},
1591 Type: hugepages1Gi,
1592 Size: 1 * gb,
1593 },
1594 },
1595 "fakeContainer2": {
1596 {
1597 NUMAAffinity: []int{0, 1},
1598 Type: v1.ResourceMemory,
1599 Size: 12 * gb,
1600 },
1601 {
1602 NUMAAffinity: []int{0, 1},
1603 Type: hugepages1Gi,
1604 Size: 1 * gb,
1605 },
1606 },
1607 },
1608 },
1609 expectedAssignments: state.ContainerMemoryAssignments{
1610 "fakePod1": map[string][]state.Block{
1611 "fakeContainer1": {
1612 {
1613 NUMAAffinity: []int{0, 1},
1614 Type: v1.ResourceMemory,
1615 Size: 1 * gb,
1616 },
1617 {
1618 NUMAAffinity: []int{0, 1},
1619 Type: hugepages1Gi,
1620 Size: 1 * gb,
1621 },
1622 },
1623 },
1624 },
1625 machineState: state.NUMANodeMap{
1626 0: &state.NUMANodeState{
1627 Cells: []int{0, 1},
1628 NumberOfAssignments: 4,
1629 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1630 v1.ResourceMemory: {
1631 Allocatable: 9 * gb,
1632 Free: 0 * gb,
1633 Reserved: 9 * gb,
1634 SystemReserved: 1 * gb,
1635 TotalMemSize: 10 * gb,
1636 },
1637 hugepages1Gi: {
1638 Allocatable: 5 * gb,
1639 Free: 3 * gb,
1640 Reserved: 2 * gb,
1641 SystemReserved: 0 * gb,
1642 TotalMemSize: 5 * gb,
1643 },
1644 },
1645 },
1646 1: &state.NUMANodeState{
1647 Cells: []int{0, 1},
1648 NumberOfAssignments: 4,
1649 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1650 v1.ResourceMemory: {
1651 Allocatable: 9 * gb,
1652 Free: 5 * gb,
1653 Reserved: 4 * gb,
1654 SystemReserved: 1 * gb,
1655 TotalMemSize: 10 * gb,
1656 },
1657 hugepages1Gi: {
1658 Allocatable: 5 * gb,
1659 Free: 5 * gb,
1660 Reserved: 0,
1661 SystemReserved: 0,
1662 TotalMemSize: 5 * gb,
1663 },
1664 },
1665 },
1666 },
1667 expectedMachineState: state.NUMANodeMap{
1668 0: &state.NUMANodeState{
1669 Cells: []int{0, 1},
1670 NumberOfAssignments: 2,
1671 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1672 v1.ResourceMemory: {
1673 Allocatable: 9 * gb,
1674 Free: 9 * gb,
1675 Reserved: 0 * gb,
1676 SystemReserved: 1 * gb,
1677 TotalMemSize: 10 * gb,
1678 },
1679 hugepages1Gi: {
1680 Allocatable: 5 * gb,
1681 Free: 4 * gb,
1682 Reserved: 1 * gb,
1683 SystemReserved: 0,
1684 TotalMemSize: 5 * gb,
1685 },
1686 },
1687 },
1688 1: &state.NUMANodeState{
1689 Cells: []int{0, 1},
1690 NumberOfAssignments: 2,
1691 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1692 v1.ResourceMemory: {
1693 Allocatable: 9 * gb,
1694 Free: 8 * gb,
1695 Reserved: 1 * gb,
1696 SystemReserved: 1 * gb,
1697 TotalMemSize: 10 * gb,
1698 },
1699 hugepages1Gi: {
1700 Allocatable: 5 * gb,
1701 Free: 5 * gb,
1702 Reserved: 0,
1703 SystemReserved: 0,
1704 TotalMemSize: 5 * gb,
1705 },
1706 },
1707 },
1708 },
1709 expectedError: nil,
1710 },
1711 {
1712 description: "Should do nothing if container is not in containerMap",
1713 removeContainerID: "fakeID3",
1714 policyName: policyTypeStatic,
1715 machineInfo: machineInfo,
1716 reserved: reserved,
1717 assignments: state.ContainerMemoryAssignments{
1718 "fakePod1": map[string][]state.Block{
1719 "fakeContainer1": {
1720 {
1721 NUMAAffinity: []int{0},
1722 Type: v1.ResourceMemory,
1723 Size: 1 * gb,
1724 },
1725 {
1726 NUMAAffinity: []int{0},
1727 Type: hugepages1Gi,
1728 Size: 1 * gb,
1729 },
1730 },
1731 "fakeContainer2": {
1732 {
1733 NUMAAffinity: []int{0},
1734 Type: v1.ResourceMemory,
1735 Size: 1 * gb,
1736 },
1737 {
1738 NUMAAffinity: []int{0},
1739 Type: hugepages1Gi,
1740 Size: 1 * gb,
1741 },
1742 },
1743 },
1744 },
1745 expectedAssignments: state.ContainerMemoryAssignments{
1746 "fakePod1": map[string][]state.Block{
1747 "fakeContainer1": {
1748 {
1749 NUMAAffinity: []int{0},
1750 Type: v1.ResourceMemory,
1751 Size: 1 * gb,
1752 },
1753 {
1754 NUMAAffinity: []int{0},
1755 Type: hugepages1Gi,
1756 Size: 1 * gb,
1757 },
1758 },
1759 "fakeContainer2": {
1760 {
1761 NUMAAffinity: []int{0},
1762 Type: v1.ResourceMemory,
1763 Size: 1 * gb,
1764 },
1765 {
1766 NUMAAffinity: []int{0},
1767 Type: hugepages1Gi,
1768 Size: 1 * gb,
1769 },
1770 },
1771 },
1772 },
1773 machineState: state.NUMANodeMap{
1774 0: &state.NUMANodeState{
1775 Cells: []int{0},
1776 NumberOfAssignments: 4,
1777 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1778 v1.ResourceMemory: {
1779 Allocatable: 9 * gb,
1780 Free: 7 * gb,
1781 Reserved: 2 * gb,
1782 SystemReserved: 1 * gb,
1783 TotalMemSize: 10 * gb,
1784 },
1785 hugepages1Gi: {
1786 Allocatable: 5 * gb,
1787 Free: 3 * gb,
1788 Reserved: 2 * gb,
1789 SystemReserved: 0 * gb,
1790 TotalMemSize: 5 * gb,
1791 },
1792 },
1793 },
1794 1: &state.NUMANodeState{
1795 Cells: []int{1},
1796 NumberOfAssignments: 0,
1797 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1798 v1.ResourceMemory: {
1799 Allocatable: 9 * gb,
1800 Free: 9 * gb,
1801 Reserved: 0 * gb,
1802 SystemReserved: 1 * gb,
1803 TotalMemSize: 10 * gb,
1804 },
1805 hugepages1Gi: {
1806 Allocatable: 5 * gb,
1807 Free: 5 * gb,
1808 Reserved: 0,
1809 SystemReserved: 0,
1810 TotalMemSize: 5 * gb,
1811 },
1812 },
1813 },
1814 },
1815 expectedMachineState: state.NUMANodeMap{
1816 0: &state.NUMANodeState{
1817 Cells: []int{0},
1818 NumberOfAssignments: 4,
1819 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1820 v1.ResourceMemory: {
1821 Allocatable: 9 * gb,
1822 Free: 7 * gb,
1823 Reserved: 2 * gb,
1824 SystemReserved: 1 * gb,
1825 TotalMemSize: 10 * gb,
1826 },
1827 hugepages1Gi: {
1828 Allocatable: 5 * gb,
1829 Free: 3 * gb,
1830 Reserved: 2 * gb,
1831 SystemReserved: 0 * gb,
1832 TotalMemSize: 5 * gb,
1833 },
1834 },
1835 },
1836 1: &state.NUMANodeState{
1837 Cells: []int{1},
1838 NumberOfAssignments: 0,
1839 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1840 v1.ResourceMemory: {
1841 Allocatable: 9 * gb,
1842 Free: 9 * gb,
1843 Reserved: 0 * gb,
1844 SystemReserved: 1 * gb,
1845 TotalMemSize: 10 * gb,
1846 },
1847 hugepages1Gi: {
1848 Allocatable: 5 * gb,
1849 Free: 5 * gb,
1850 Reserved: 0,
1851 SystemReserved: 0,
1852 TotalMemSize: 5 * gb,
1853 },
1854 },
1855 },
1856 },
1857 expectedError: nil,
1858 },
1859 }
1860 for _, testCase := range testCases {
1861 t.Run(testCase.description, func(t *testing.T) {
1862 iniContainerMap := containermap.NewContainerMap()
1863 iniContainerMap.Add("fakePod1", "fakeContainer1", "fakeID1")
1864 iniContainerMap.Add("fakePod1", "fakeContainer2", "fakeID2")
1865 mgr := &manager{
1866 policy: returnPolicyByName(testCase),
1867 state: state.NewMemoryState(),
1868 containerMap: iniContainerMap,
1869 containerRuntime: mockRuntimeService{
1870 err: testCase.expectedError,
1871 },
1872 activePods: func() []*v1.Pod { return nil },
1873 podStatusProvider: mockPodStatusProvider{},
1874 }
1875 mgr.sourcesReady = &sourcesReadyStub{}
1876 mgr.state.SetMemoryAssignments(testCase.assignments)
1877 mgr.state.SetMachineState(testCase.machineState)
1878
1879 err := mgr.RemoveContainer(testCase.removeContainerID)
1880 if !reflect.DeepEqual(err, testCase.expectedError) {
1881 t.Errorf("Memory Manager RemoveContainer() error (%v), expected error: %v, but got: %v",
1882 testCase.description, testCase.expectedError, err)
1883 }
1884
1885 if !areContainerMemoryAssignmentsEqual(t, mgr.state.GetMemoryAssignments(), testCase.expectedAssignments) {
1886 t.Fatalf("Memory Manager RemoveContainer() inconsistent assignment, expected: %+v, but got: %+v, start %+v",
1887 testCase.expectedAssignments, mgr.state.GetMemoryAssignments(), testCase.expectedAssignments)
1888 }
1889
1890 if !areMachineStatesEqual(mgr.state.GetMachineState(), testCase.expectedMachineState) {
1891 t.Errorf("[test] %+v", mgr.state.GetMemoryAssignments())
1892 t.Errorf("[test] %+v, %+v", mgr.state.GetMachineState()[0].MemoryMap["memory"], mgr.state.GetMachineState()[1].MemoryMap["memory"])
1893 t.Fatalf("The actual machine state: %v is different from the expected one: %v", mgr.state.GetMachineState(), testCase.expectedMachineState)
1894 }
1895 })
1896 }
1897 }
1898
1899 func TestNewManager(t *testing.T) {
1900 machineInfo := returnMachineInfo()
1901 expectedReserved := systemReservedMemory{
1902 0: map[v1.ResourceName]uint64{
1903 v1.ResourceMemory: 1 * gb,
1904 },
1905 1: map[v1.ResourceName]uint64{
1906 v1.ResourceMemory: 1 * gb,
1907 },
1908 }
1909 testCases := []testMemoryManager{
1910 {
1911 description: "Successful creation of Memory Manager instance",
1912 policyName: policyTypeStatic,
1913 machineInfo: machineInfo,
1914 nodeAllocatableReservation: v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(2*gb, resource.BinarySI)},
1915 systemReservedMemory: []kubeletconfig.MemoryReservation{
1916 {
1917 NumaNode: 0,
1918 Limits: v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI)},
1919 },
1920 {
1921 NumaNode: 1,
1922 Limits: v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI)},
1923 },
1924 },
1925 affinity: topologymanager.NewFakeManager(),
1926 expectedError: nil,
1927 expectedReserved: expectedReserved,
1928 },
1929 {
1930 description: "Should return an error when systemReservedMemory (configured with kubelet flag) does not comply with Node Allocatable feature values",
1931 policyName: policyTypeStatic,
1932 machineInfo: machineInfo,
1933 nodeAllocatableReservation: v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(2*gb, resource.BinarySI)},
1934 systemReservedMemory: []kubeletconfig.MemoryReservation{
1935 {
1936 NumaNode: 0,
1937 Limits: v1.ResourceList{
1938 v1.ResourceMemory: *resource.NewQuantity(gb, resource.BinarySI),
1939 },
1940 },
1941 {
1942 NumaNode: 1,
1943 Limits: v1.ResourceList{
1944 v1.ResourceMemory: *resource.NewQuantity(2*gb, resource.BinarySI),
1945 },
1946 },
1947 },
1948 affinity: topologymanager.NewFakeManager(),
1949 expectedError: fmt.Errorf("the total amount \"3Gi\" of type %q is not equal to the value \"2Gi\" determined by Node Allocatable feature", v1.ResourceMemory),
1950 expectedReserved: expectedReserved,
1951 },
1952 {
1953 description: "Should return an error when memory reserved for system is empty (systemReservedMemory)",
1954 policyName: policyTypeStatic,
1955 machineInfo: machineInfo,
1956 nodeAllocatableReservation: v1.ResourceList{},
1957 systemReservedMemory: []kubeletconfig.MemoryReservation{},
1958 affinity: topologymanager.NewFakeManager(),
1959 expectedError: fmt.Errorf("[memorymanager] you should specify the system reserved memory"),
1960 expectedReserved: expectedReserved,
1961 },
1962 {
1963 description: "Should return an error when policy name is not correct",
1964 policyName: "fake",
1965 machineInfo: machineInfo,
1966 nodeAllocatableReservation: v1.ResourceList{},
1967 systemReservedMemory: []kubeletconfig.MemoryReservation{},
1968 affinity: topologymanager.NewFakeManager(),
1969 expectedError: fmt.Errorf("unknown policy: \"fake\""),
1970 expectedReserved: expectedReserved,
1971 },
1972 {
1973 description: "Should create manager with \"none\" policy",
1974 policyName: policyTypeNone,
1975 machineInfo: machineInfo,
1976 nodeAllocatableReservation: v1.ResourceList{},
1977 systemReservedMemory: []kubeletconfig.MemoryReservation{},
1978 affinity: topologymanager.NewFakeManager(),
1979 expectedError: nil,
1980 expectedReserved: expectedReserved,
1981 },
1982 }
1983 for _, testCase := range testCases {
1984 t.Run(testCase.description, func(t *testing.T) {
1985 stateFileDirectory, err := os.MkdirTemp("", "memory_manager_tests")
1986 if err != nil {
1987 t.Errorf("Cannot create state file: %s", err.Error())
1988 }
1989 defer os.RemoveAll(stateFileDirectory)
1990
1991 mgr, err := NewManager(string(testCase.policyName), &testCase.machineInfo, testCase.nodeAllocatableReservation, testCase.systemReservedMemory, stateFileDirectory, testCase.affinity)
1992
1993 if !reflect.DeepEqual(err, testCase.expectedError) {
1994 t.Errorf("Could not create the Memory Manager. Expected error: '%v', but got: '%v'",
1995 testCase.expectedError, err)
1996 }
1997
1998 if testCase.expectedError == nil {
1999 if mgr != nil {
2000 rawMgr := mgr.(*manager)
2001 if !reflect.DeepEqual(rawMgr.policy.Name(), string(testCase.policyName)) {
2002 t.Errorf("Could not create the Memory Manager. Expected policy name: %v, but got: %v",
2003 testCase.policyName, rawMgr.policy.Name())
2004 }
2005 if testCase.policyName == policyTypeStatic {
2006 if !reflect.DeepEqual(rawMgr.policy.(*staticPolicy).systemReserved, testCase.expectedReserved) {
2007 t.Errorf("Could not create the Memory Manager. Expected system reserved: %+v, but got: %+v",
2008 testCase.expectedReserved, rawMgr.policy.(*staticPolicy).systemReserved)
2009 }
2010 }
2011 } else {
2012 t.Errorf("Could not create the Memory Manager - manager is nil, but it should not be.")
2013 }
2014
2015 }
2016 })
2017 }
2018 }
2019
2020 func TestGetTopologyHints(t *testing.T) {
2021 testCases := []testMemoryManager{
2022 {
2023 description: "Successful hint generation",
2024 policyName: policyTypeStatic,
2025 machineInfo: returnMachineInfo(),
2026 reserved: systemReservedMemory{
2027 0: map[v1.ResourceName]uint64{
2028 v1.ResourceMemory: 1 * gb,
2029 },
2030 1: map[v1.ResourceName]uint64{
2031 v1.ResourceMemory: 1 * gb,
2032 },
2033 },
2034 assignments: state.ContainerMemoryAssignments{
2035 "fakePod1": map[string][]state.Block{
2036 "fakeContainer1": {
2037 {
2038 NUMAAffinity: []int{0},
2039 Type: v1.ResourceMemory,
2040 Size: 1 * gb,
2041 },
2042 {
2043 NUMAAffinity: []int{0},
2044 Type: hugepages1Gi,
2045 Size: 1 * gb,
2046 },
2047 },
2048 "fakeContainer2": {
2049 {
2050 NUMAAffinity: []int{0},
2051 Type: v1.ResourceMemory,
2052 Size: 1 * gb,
2053 },
2054 {
2055 NUMAAffinity: []int{0},
2056 Type: hugepages1Gi,
2057 Size: 1 * gb,
2058 },
2059 },
2060 },
2061 },
2062 machineState: state.NUMANodeMap{
2063 0: &state.NUMANodeState{
2064 Cells: []int{0},
2065 NumberOfAssignments: 4,
2066 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2067 v1.ResourceMemory: {
2068 Allocatable: 9 * gb,
2069 Free: 7 * gb,
2070 Reserved: 2 * gb,
2071 SystemReserved: 1 * gb,
2072 TotalMemSize: 10 * gb,
2073 },
2074 hugepages1Gi: {
2075 Allocatable: 5 * gb,
2076 Free: 3 * gb,
2077 Reserved: 2 * gb,
2078 SystemReserved: 0 * gb,
2079 TotalMemSize: 5 * gb,
2080 },
2081 },
2082 },
2083 1: &state.NUMANodeState{
2084 Cells: []int{1},
2085 NumberOfAssignments: 0,
2086 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2087 v1.ResourceMemory: {
2088 Allocatable: 9 * gb,
2089 Free: 9 * gb,
2090 Reserved: 0 * gb,
2091 SystemReserved: 1 * gb,
2092 TotalMemSize: 10 * gb,
2093 },
2094 hugepages1Gi: {
2095 Allocatable: 5 * gb,
2096 Free: 5 * gb,
2097 Reserved: 0,
2098 SystemReserved: 0,
2099 TotalMemSize: 5 * gb,
2100 },
2101 },
2102 },
2103 },
2104 expectedError: nil,
2105 expectedHints: map[string][]topologymanager.TopologyHint{
2106 string(v1.ResourceMemory): {
2107 {
2108 NUMANodeAffinity: newNUMAAffinity(0),
2109 Preferred: true,
2110 },
2111 {
2112 NUMANodeAffinity: newNUMAAffinity(1),
2113 Preferred: true,
2114 },
2115 },
2116 string(hugepages1Gi): {
2117 {
2118 NUMANodeAffinity: newNUMAAffinity(0),
2119 Preferred: true,
2120 },
2121 {
2122 NUMANodeAffinity: newNUMAAffinity(1),
2123 Preferred: true,
2124 },
2125 },
2126 },
2127 activePods: []*v1.Pod{
2128 {
2129 ObjectMeta: metav1.ObjectMeta{
2130 UID: "fakePod1",
2131 },
2132 Spec: v1.PodSpec{
2133 Containers: []v1.Container{
2134 {
2135 Name: "fakeContainer1",
2136 },
2137 {
2138 Name: "fakeContainer2",
2139 },
2140 },
2141 },
2142 },
2143 },
2144 },
2145 {
2146 description: "Successful hint generation",
2147 policyName: policyTypeStatic,
2148 machineInfo: returnMachineInfo(),
2149 reserved: systemReservedMemory{
2150 0: map[v1.ResourceName]uint64{
2151 v1.ResourceMemory: 1 * gb,
2152 },
2153 1: map[v1.ResourceName]uint64{
2154 v1.ResourceMemory: 1 * gb,
2155 },
2156 },
2157 assignments: state.ContainerMemoryAssignments{
2158 "fakePod1": map[string][]state.Block{
2159 "fakeContainer1": {
2160 {
2161 NUMAAffinity: []int{0},
2162 Type: v1.ResourceMemory,
2163 Size: 1 * gb,
2164 },
2165 {
2166 NUMAAffinity: []int{0},
2167 Type: hugepages1Gi,
2168 Size: 1 * gb,
2169 },
2170 },
2171 "fakeContainer2": {
2172 {
2173 NUMAAffinity: []int{0},
2174 Type: v1.ResourceMemory,
2175 Size: 1 * gb,
2176 },
2177 {
2178 NUMAAffinity: []int{0},
2179 Type: hugepages1Gi,
2180 Size: 1 * gb,
2181 },
2182 },
2183 },
2184 },
2185 machineState: state.NUMANodeMap{
2186 0: &state.NUMANodeState{
2187 Cells: []int{0},
2188 NumberOfAssignments: 4,
2189 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2190 v1.ResourceMemory: {
2191 Allocatable: 9 * gb,
2192 Free: 7 * gb,
2193 Reserved: 2 * gb,
2194 SystemReserved: 1 * gb,
2195 TotalMemSize: 10 * gb,
2196 },
2197 hugepages1Gi: {
2198 Allocatable: 5 * gb,
2199 Free: 3 * gb,
2200 Reserved: 2 * gb,
2201 SystemReserved: 0 * gb,
2202 TotalMemSize: 5 * gb,
2203 },
2204 },
2205 },
2206 1: &state.NUMANodeState{
2207 Cells: []int{1},
2208 NumberOfAssignments: 0,
2209 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2210 v1.ResourceMemory: {
2211 Allocatable: 9 * gb,
2212 Free: 9 * gb,
2213 Reserved: 0 * gb,
2214 SystemReserved: 1 * gb,
2215 TotalMemSize: 10 * gb,
2216 },
2217 hugepages1Gi: {
2218 Allocatable: 5 * gb,
2219 Free: 5 * gb,
2220 Reserved: 0,
2221 SystemReserved: 0,
2222 TotalMemSize: 5 * gb,
2223 },
2224 },
2225 },
2226 },
2227 expectedError: nil,
2228 expectedHints: map[string][]topologymanager.TopologyHint{
2229 string(v1.ResourceMemory): {
2230 {
2231 NUMANodeAffinity: newNUMAAffinity(0),
2232 Preferred: true,
2233 },
2234 {
2235 NUMANodeAffinity: newNUMAAffinity(1),
2236 Preferred: true,
2237 },
2238 {
2239 NUMANodeAffinity: newNUMAAffinity(0, 1),
2240 Preferred: false,
2241 },
2242 },
2243 string(hugepages1Gi): {
2244 {
2245 NUMANodeAffinity: newNUMAAffinity(0),
2246 Preferred: true,
2247 },
2248 {
2249 NUMANodeAffinity: newNUMAAffinity(1),
2250 Preferred: true,
2251 },
2252 {
2253 NUMANodeAffinity: newNUMAAffinity(0, 1),
2254 Preferred: false,
2255 },
2256 },
2257 },
2258 activePods: []*v1.Pod{},
2259 },
2260 }
2261
2262 for _, testCase := range testCases {
2263 t.Run(testCase.description, func(t *testing.T) {
2264 mgr := &manager{
2265 policy: returnPolicyByName(testCase),
2266 state: state.NewMemoryState(),
2267 containerMap: containermap.NewContainerMap(),
2268 containerRuntime: mockRuntimeService{
2269 err: nil,
2270 },
2271 activePods: func() []*v1.Pod { return testCase.activePods },
2272 podStatusProvider: mockPodStatusProvider{},
2273 }
2274 mgr.sourcesReady = &sourcesReadyStub{}
2275 mgr.state.SetMachineState(testCase.machineState.Clone())
2276 mgr.state.SetMemoryAssignments(testCase.assignments.Clone())
2277
2278 pod := getPod("fakePod2", "fakeContainer1", requirementsGuaranteed)
2279 container := &pod.Spec.Containers[0]
2280 hints := mgr.GetTopologyHints(pod, container)
2281 if !reflect.DeepEqual(hints, testCase.expectedHints) {
2282 t.Errorf("Hints were not generated correctly. Hints generated: %+v, hints expected: %+v",
2283 hints, testCase.expectedHints)
2284 }
2285 })
2286 }
2287 }
2288
2289 func TestAllocateAndAddPodWithInitContainers(t *testing.T) {
2290 testCases := []testMemoryManager{
2291 {
2292 description: "should remove init containers from the state file, once app container started",
2293 policyName: policyTypeStatic,
2294 machineInfo: returnMachineInfo(),
2295 assignments: state.ContainerMemoryAssignments{},
2296 expectedAssignments: state.ContainerMemoryAssignments{
2297 "pod1": map[string][]state.Block{
2298 "container1": {
2299 {
2300 NUMAAffinity: []int{0},
2301 Type: v1.ResourceMemory,
2302 Size: 4 * gb,
2303 },
2304 {
2305 NUMAAffinity: []int{0},
2306 Type: hugepages1Gi,
2307 Size: 4 * gb,
2308 },
2309 },
2310 },
2311 },
2312 machineState: state.NUMANodeMap{
2313 0: &state.NUMANodeState{
2314 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2315 v1.ResourceMemory: {
2316 Allocatable: 9728 * mb,
2317 Free: 9728 * mb,
2318 Reserved: 0,
2319 SystemReserved: 512 * mb,
2320 TotalMemSize: 10 * gb,
2321 },
2322 hugepages1Gi: {
2323 Allocatable: 5 * gb,
2324 Free: 5 * gb,
2325 Reserved: 0,
2326 SystemReserved: 0,
2327 TotalMemSize: 5 * gb,
2328 },
2329 },
2330 Cells: []int{0},
2331 },
2332 1: &state.NUMANodeState{
2333 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2334 v1.ResourceMemory: {
2335 Allocatable: 9728 * mb,
2336 Free: 9728 * mb,
2337 Reserved: 0,
2338 SystemReserved: 512 * mb,
2339 TotalMemSize: 10 * gb,
2340 },
2341 hugepages1Gi: {
2342 Allocatable: 5 * gb,
2343 Free: 5 * gb,
2344 Reserved: 0,
2345 SystemReserved: 0,
2346 TotalMemSize: 5 * gb,
2347 },
2348 },
2349 Cells: []int{1},
2350 },
2351 },
2352 expectedMachineState: state.NUMANodeMap{
2353 0: &state.NUMANodeState{
2354 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2355 v1.ResourceMemory: {
2356 Allocatable: 9728 * mb,
2357 Free: 5632 * mb,
2358 Reserved: 4 * gb,
2359 SystemReserved: 512 * mb,
2360 TotalMemSize: 10 * gb,
2361 },
2362 hugepages1Gi: {
2363 Allocatable: 5 * gb,
2364 Free: gb,
2365 Reserved: 4 * gb,
2366 SystemReserved: 0,
2367 TotalMemSize: 5 * gb,
2368 },
2369 },
2370 Cells: []int{0},
2371 NumberOfAssignments: 2,
2372 },
2373 1: &state.NUMANodeState{
2374 MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2375 v1.ResourceMemory: {
2376 Allocatable: 9728 * mb,
2377 Free: 9728 * mb,
2378 Reserved: 0,
2379 SystemReserved: 512 * mb,
2380 TotalMemSize: 10 * gb,
2381 },
2382 hugepages1Gi: {
2383 Allocatable: 5 * gb,
2384 Free: 5 * gb,
2385 Reserved: 0,
2386 SystemReserved: 0,
2387 TotalMemSize: 5 * gb,
2388 },
2389 },
2390 Cells: []int{1},
2391 },
2392 },
2393 reserved: systemReservedMemory{
2394 0: map[v1.ResourceName]uint64{
2395 v1.ResourceMemory: 512 * mb,
2396 },
2397 },
2398 podAllocate: getPodWithInitContainers(
2399 "pod1",
2400 []v1.Container{
2401 {
2402 Name: "container1",
2403 Resources: v1.ResourceRequirements{
2404 Limits: v1.ResourceList{
2405 v1.ResourceCPU: resource.MustParse("1000Mi"),
2406 v1.ResourceMemory: resource.MustParse("4Gi"),
2407 hugepages1Gi: resource.MustParse("4Gi"),
2408 },
2409 Requests: v1.ResourceList{
2410 v1.ResourceCPU: resource.MustParse("1000Mi"),
2411 v1.ResourceMemory: resource.MustParse("4Gi"),
2412 hugepages1Gi: resource.MustParse("4Gi"),
2413 },
2414 },
2415 },
2416 },
2417 []v1.Container{
2418 {
2419 Name: "initContainer1",
2420 Resources: v1.ResourceRequirements{
2421 Limits: v1.ResourceList{
2422 v1.ResourceCPU: resource.MustParse("1000Mi"),
2423 v1.ResourceMemory: resource.MustParse("7Gi"),
2424 hugepages1Gi: resource.MustParse("5Gi"),
2425 },
2426 Requests: v1.ResourceList{
2427 v1.ResourceCPU: resource.MustParse("1000Mi"),
2428 v1.ResourceMemory: resource.MustParse("7Gi"),
2429 hugepages1Gi: resource.MustParse("5Gi"),
2430 },
2431 },
2432 },
2433 },
2434 ),
2435 },
2436 }
2437
2438 for _, testCase := range testCases {
2439 t.Run(testCase.description, func(t *testing.T) {
2440 klog.InfoS("TestAllocateAndAddPodWithInitContainers", "name", testCase.description)
2441 mgr := &manager{
2442 policy: returnPolicyByName(testCase),
2443 state: state.NewMemoryState(),
2444 containerMap: containermap.NewContainerMap(),
2445 containerRuntime: mockRuntimeService{
2446 err: nil,
2447 },
2448 activePods: func() []*v1.Pod { return []*v1.Pod{testCase.podAllocate} },
2449 podStatusProvider: mockPodStatusProvider{},
2450 }
2451 mgr.sourcesReady = &sourcesReadyStub{}
2452 mgr.state.SetMachineState(testCase.machineState.Clone())
2453 mgr.state.SetMemoryAssignments(testCase.assignments.Clone())
2454
2455
2456 for i := range testCase.podAllocate.Spec.InitContainers {
2457 err := mgr.Allocate(testCase.podAllocate, &testCase.podAllocate.Spec.InitContainers[i])
2458 if !reflect.DeepEqual(err, testCase.expectedError) {
2459 t.Fatalf("The actual error %v is different from the expected one %v", err, testCase.expectedError)
2460 }
2461 }
2462
2463
2464 for i := range testCase.podAllocate.Spec.Containers {
2465 err := mgr.Allocate(testCase.podAllocate, &testCase.podAllocate.Spec.Containers[i])
2466 if !reflect.DeepEqual(err, testCase.expectedError) {
2467 t.Fatalf("The actual error %v is different from the expected one %v", err, testCase.expectedError)
2468 }
2469 }
2470
2471
2472 for i, initContainer := range testCase.podAllocate.Spec.InitContainers {
2473 mgr.AddContainer(testCase.podAllocate, &testCase.podAllocate.Spec.InitContainers[i], initContainer.Name)
2474 }
2475
2476
2477 for i, appContainer := range testCase.podAllocate.Spec.Containers {
2478 mgr.AddContainer(testCase.podAllocate, &testCase.podAllocate.Spec.Containers[i], appContainer.Name)
2479 }
2480
2481 assignments := mgr.state.GetMemoryAssignments()
2482 if !areContainerMemoryAssignmentsEqual(t, assignments, testCase.expectedAssignments) {
2483 t.Fatalf("Actual assignments %v are different from the expected %v", assignments, testCase.expectedAssignments)
2484 }
2485
2486 machineState := mgr.state.GetMachineState()
2487 if !areMachineStatesEqual(machineState, testCase.expectedMachineState) {
2488 t.Fatalf("The actual machine state %v is different from the expected %v", machineState, testCase.expectedMachineState)
2489 }
2490 })
2491 }
2492 }
2493
2494 func returnMachineInfo() cadvisorapi.MachineInfo {
2495 return cadvisorapi.MachineInfo{
2496 Topology: []cadvisorapi.Node{
2497 {
2498 Id: 0,
2499 Memory: 10 * gb,
2500 HugePages: []cadvisorapi.HugePagesInfo{
2501 {
2502 PageSize: pageSize1Gb,
2503 NumPages: 5,
2504 },
2505 },
2506 },
2507 {
2508 Id: 1,
2509 Memory: 10 * gb,
2510 HugePages: []cadvisorapi.HugePagesInfo{
2511 {
2512 PageSize: pageSize1Gb,
2513 NumPages: 5,
2514 },
2515 },
2516 },
2517 },
2518 }
2519 }
2520
View as plain text