1/*
2Copyright 2020 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package memorymanager
18
19import (
20	"fmt"
21	"reflect"
22	"testing"
23
24	"k8s.io/klog/v2"
25
26	cadvisorapi "github.com/google/cadvisor/info/v1"
27
28	v1 "k8s.io/api/core/v1"
29	"k8s.io/apimachinery/pkg/api/resource"
30	"k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
31	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
32	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
33)
34
35const (
36	mb           = 1024 * 1024
37	gb           = mb * 1024
38	pageSize1Gb  = 1048576
39	hugepages1Gi = v1.ResourceName(v1.ResourceHugePagesPrefix + "1Gi")
40)
41
42var (
43	requirementsGuaranteed = &v1.ResourceRequirements{
44		Limits: v1.ResourceList{
45			v1.ResourceCPU:    resource.MustParse("1000Mi"),
46			v1.ResourceMemory: resource.MustParse("1Gi"),
47			hugepages1Gi:      resource.MustParse("1Gi"),
48		},
49		Requests: v1.ResourceList{
50			v1.ResourceCPU:    resource.MustParse("1000Mi"),
51			v1.ResourceMemory: resource.MustParse("1Gi"),
52			hugepages1Gi:      resource.MustParse("1Gi"),
53		},
54	}
55	requirementsBurstable = &v1.ResourceRequirements{
56		Limits: v1.ResourceList{
57			v1.ResourceCPU:    resource.MustParse("1000Mi"),
58			v1.ResourceMemory: resource.MustParse("2Gi"),
59			hugepages1Gi:      resource.MustParse("2Gi"),
60		},
61		Requests: v1.ResourceList{
62			v1.ResourceCPU:    resource.MustParse("1000Mi"),
63			v1.ResourceMemory: resource.MustParse("1Gi"),
64			hugepages1Gi:      resource.MustParse("1Gi"),
65		},
66	}
67)
68
69func areMemoryBlocksEqual(mb1, mb2 []state.Block) bool {
70	if len(mb1) != len(mb2) {
71		return false
72	}
73
74	copyMemoryBlocks := make([]state.Block, len(mb2))
75	copy(copyMemoryBlocks, mb2)
76	for _, block := range mb1 {
77		for i, copyBlock := range copyMemoryBlocks {
78			if reflect.DeepEqual(block, copyBlock) {
79				// move the element that equals to the block to the end of the slice
80				copyMemoryBlocks[i] = copyMemoryBlocks[len(copyMemoryBlocks)-1]
81
82				// remove the last element from our slice
83				copyMemoryBlocks = copyMemoryBlocks[:len(copyMemoryBlocks)-1]
84
85				break
86			}
87		}
88	}
89
90	return len(copyMemoryBlocks) == 0
91}
92
93func areContainerMemoryAssignmentsEqual(t *testing.T, cma1, cma2 state.ContainerMemoryAssignments) bool {
94	if len(cma1) != len(cma2) {
95		return false
96	}
97
98	for podUID, container := range cma1 {
99		if _, ok := cma2[podUID]; !ok {
100			t.Logf("[memorymanager_tests] the assignment does not have pod UID %s", podUID)
101			return false
102		}
103
104		for containerName, memoryBlocks := range container {
105			if _, ok := cma2[podUID][containerName]; !ok {
106				t.Logf("[memorymanager_tests] the assignment does not have container name %s", containerName)
107				return false
108			}
109
110			if !areMemoryBlocksEqual(memoryBlocks, cma2[podUID][containerName]) {
111				t.Logf("[memorymanager_tests] assignments memory blocks are different: %v != %v", memoryBlocks, cma2[podUID][containerName])
112				return false
113			}
114		}
115	}
116	return true
117}
118
119type testStaticPolicy struct {
120	description           string
121	assignments           state.ContainerMemoryAssignments
122	expectedAssignments   state.ContainerMemoryAssignments
123	machineState          state.NUMANodeMap
124	expectedMachineState  state.NUMANodeMap
125	systemReserved        systemReservedMemory
126	expectedError         error
127	machineInfo           *cadvisorapi.MachineInfo
128	pod                   *v1.Pod
129	topologyHint          *topologymanager.TopologyHint
130	expectedTopologyHints map[string][]topologymanager.TopologyHint
131}
132
133func initTests(t *testing.T, testCase *testStaticPolicy, hint *topologymanager.TopologyHint) (Policy, state.State, error) {
134	manager := topologymanager.NewFakeManager()
135	if hint != nil {
136		manager = topologymanager.NewFakeManagerWithHint(hint)
137	}
138
139	p, err := NewPolicyStatic(testCase.machineInfo, testCase.systemReserved, manager)
140	if err != nil {
141		return nil, nil, err
142	}
143	s := state.NewMemoryState()
144	s.SetMachineState(testCase.machineState)
145	s.SetMemoryAssignments(testCase.assignments)
146	return p, s, nil
147}
148
149func newNUMAAffinity(bits ...int) bitmask.BitMask {
150	affinity, err := bitmask.NewBitMask(bits...)
151	if err != nil {
152		panic(err)
153	}
154	return affinity
155}
156
157func TestStaticPolicyNew(t *testing.T) {
158	testCases := []testStaticPolicy{
159		{
160			description:   "should fail, when machine does not have reserved memory for the system workloads",
161			expectedError: fmt.Errorf("[memorymanager] you should specify the system reserved memory"),
162		},
163		{
164			description: "should succeed, when at least one NUMA node has reserved memory",
165			systemReserved: systemReservedMemory{
166				0: map[v1.ResourceName]uint64{},
167				1: map[v1.ResourceName]uint64{
168					v1.ResourceMemory: 512 * mb,
169				},
170			},
171		},
172	}
173
174	for _, testCase := range testCases {
175		t.Run(testCase.description, func(t *testing.T) {
176			_, _, err := initTests(t, &testCase, nil)
177			if !reflect.DeepEqual(err, testCase.expectedError) {
178				t.Fatalf("The actual error: %v is different from the expected one: %v", err, testCase.expectedError)
179			}
180		})
181	}
182}
183
184func TestStaticPolicyName(t *testing.T) {
185	testCases := []testStaticPolicy{
186		{
187			description: "should return the correct policy name",
188			systemReserved: systemReservedMemory{
189				0: map[v1.ResourceName]uint64{
190					v1.ResourceMemory: 512 * mb,
191				},
192			},
193		},
194	}
195	for _, testCase := range testCases {
196		t.Run(testCase.description, func(t *testing.T) {
197			p, _, err := initTests(t, &testCase, nil)
198			if err != nil {
199				t.Fatalf("Unexpected error: %v", err)
200			}
201			if p.Name() != string(policyTypeStatic) {
202				t.Errorf("policy name is different, expected: %q, actual: %q", p.Name(), policyTypeStatic)
203			}
204		})
205	}
206}
207
208func TestStaticPolicyStart(t *testing.T) {
209	testCases := []testStaticPolicy{
210		{
211			description: "should fail, if machine state is empty, but it has memory assignments",
212			assignments: state.ContainerMemoryAssignments{
213				"pod": map[string][]state.Block{
214					"container1": {
215						{
216							NUMAAffinity: []int{0},
217							Type:         v1.ResourceMemory,
218							Size:         512 * mb,
219						},
220					},
221				},
222			},
223			systemReserved: systemReservedMemory{
224				0: map[v1.ResourceName]uint64{
225					v1.ResourceMemory: 512 * mb,
226				},
227			},
228			expectedError: fmt.Errorf("[memorymanager] machine state can not be empty when it has memory assignments"),
229		},
230		{
231			description:         "should fill the state with default values, when the state is empty",
232			expectedAssignments: state.ContainerMemoryAssignments{},
233			expectedMachineState: state.NUMANodeMap{
234				0: &state.NUMANodeState{
235					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
236						v1.ResourceMemory: {
237							Allocatable:    1536 * mb,
238							Free:           1536 * mb,
239							Reserved:       0,
240							SystemReserved: 512 * mb,
241							TotalMemSize:   3 * gb,
242						},
243						hugepages1Gi: {
244							Allocatable:    gb,
245							Free:           gb,
246							Reserved:       0,
247							SystemReserved: 0,
248							TotalMemSize:   gb,
249						},
250					},
251					NumberOfAssignments: 0,
252					Cells:               []int{0},
253				},
254			},
255			systemReserved: systemReservedMemory{
256				0: map[v1.ResourceName]uint64{
257					v1.ResourceMemory: 512 * mb,
258				},
259			},
260			machineInfo: &cadvisorapi.MachineInfo{
261				Topology: []cadvisorapi.Node{
262					{
263						Id:     0,
264						Memory: 3 * gb,
265						HugePages: []cadvisorapi.HugePagesInfo{
266							{
267								// size in KB
268								PageSize: pageSize1Gb,
269								NumPages: 1,
270							},
271						},
272					},
273				},
274			},
275		},
276		{
277			description: "should fail when machine state does not have all NUMA nodes",
278			machineState: state.NUMANodeMap{
279				0: &state.NUMANodeState{
280					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
281						v1.ResourceMemory: {
282							Allocatable:    1536 * mb,
283							Free:           1536 * mb,
284							Reserved:       0,
285							SystemReserved: 512 * mb,
286							TotalMemSize:   2 * gb,
287						},
288						hugepages1Gi: {
289							Allocatable:    gb,
290							Free:           gb,
291							Reserved:       0,
292							SystemReserved: 0,
293							TotalMemSize:   gb,
294						},
295					},
296					Cells:               []int{0},
297					NumberOfAssignments: 0,
298				},
299			},
300			systemReserved: systemReservedMemory{
301				0: map[v1.ResourceName]uint64{
302					v1.ResourceMemory: 512 * mb,
303				},
304			},
305			machineInfo: &cadvisorapi.MachineInfo{
306				Topology: []cadvisorapi.Node{
307					{
308						Id:     0,
309						Memory: 2 * gb,
310						HugePages: []cadvisorapi.HugePagesInfo{
311							{
312								// size in KB
313								PageSize: pageSize1Gb,
314								NumPages: 1,
315							},
316						},
317					},
318					{
319						Id:     1,
320						Memory: 2 * gb,
321						HugePages: []cadvisorapi.HugePagesInfo{
322							{
323								// size in KB
324								PageSize: pageSize1Gb,
325								NumPages: 1,
326							},
327						},
328					},
329				},
330			},
331			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
332		},
333		{
334			description: "should fail when machine state does not have memory resource",
335			machineState: state.NUMANodeMap{
336				0: &state.NUMANodeState{
337					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
338						hugepages1Gi: {
339							Allocatable:    gb,
340							Free:           gb,
341							Reserved:       0,
342							SystemReserved: 0,
343							TotalMemSize:   gb,
344						},
345					},
346					Cells:               []int{0},
347					NumberOfAssignments: 0,
348				},
349			},
350			machineInfo: &cadvisorapi.MachineInfo{
351				Topology: []cadvisorapi.Node{
352					{
353						Id:     0,
354						Memory: 2 * gb,
355						HugePages: []cadvisorapi.HugePagesInfo{
356							{
357								// size in KB
358								PageSize: pageSize1Gb,
359								NumPages: 1,
360							},
361						},
362					},
363				},
364			},
365			systemReserved: systemReservedMemory{
366				0: map[v1.ResourceName]uint64{
367					v1.ResourceMemory: 512 * mb,
368				},
369			},
370			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
371		},
372		{
373			description: "should fail when machine state has wrong size of total memory",
374			machineState: state.NUMANodeMap{
375				0: &state.NUMANodeState{
376					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
377						v1.ResourceMemory: {
378							Allocatable:    1536 * mb,
379							Free:           1536 * mb,
380							Reserved:       0,
381							SystemReserved: 512 * mb,
382							TotalMemSize:   1536 * mb,
383						},
384					},
385					Cells:               []int{0},
386					NumberOfAssignments: 0,
387				},
388			},
389			systemReserved: systemReservedMemory{
390				0: map[v1.ResourceName]uint64{
391					v1.ResourceMemory: 512 * mb,
392				},
393			},
394			machineInfo: &cadvisorapi.MachineInfo{
395				Topology: []cadvisorapi.Node{
396					{
397						Id:     0,
398						Memory: 2 * gb,
399						HugePages: []cadvisorapi.HugePagesInfo{
400							{
401								// size in KB
402								PageSize: pageSize1Gb,
403								NumPages: 1,
404							},
405						},
406					},
407				},
408			},
409			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
410		},
411		{
412			description: "should fail when machine state has wrong size of system reserved memory",
413			machineState: state.NUMANodeMap{
414				0: &state.NUMANodeState{
415					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
416						v1.ResourceMemory: {
417							Allocatable:    1536 * mb,
418							Free:           1536 * mb,
419							Reserved:       0,
420							SystemReserved: 1024,
421							TotalMemSize:   2 * gb,
422						},
423					},
424					Cells:               []int{0},
425					NumberOfAssignments: 0,
426				},
427			},
428			systemReserved: systemReservedMemory{
429				0: map[v1.ResourceName]uint64{
430					v1.ResourceMemory: 512 * mb,
431				},
432			},
433			machineInfo: &cadvisorapi.MachineInfo{
434				Topology: []cadvisorapi.Node{
435					{
436						Id:     0,
437						Memory: 2 * gb,
438						HugePages: []cadvisorapi.HugePagesInfo{
439							{
440								// size in KB
441								PageSize: pageSize1Gb,
442								NumPages: 1,
443							},
444						},
445					},
446				},
447			},
448			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
449		},
450		{
451			description: "should fail when machine state reserved memory is different from the memory of all containers memory assignments",
452			assignments: state.ContainerMemoryAssignments{
453				"pod": map[string][]state.Block{
454					"container1": {
455						{
456							NUMAAffinity: []int{0},
457							Type:         v1.ResourceMemory,
458							Size:         512 * mb,
459						},
460					},
461				},
462			},
463			machineState: state.NUMANodeMap{
464				0: &state.NUMANodeState{
465					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
466						v1.ResourceMemory: {
467							Allocatable:    1536 * mb,
468							Free:           1536 * mb,
469							Reserved:       0,
470							SystemReserved: 512 * mb,
471							TotalMemSize:   2 * gb,
472						},
473					},
474					Cells:               []int{0},
475					NumberOfAssignments: 1,
476				},
477			},
478			systemReserved: systemReservedMemory{
479				0: map[v1.ResourceName]uint64{
480					v1.ResourceMemory: 512 * mb,
481				},
482			},
483			machineInfo: &cadvisorapi.MachineInfo{
484				Topology: []cadvisorapi.Node{
485					{
486						Id:     0,
487						Memory: 2 * gb,
488						HugePages: []cadvisorapi.HugePagesInfo{
489							{
490								// size in KB
491								PageSize: pageSize1Gb,
492								NumPages: 1,
493							},
494						},
495					},
496				},
497			},
498			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
499		},
500		{
501			description: "should fail when machine state has wrong size of hugepages",
502			machineState: state.NUMANodeMap{
503				0: &state.NUMANodeState{
504					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
505						v1.ResourceMemory: {
506							Allocatable:    1536 * mb,
507							Free:           1536 * mb,
508							Reserved:       0,
509							SystemReserved: 512 * mb,
510							TotalMemSize:   2 * gb,
511						},
512						hugepages1Gi: {
513							Allocatable:    gb,
514							Free:           gb,
515							Reserved:       0,
516							SystemReserved: 0,
517							TotalMemSize:   gb,
518						},
519					},
520					Cells:               []int{0},
521					NumberOfAssignments: 0,
522				},
523			},
524			systemReserved: systemReservedMemory{
525				0: map[v1.ResourceName]uint64{
526					v1.ResourceMemory: 512 * mb,
527				},
528			},
529			machineInfo: &cadvisorapi.MachineInfo{
530				Topology: []cadvisorapi.Node{
531					{
532						Id:     0,
533						Memory: 2 * gb,
534						HugePages: []cadvisorapi.HugePagesInfo{
535							{
536								// size in KB
537								PageSize: pageSize1Gb,
538								NumPages: 2,
539							},
540						},
541					},
542				},
543			},
544			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
545		},
546		{
547			description: "should fail when machine state has wrong size of system reserved hugepages",
548			machineState: state.NUMANodeMap{
549				0: &state.NUMANodeState{
550					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
551						v1.ResourceMemory: {
552							Allocatable:    1536 * mb,
553							Free:           1536 * mb,
554							Reserved:       0,
555							SystemReserved: 512 * mb,
556							TotalMemSize:   2 * gb,
557						},
558						hugepages1Gi: {
559							Allocatable:    gb,
560							Free:           gb,
561							Reserved:       0,
562							SystemReserved: gb,
563							TotalMemSize:   2 * gb,
564						},
565					},
566					Cells:               []int{0},
567					NumberOfAssignments: 0,
568				},
569			},
570			systemReserved: systemReservedMemory{
571				0: map[v1.ResourceName]uint64{
572					v1.ResourceMemory: 512 * mb,
573				},
574			},
575			machineInfo: &cadvisorapi.MachineInfo{
576				Topology: []cadvisorapi.Node{
577					{
578						Id:     0,
579						Memory: 2 * gb,
580						HugePages: []cadvisorapi.HugePagesInfo{
581							{
582								// size in KB
583								PageSize: pageSize1Gb,
584								NumPages: 2,
585							},
586						},
587					},
588				},
589			},
590			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
591		},
592		{
593			description: "should fail when the hugepages reserved machine state is different from the hugepages of all containers memory assignments",
594			assignments: state.ContainerMemoryAssignments{
595				"pod1": map[string][]state.Block{
596					"container1": {
597						{
598							NUMAAffinity: []int{0},
599							Type:         hugepages1Gi,
600							Size:         gb,
601						},
602					},
603				},
604				"pod2": map[string][]state.Block{
605					"container2": {
606						{
607							NUMAAffinity: []int{0},
608							Type:         hugepages1Gi,
609							Size:         gb,
610						},
611					},
612				},
613			},
614			machineState: state.NUMANodeMap{
615				0: &state.NUMANodeState{
616					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
617						v1.ResourceMemory: {
618							Allocatable:    1536 * mb,
619							Free:           1536 * mb,
620							Reserved:       0,
621							SystemReserved: 512 * mb,
622							TotalMemSize:   2 * gb,
623						},
624						hugepages1Gi: {
625							Allocatable:    4 * gb,
626							Free:           gb,
627							Reserved:       3 * gb,
628							SystemReserved: 0,
629							TotalMemSize:   4 * gb,
630						},
631					},
632					Cells:               []int{0},
633					NumberOfAssignments: 2,
634				},
635			},
636			systemReserved: systemReservedMemory{
637				0: map[v1.ResourceName]uint64{
638					v1.ResourceMemory: 512 * mb,
639				},
640			},
641			machineInfo: &cadvisorapi.MachineInfo{
642				Topology: []cadvisorapi.Node{
643					{
644						Id:     0,
645						Memory: 2 * gb,
646						HugePages: []cadvisorapi.HugePagesInfo{
647							{
648								// size in KB
649								PageSize: pageSize1Gb,
650								NumPages: 4,
651							},
652						},
653					},
654				},
655			},
656			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
657		},
658		{
659			description: "should fail when machine state does not have NUMA node that used under the memory assignment",
660			assignments: state.ContainerMemoryAssignments{
661				"pod1": map[string][]state.Block{
662					"container1": {
663						{
664							NUMAAffinity: []int{1},
665							Type:         v1.ResourceMemory,
666							Size:         gb,
667						},
668					},
669				},
670			},
671			machineState: state.NUMANodeMap{
672				0: &state.NUMANodeState{
673					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
674						v1.ResourceMemory: {
675							Allocatable:    1536 * mb,
676							Free:           1536 * mb,
677							Reserved:       0,
678							SystemReserved: 512 * mb,
679							TotalMemSize:   2 * gb,
680						},
681						hugepages1Gi: {
682							Allocatable:    gb,
683							Free:           gb,
684							Reserved:       0,
685							SystemReserved: 0,
686							TotalMemSize:   gb,
687						},
688					},
689					Cells:               []int{0},
690					NumberOfAssignments: 0,
691				},
692			},
693			systemReserved: systemReservedMemory{
694				0: map[v1.ResourceName]uint64{
695					v1.ResourceMemory: 512 * mb,
696				},
697			},
698			machineInfo: &cadvisorapi.MachineInfo{
699				Topology: []cadvisorapi.Node{
700					{
701						Id:     0,
702						Memory: 2 * gb,
703						HugePages: []cadvisorapi.HugePagesInfo{
704							{
705								// size in KB
706								PageSize: pageSize1Gb,
707								NumPages: 1,
708							},
709						},
710					},
711				},
712			},
713			expectedError: fmt.Errorf("[memorymanager] (pod: pod1, container: container1) the memory assignment uses the NUMA that does not exist"),
714		},
715		{
716			description: "should fail when machine state does not have resource that used under the memory assignment",
717			assignments: state.ContainerMemoryAssignments{
718				"pod1": map[string][]state.Block{
719					"container1": {
720						{
721							NUMAAffinity: []int{0},
722							Type:         v1.ResourceMemory,
723							Size:         gb,
724						},
725						{
726							NUMAAffinity: []int{0},
727							Type:         hugepages2M,
728							Size:         gb,
729						},
730					},
731				},
732			},
733			machineState: state.NUMANodeMap{
734				0: &state.NUMANodeState{
735					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
736						v1.ResourceMemory: {
737							Allocatable:    1536 * mb,
738							Free:           1536 * mb,
739							Reserved:       0,
740							SystemReserved: 512 * mb,
741							TotalMemSize:   2 * gb,
742						},
743						hugepages1Gi: {
744							Allocatable:    gb,
745							Free:           gb,
746							Reserved:       0,
747							SystemReserved: 0,
748							TotalMemSize:   gb,
749						},
750					},
751					Cells:               []int{0},
752					NumberOfAssignments: 2,
753				},
754			},
755			systemReserved: systemReservedMemory{
756				0: map[v1.ResourceName]uint64{
757					v1.ResourceMemory: 512 * mb,
758				},
759			},
760			machineInfo: &cadvisorapi.MachineInfo{
761				Topology: []cadvisorapi.Node{
762					{
763						Id:     0,
764						Memory: 2 * gb,
765						HugePages: []cadvisorapi.HugePagesInfo{
766							{
767								// size in KB
768								PageSize: pageSize1Gb,
769								NumPages: 1,
770							},
771						},
772					},
773				},
774			},
775			expectedError: fmt.Errorf("[memorymanager] (pod: pod1, container: container1) the memory assignment uses memory resource that does not exist"),
776		},
777		{
778			description: "should fail when machine state number of assignments is different from the expected one",
779			assignments: state.ContainerMemoryAssignments{
780				"pod1": map[string][]state.Block{
781					"container1": {
782						{
783							NUMAAffinity: []int{0},
784							Type:         v1.ResourceMemory,
785							Size:         gb,
786						},
787						{
788							NUMAAffinity: []int{0},
789							Type:         hugepages1Gi,
790							Size:         gb,
791						},
792					},
793				},
794			},
795			machineState: state.NUMANodeMap{
796				0: &state.NUMANodeState{
797					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
798						v1.ResourceMemory: {
799							Allocatable:    1536 * mb,
800							Free:           1536 * mb,
801							Reserved:       0,
802							SystemReserved: 512 * mb,
803							TotalMemSize:   2 * gb,
804						},
805						hugepages1Gi: {
806							Allocatable:    gb,
807							Free:           gb,
808							Reserved:       0,
809							SystemReserved: 0,
810							TotalMemSize:   gb,
811						},
812					},
813					Cells:               []int{0},
814					NumberOfAssignments: 1,
815				},
816			},
817			systemReserved: systemReservedMemory{
818				0: map[v1.ResourceName]uint64{
819					v1.ResourceMemory: 512 * mb,
820				},
821			},
822			machineInfo: &cadvisorapi.MachineInfo{
823				Topology: []cadvisorapi.Node{
824					{
825						Id:     0,
826						Memory: 2 * gb,
827						HugePages: []cadvisorapi.HugePagesInfo{
828							{
829								// size in KB
830								PageSize: pageSize1Gb,
831								NumPages: 1,
832							},
833						},
834					},
835				},
836			},
837			expectedError: fmt.Errorf("[memorymanager] the expected machine state is different from the real one"),
838		},
839		{
840			description: "should validate cross NUMA reserved memory vs container assignments",
841			assignments: state.ContainerMemoryAssignments{
842				"pod1": map[string][]state.Block{
843					"container1": {
844						{
845							NUMAAffinity: []int{0, 1},
846							Type:         v1.ResourceMemory,
847							Size:         768 * mb,
848						},
849						{
850							NUMAAffinity: []int{0, 1},
851							Type:         hugepages1Gi,
852							Size:         gb,
853						},
854					},
855				},
856				"pod2": map[string][]state.Block{
857					"container2": {
858						{
859							NUMAAffinity: []int{0, 1},
860							Type:         v1.ResourceMemory,
861							Size:         256 * mb,
862						},
863						{
864							NUMAAffinity: []int{0, 1},
865							Type:         hugepages1Gi,
866							Size:         gb,
867						},
868					},
869				},
870			},
871			expectedAssignments: state.ContainerMemoryAssignments{
872				"pod1": map[string][]state.Block{
873					"container1": {
874						{
875							NUMAAffinity: []int{0, 1},
876							Type:         v1.ResourceMemory,
877							Size:         768 * mb,
878						},
879						{
880							NUMAAffinity: []int{0, 1},
881							Type:         hugepages1Gi,
882							Size:         gb,
883						},
884					},
885				},
886				"pod2": map[string][]state.Block{
887					"container2": {
888						{
889							NUMAAffinity: []int{0, 1},
890							Type:         v1.ResourceMemory,
891							Size:         256 * mb,
892						},
893						{
894							NUMAAffinity: []int{0, 1},
895							Type:         hugepages1Gi,
896							Size:         gb,
897						},
898					},
899				},
900			},
901			machineState: state.NUMANodeMap{
902				0: &state.NUMANodeState{
903					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
904						v1.ResourceMemory: {
905							Allocatable:    640 * mb,
906							Free:           0,
907							Reserved:       640 * mb,
908							SystemReserved: 512 * mb,
909							TotalMemSize:   2176 * mb,
910						},
911						hugepages1Gi: {
912							Allocatable:    gb,
913							Free:           0,
914							Reserved:       gb,
915							SystemReserved: 0,
916							TotalMemSize:   gb,
917						},
918					},
919					Cells:               []int{0, 1},
920					NumberOfAssignments: 4,
921				},
922				1: &state.NUMANodeState{
923					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
924						v1.ResourceMemory: {
925							Allocatable:    640 * mb,
926							Free:           256 * mb,
927							Reserved:       384 * mb,
928							SystemReserved: 512 * mb,
929							TotalMemSize:   2176 * mb,
930						},
931						hugepages1Gi: {
932							Allocatable:    gb,
933							Free:           0,
934							Reserved:       gb,
935							SystemReserved: 0,
936							TotalMemSize:   gb,
937						},
938					},
939					Cells:               []int{0, 1},
940					NumberOfAssignments: 4,
941				},
942			},
943			expectedMachineState: state.NUMANodeMap{
944				0: &state.NUMANodeState{
945					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
946						v1.ResourceMemory: {
947							Allocatable:    640 * mb,
948							Free:           0,
949							Reserved:       640 * mb,
950							SystemReserved: 512 * mb,
951							TotalMemSize:   2176 * mb,
952						},
953						hugepages1Gi: {
954							Allocatable:    gb,
955							Free:           0,
956							Reserved:       gb,
957							SystemReserved: 0,
958							TotalMemSize:   gb,
959						},
960					},
961					Cells:               []int{0, 1},
962					NumberOfAssignments: 4,
963				},
964				1: &state.NUMANodeState{
965					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
966						v1.ResourceMemory: {
967							Allocatable:    640 * mb,
968							Free:           256 * mb,
969							Reserved:       384 * mb,
970							SystemReserved: 512 * mb,
971							TotalMemSize:   2176 * mb,
972						},
973						hugepages1Gi: {
974							Allocatable:    gb,
975							Free:           0,
976							Reserved:       gb,
977							SystemReserved: 0,
978							TotalMemSize:   gb,
979						},
980					},
981					Cells:               []int{0, 1},
982					NumberOfAssignments: 4,
983				},
984			},
985			systemReserved: systemReservedMemory{
986				0: map[v1.ResourceName]uint64{
987					v1.ResourceMemory: 512 * mb,
988				},
989				1: map[v1.ResourceName]uint64{
990					v1.ResourceMemory: 512 * mb,
991				},
992			},
993			machineInfo: &cadvisorapi.MachineInfo{
994				Topology: []cadvisorapi.Node{
995					{
996						Id:     0,
997						Memory: 2176 * mb,
998						HugePages: []cadvisorapi.HugePagesInfo{
999							{
1000								// size in KB
1001								PageSize: pageSize1Gb,
1002								NumPages: 1,
1003							},
1004						},
1005					},
1006					{
1007						Id:     1,
1008						Memory: 2176 * mb,
1009						HugePages: []cadvisorapi.HugePagesInfo{
1010							{
1011								// size in KB
1012								PageSize: pageSize1Gb,
1013								NumPages: 1,
1014							},
1015						},
1016					},
1017				},
1018			},
1019		},
1020	}
1021
1022	for _, testCase := range testCases {
1023		t.Run(testCase.description, func(t *testing.T) {
1024			t.Logf("[Start] %s", testCase.description)
1025			p, s, err := initTests(t, &testCase, nil)
1026			if err != nil {
1027				t.Fatalf("Unexpected error: %v", err)
1028			}
1029
1030			err = p.Start(s)
1031			if !reflect.DeepEqual(err, testCase.expectedError) {
1032				t.Fatalf("The actual error: %v is different from the expected one: %v", err, testCase.expectedError)
1033			}
1034
1035			if err != nil {
1036				return
1037			}
1038
1039			assignments := s.GetMemoryAssignments()
1040			if !areContainerMemoryAssignmentsEqual(t, assignments, testCase.expectedAssignments) {
1041				t.Fatalf("Actual assignments: %v is different from the expected one: %v", assignments, testCase.expectedAssignments)
1042			}
1043
1044			machineState := s.GetMachineState()
1045			if !areMachineStatesEqual(machineState, testCase.expectedMachineState) {
1046				t.Fatalf("The actual machine state: %v is different from the expected one: %v", machineState, testCase.expectedMachineState)
1047			}
1048		})
1049	}
1050}
1051
1052func TestStaticPolicyAllocate(t *testing.T) {
1053	testCases := []testStaticPolicy{
1054		{
1055			description:         "should do nothing for non-guaranteed pods",
1056			expectedAssignments: state.ContainerMemoryAssignments{},
1057			machineState: state.NUMANodeMap{
1058				0: &state.NUMANodeState{
1059					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1060						v1.ResourceMemory: {
1061							Allocatable:    1536 * mb,
1062							Free:           1536 * mb,
1063							Reserved:       0,
1064							SystemReserved: 512 * mb,
1065							TotalMemSize:   2 * gb,
1066						},
1067						hugepages1Gi: {
1068							Allocatable:    gb,
1069							Free:           gb,
1070							Reserved:       0,
1071							SystemReserved: 0,
1072							TotalMemSize:   gb,
1073						},
1074					},
1075					Cells: []int{},
1076				},
1077			},
1078			expectedMachineState: state.NUMANodeMap{
1079				0: &state.NUMANodeState{
1080					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1081						v1.ResourceMemory: {
1082							Allocatable:    1536 * mb,
1083							Free:           1536 * mb,
1084							Reserved:       0,
1085							SystemReserved: 512 * mb,
1086							TotalMemSize:   2 * gb,
1087						},
1088						hugepages1Gi: {
1089							Allocatable:    gb,
1090							Free:           gb,
1091							Reserved:       0,
1092							SystemReserved: 0,
1093							TotalMemSize:   gb,
1094						},
1095					},
1096					Cells: []int{},
1097				},
1098			},
1099			systemReserved: systemReservedMemory{
1100				0: map[v1.ResourceName]uint64{
1101					v1.ResourceMemory: 512 * mb,
1102				},
1103			},
1104			pod:                   getPod("pod1", "container1", requirementsBurstable),
1105			expectedTopologyHints: nil,
1106			topologyHint:          &topologymanager.TopologyHint{},
1107		},
1108		{
1109			description: "should do nothing once container already exists under the state file",
1110			assignments: state.ContainerMemoryAssignments{
1111				"pod1": map[string][]state.Block{
1112					"container1": {
1113						{
1114							NUMAAffinity: []int{0},
1115							Type:         v1.ResourceMemory,
1116							Size:         gb,
1117						},
1118					},
1119				},
1120			},
1121			expectedAssignments: state.ContainerMemoryAssignments{
1122				"pod1": map[string][]state.Block{
1123					"container1": {
1124						{
1125							NUMAAffinity: []int{0},
1126							Type:         v1.ResourceMemory,
1127							Size:         gb,
1128						},
1129					},
1130				},
1131			},
1132			machineState: state.NUMANodeMap{
1133				0: &state.NUMANodeState{
1134					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1135						v1.ResourceMemory: {
1136							Allocatable:    1536 * mb,
1137							Free:           512 * mb,
1138							Reserved:       1024 * mb,
1139							SystemReserved: 512 * mb,
1140							TotalMemSize:   2 * gb,
1141						},
1142						hugepages1Gi: {
1143							Allocatable:    gb,
1144							Free:           gb,
1145							Reserved:       0,
1146							SystemReserved: 0,
1147							TotalMemSize:   gb,
1148						},
1149					},
1150					Cells: []int{},
1151				},
1152			},
1153			expectedMachineState: state.NUMANodeMap{
1154				0: &state.NUMANodeState{
1155					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1156						v1.ResourceMemory: {
1157							Allocatable:    1536 * mb,
1158							Free:           512 * mb,
1159							Reserved:       1024 * mb,
1160							SystemReserved: 512 * mb,
1161							TotalMemSize:   2 * gb,
1162						},
1163						hugepages1Gi: {
1164							Allocatable:    gb,
1165							Free:           gb,
1166							Reserved:       0,
1167							SystemReserved: 0,
1168							TotalMemSize:   gb,
1169						},
1170					},
1171					Cells: []int{},
1172				},
1173			},
1174			systemReserved: systemReservedMemory{
1175				0: map[v1.ResourceName]uint64{
1176					v1.ResourceMemory: 512 * mb,
1177				},
1178			},
1179			pod:                   getPod("pod1", "container1", requirementsGuaranteed),
1180			expectedTopologyHints: nil,
1181			topologyHint:          &topologymanager.TopologyHint{},
1182		},
1183		{
1184			description: "should calculate a default topology hint when no NUMA affinity was provided by the topology manager hint",
1185			assignments: state.ContainerMemoryAssignments{},
1186			expectedAssignments: state.ContainerMemoryAssignments{
1187				"pod1": map[string][]state.Block{
1188					"container1": {
1189						{
1190							NUMAAffinity: []int{0},
1191							Type:         v1.ResourceMemory,
1192							Size:         gb,
1193						},
1194						{
1195							NUMAAffinity: []int{0},
1196							Type:         hugepages1Gi,
1197							Size:         gb,
1198						},
1199					},
1200				},
1201			},
1202			machineState: state.NUMANodeMap{
1203				0: &state.NUMANodeState{
1204					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1205						v1.ResourceMemory: {
1206							Allocatable:    1536 * mb,
1207							Free:           1536 * mb,
1208							Reserved:       0,
1209							SystemReserved: 512 * mb,
1210							TotalMemSize:   2 * gb,
1211						},
1212						hugepages1Gi: {
1213							Allocatable:    gb,
1214							Free:           gb,
1215							Reserved:       0,
1216							SystemReserved: 0,
1217							TotalMemSize:   gb,
1218						},
1219					},
1220					Cells: []int{0},
1221				},
1222			},
1223			expectedMachineState: state.NUMANodeMap{
1224				0: &state.NUMANodeState{
1225					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1226						v1.ResourceMemory: {
1227							Allocatable:    1536 * mb,
1228							Free:           512 * mb,
1229							Reserved:       1024 * mb,
1230							SystemReserved: 512 * mb,
1231							TotalMemSize:   2 * gb,
1232						},
1233						hugepages1Gi: {
1234							Allocatable:    gb,
1235							Free:           0,
1236							Reserved:       gb,
1237							SystemReserved: 0,
1238							TotalMemSize:   gb,
1239						},
1240					},
1241					Cells:               []int{0},
1242					NumberOfAssignments: 2,
1243				},
1244			},
1245			systemReserved: systemReservedMemory{
1246				0: map[v1.ResourceName]uint64{
1247					v1.ResourceMemory: 512 * mb,
1248				},
1249			},
1250			pod:          getPod("pod1", "container1", requirementsGuaranteed),
1251			topologyHint: &topologymanager.TopologyHint{},
1252		},
1253		{
1254			description: "should fail when no NUMA affinity was provided under the topology manager hint and calculation of the default hint failed",
1255			assignments: state.ContainerMemoryAssignments{
1256				"pod1": map[string][]state.Block{
1257					"container1": {
1258						{
1259							NUMAAffinity: []int{0},
1260							Type:         v1.ResourceMemory,
1261							Size:         gb,
1262						},
1263						{
1264							NUMAAffinity: []int{0},
1265							Type:         hugepages1Gi,
1266							Size:         gb,
1267						},
1268					},
1269				},
1270			},
1271			machineState: state.NUMANodeMap{
1272				0: &state.NUMANodeState{
1273					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1274						v1.ResourceMemory: {
1275							Allocatable:    1536 * mb,
1276							Free:           512 * mb,
1277							Reserved:       1024 * mb,
1278							SystemReserved: 512 * mb,
1279							TotalMemSize:   2 * gb,
1280						},
1281						hugepages1Gi: {
1282							Allocatable:    gb,
1283							Free:           0,
1284							Reserved:       gb,
1285							SystemReserved: 0,
1286							TotalMemSize:   gb,
1287						},
1288					},
1289					Cells:               []int{0},
1290					NumberOfAssignments: 2,
1291				},
1292			},
1293			systemReserved: systemReservedMemory{
1294				0: map[v1.ResourceName]uint64{
1295					v1.ResourceMemory: 512 * mb,
1296				},
1297			},
1298			pod:           getPod("pod2", "container2", requirementsGuaranteed),
1299			expectedError: fmt.Errorf("[memorymanager] failed to get the default NUMA affinity, no NUMA nodes with enough memory is available"),
1300			topologyHint:  &topologymanager.TopologyHint{},
1301		},
1302		{
1303			description: "should fail when no NUMA affinity was provided under the topology manager preferred hint and default hint has preferred false",
1304			assignments: state.ContainerMemoryAssignments{
1305				"pod1": map[string][]state.Block{
1306					"container1": {
1307						{
1308							NUMAAffinity: []int{0},
1309							Type:         v1.ResourceMemory,
1310							Size:         512 * mb,
1311						},
1312					},
1313				},
1314			},
1315			machineState: state.NUMANodeMap{
1316				0: &state.NUMANodeState{
1317					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1318						v1.ResourceMemory: {
1319							Allocatable:    gb,
1320							Free:           512 * mb,
1321							Reserved:       512 * mb,
1322							SystemReserved: 512 * mb,
1323							TotalMemSize:   1536 * mb,
1324						},
1325						hugepages1Gi: {
1326							Allocatable:    gb,
1327							Free:           gb,
1328							Reserved:       0,
1329							SystemReserved: 0,
1330							TotalMemSize:   gb,
1331						},
1332					},
1333					Cells:               []int{0},
1334					NumberOfAssignments: 1,
1335				},
1336				1: &state.NUMANodeState{
1337					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1338						v1.ResourceMemory: {
1339							Allocatable:    512 * mb,
1340							Free:           512 * mb,
1341							Reserved:       0,
1342							SystemReserved: 512 * mb,
1343							TotalMemSize:   1536 * mb,
1344						},
1345						hugepages1Gi: {
1346							Allocatable:    gb,
1347							Free:           gb,
1348							Reserved:       0,
1349							SystemReserved: 0,
1350							TotalMemSize:   gb,
1351						},
1352					},
1353					Cells:               []int{1},
1354					NumberOfAssignments: 0,
1355				},
1356				2: &state.NUMANodeState{
1357					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1358						v1.ResourceMemory: {
1359							Allocatable:    512 * mb,
1360							Free:           512 * mb,
1361							Reserved:       0,
1362							SystemReserved: 512 * mb,
1363							TotalMemSize:   1536 * mb,
1364						},
1365						hugepages1Gi: {
1366							Allocatable:    gb,
1367							Free:           gb,
1368							Reserved:       0,
1369							SystemReserved: 0,
1370							TotalMemSize:   gb,
1371						},
1372					},
1373					Cells:               []int{2},
1374					NumberOfAssignments: 0,
1375				},
1376			},
1377			systemReserved: systemReservedMemory{
1378				0: map[v1.ResourceName]uint64{
1379					v1.ResourceMemory: 512 * mb,
1380				},
1381				1: map[v1.ResourceName]uint64{
1382					v1.ResourceMemory: 512 * mb,
1383				},
1384				2: map[v1.ResourceName]uint64{
1385					v1.ResourceMemory: 512 * mb,
1386				},
1387			},
1388			pod:           getPod("pod2", "container2", requirementsGuaranteed),
1389			expectedError: fmt.Errorf("[memorymanager] failed to find the default preferred hint"),
1390			topologyHint:  &topologymanager.TopologyHint{Preferred: true},
1391		},
1392		{
1393			description: "should fail when NUMA affinity provided under the topology manager hint did not satisfy container requirements and extended hint generation failed",
1394			machineState: state.NUMANodeMap{
1395				0: &state.NUMANodeState{
1396					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1397						v1.ResourceMemory: {
1398							Allocatable:    512 * mb,
1399							Free:           512 * mb,
1400							Reserved:       0,
1401							SystemReserved: 512 * mb,
1402							TotalMemSize:   gb,
1403						},
1404						hugepages1Gi: {
1405							Allocatable:    gb,
1406							Free:           gb,
1407							Reserved:       0,
1408							SystemReserved: 0,
1409							TotalMemSize:   gb,
1410						},
1411					},
1412					Cells:               []int{0},
1413					NumberOfAssignments: 0,
1414				},
1415				1: &state.NUMANodeState{
1416					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1417						v1.ResourceMemory: {
1418							Allocatable:    1536 * mb,
1419							Free:           512 * mb,
1420							Reserved:       gb,
1421							SystemReserved: 512 * mb,
1422							TotalMemSize:   2 * gb,
1423						},
1424						hugepages1Gi: {
1425							Allocatable:    gb,
1426							Free:           gb,
1427							Reserved:       0,
1428							SystemReserved: 0,
1429							TotalMemSize:   gb,
1430						},
1431					},
1432					Cells:               []int{1, 2},
1433					NumberOfAssignments: 1,
1434				},
1435				2: &state.NUMANodeState{
1436					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1437						v1.ResourceMemory: {
1438							Allocatable:    1536 * mb,
1439							Free:           512 * mb,
1440							Reserved:       gb,
1441							SystemReserved: 512 * mb,
1442							TotalMemSize:   2 * gb,
1443						},
1444						hugepages1Gi: {
1445							Allocatable:    gb,
1446							Free:           gb,
1447							Reserved:       0,
1448							SystemReserved: 0,
1449							TotalMemSize:   gb,
1450						},
1451					},
1452					Cells:               []int{1, 2},
1453					NumberOfAssignments: 1,
1454				},
1455			},
1456			systemReserved: systemReservedMemory{
1457				0: map[v1.ResourceName]uint64{
1458					v1.ResourceMemory: 512 * mb,
1459				},
1460				1: map[v1.ResourceName]uint64{
1461					v1.ResourceMemory: 512 * mb,
1462				},
1463				2: map[v1.ResourceName]uint64{
1464					v1.ResourceMemory: 512 * mb,
1465				},
1466			},
1467			pod:           getPod("pod1", "container1", requirementsGuaranteed),
1468			expectedError: fmt.Errorf("[memorymanager] failed to find NUMA nodes to extend the current topology hint"),
1469			topologyHint:  &topologymanager.TopologyHint{NUMANodeAffinity: newNUMAAffinity(0), Preferred: false},
1470		},
1471		{
1472			description: "should fail when the topology manager provided the preferred hint and extended hint has preferred false",
1473			assignments: state.ContainerMemoryAssignments{
1474				"pod1": map[string][]state.Block{
1475					"container1": {
1476						{
1477							NUMAAffinity: []int{0},
1478							Type:         v1.ResourceMemory,
1479							Size:         512 * mb,
1480						},
1481					},
1482				},
1483			},
1484			machineState: state.NUMANodeMap{
1485				0: &state.NUMANodeState{
1486					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1487						v1.ResourceMemory: {
1488							Allocatable:    gb,
1489							Free:           512 * mb,
1490							Reserved:       512 * mb,
1491							SystemReserved: 512 * mb,
1492							TotalMemSize:   1536 * mb,
1493						},
1494						hugepages1Gi: {
1495							Allocatable:    gb,
1496							Free:           gb,
1497							Reserved:       0,
1498							SystemReserved: 0,
1499							TotalMemSize:   gb,
1500						},
1501					},
1502					Cells:               []int{0},
1503					NumberOfAssignments: 1,
1504				},
1505				1: &state.NUMANodeState{
1506					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1507						v1.ResourceMemory: {
1508							Allocatable:    512 * mb,
1509							Free:           512 * mb,
1510							Reserved:       0,
1511							SystemReserved: 512 * mb,
1512							TotalMemSize:   1536 * mb,
1513						},
1514						hugepages1Gi: {
1515							Allocatable:    gb,
1516							Free:           gb,
1517							Reserved:       0,
1518							SystemReserved: 0,
1519							TotalMemSize:   gb,
1520						},
1521					},
1522					Cells:               []int{1},
1523					NumberOfAssignments: 0,
1524				},
1525				2: &state.NUMANodeState{
1526					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1527						v1.ResourceMemory: {
1528							Allocatable:    512 * mb,
1529							Free:           512 * mb,
1530							Reserved:       0,
1531							SystemReserved: 512 * mb,
1532							TotalMemSize:   1536 * mb,
1533						},
1534						hugepages1Gi: {
1535							Allocatable:    gb,
1536							Free:           gb,
1537							Reserved:       0,
1538							SystemReserved: 0,
1539							TotalMemSize:   gb,
1540						},
1541					},
1542					Cells:               []int{2},
1543					NumberOfAssignments: 0,
1544				},
1545			},
1546			systemReserved: systemReservedMemory{
1547				0: map[v1.ResourceName]uint64{
1548					v1.ResourceMemory: 512 * mb,
1549				},
1550				1: map[v1.ResourceName]uint64{
1551					v1.ResourceMemory: 512 * mb,
1552				},
1553				2: map[v1.ResourceName]uint64{
1554					v1.ResourceMemory: 512 * mb,
1555				},
1556			},
1557			pod:           getPod("pod2", "container2", requirementsGuaranteed),
1558			expectedError: fmt.Errorf("[memorymanager] failed to find the extended preferred hint"),
1559			topologyHint:  &topologymanager.TopologyHint{NUMANodeAffinity: newNUMAAffinity(1), Preferred: true},
1560		},
1561		{
1562			description: "should succeed to allocate memory from multiple NUMA nodes",
1563			assignments: state.ContainerMemoryAssignments{},
1564			expectedAssignments: state.ContainerMemoryAssignments{
1565				"pod1": map[string][]state.Block{
1566					"container1": {
1567						{
1568							NUMAAffinity: []int{0, 1},
1569							Type:         v1.ResourceMemory,
1570							Size:         gb,
1571						},
1572						{
1573							NUMAAffinity: []int{0, 1},
1574							Type:         hugepages1Gi,
1575							Size:         gb,
1576						},
1577					},
1578				},
1579			},
1580			machineState: state.NUMANodeMap{
1581				0: &state.NUMANodeState{
1582					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1583						v1.ResourceMemory: {
1584							Allocatable:    512 * mb,
1585							Free:           512 * mb,
1586							Reserved:       0,
1587							SystemReserved: 512 * mb,
1588							TotalMemSize:   gb,
1589						},
1590						hugepages1Gi: {
1591							Allocatable:    gb,
1592							Free:           gb,
1593							Reserved:       0,
1594							SystemReserved: 0,
1595							TotalMemSize:   gb,
1596						},
1597					},
1598					Cells:               []int{0},
1599					NumberOfAssignments: 0,
1600				},
1601				1: &state.NUMANodeState{
1602					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1603						v1.ResourceMemory: {
1604							Allocatable:    512 * mb,
1605							Free:           512 * mb,
1606							Reserved:       0,
1607							SystemReserved: 512 * mb,
1608							TotalMemSize:   gb,
1609						},
1610						hugepages1Gi: {
1611							Allocatable:    gb,
1612							Free:           gb,
1613							Reserved:       0,
1614							SystemReserved: 0,
1615							TotalMemSize:   gb,
1616						},
1617					},
1618					Cells:               []int{1},
1619					NumberOfAssignments: 0,
1620				},
1621				2: &state.NUMANodeState{
1622					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1623						v1.ResourceMemory: {
1624							Allocatable:    512 * mb,
1625							Free:           512 * mb,
1626							Reserved:       0,
1627							SystemReserved: 512 * mb,
1628							TotalMemSize:   gb,
1629						},
1630						hugepages1Gi: {
1631							Allocatable:    gb,
1632							Free:           gb,
1633							Reserved:       0,
1634							SystemReserved: 0,
1635							TotalMemSize:   gb,
1636						},
1637					},
1638					Cells:               []int{2},
1639					NumberOfAssignments: 0,
1640				},
1641				3: &state.NUMANodeState{
1642					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1643						v1.ResourceMemory: {
1644							Allocatable:    512 * mb,
1645							Free:           512 * mb,
1646							Reserved:       0,
1647							SystemReserved: 512 * mb,
1648							TotalMemSize:   gb,
1649						},
1650						hugepages1Gi: {
1651							Allocatable:    gb,
1652							Free:           gb,
1653							Reserved:       0,
1654							SystemReserved: 0,
1655							TotalMemSize:   gb,
1656						},
1657					},
1658					Cells:               []int{3},
1659					NumberOfAssignments: 0,
1660				},
1661			},
1662			expectedMachineState: state.NUMANodeMap{
1663				0: &state.NUMANodeState{
1664					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1665						v1.ResourceMemory: {
1666							Allocatable:    512 * mb,
1667							Free:           0,
1668							Reserved:       512 * mb,
1669							SystemReserved: 512 * mb,
1670							TotalMemSize:   gb,
1671						},
1672						hugepages1Gi: {
1673							Allocatable:    gb,
1674							Free:           0,
1675							Reserved:       gb,
1676							SystemReserved: 0,
1677							TotalMemSize:   gb,
1678						},
1679					},
1680					Cells:               []int{0, 1},
1681					NumberOfAssignments: 2,
1682				},
1683				1: &state.NUMANodeState{
1684					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1685						v1.ResourceMemory: {
1686							Allocatable:    512 * mb,
1687							Free:           0,
1688							Reserved:       512 * mb,
1689							SystemReserved: 512 * mb,
1690							TotalMemSize:   gb,
1691						},
1692						hugepages1Gi: {
1693							Allocatable:    gb,
1694							Free:           gb,
1695							Reserved:       0,
1696							SystemReserved: 0,
1697							TotalMemSize:   gb,
1698						},
1699					},
1700					Cells:               []int{0, 1},
1701					NumberOfAssignments: 2,
1702				},
1703				2: &state.NUMANodeState{
1704					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1705						v1.ResourceMemory: {
1706							Allocatable:    512 * mb,
1707							Free:           512 * mb,
1708							Reserved:       0,
1709							SystemReserved: 512 * mb,
1710							TotalMemSize:   gb,
1711						},
1712						hugepages1Gi: {
1713							Allocatable:    gb,
1714							Free:           gb,
1715							Reserved:       0,
1716							SystemReserved: 0,
1717							TotalMemSize:   gb,
1718						},
1719					},
1720					Cells:               []int{2},
1721					NumberOfAssignments: 0,
1722				},
1723				3: &state.NUMANodeState{
1724					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1725						v1.ResourceMemory: {
1726							Allocatable:    512 * mb,
1727							Free:           512 * mb,
1728							Reserved:       0,
1729							SystemReserved: 512 * mb,
1730							TotalMemSize:   gb,
1731						},
1732						hugepages1Gi: {
1733							Allocatable:    gb,
1734							Free:           gb,
1735							Reserved:       0,
1736							SystemReserved: 0,
1737							TotalMemSize:   gb,
1738						},
1739					},
1740					Cells:               []int{3},
1741					NumberOfAssignments: 0,
1742				},
1743			},
1744			systemReserved: systemReservedMemory{
1745				0: map[v1.ResourceName]uint64{
1746					v1.ResourceMemory: 512 * mb,
1747				},
1748				1: map[v1.ResourceName]uint64{
1749					v1.ResourceMemory: 512 * mb,
1750				},
1751				2: map[v1.ResourceName]uint64{
1752					v1.ResourceMemory: 512 * mb,
1753				},
1754				3: map[v1.ResourceName]uint64{
1755					v1.ResourceMemory: 512 * mb,
1756				},
1757			},
1758			pod:          getPod("pod1", "container1", requirementsGuaranteed),
1759			topologyHint: &topologymanager.TopologyHint{Preferred: true},
1760		},
1761	}
1762
1763	for _, testCase := range testCases {
1764		t.Run(testCase.description, func(t *testing.T) {
1765			t.Logf("TestStaticPolicyAllocate %s", testCase.description)
1766			p, s, err := initTests(t, &testCase, testCase.topologyHint)
1767			if err != nil {
1768				t.Fatalf("Unexpected error: %v", err)
1769			}
1770
1771			err = p.Allocate(s, testCase.pod, &testCase.pod.Spec.Containers[0])
1772			if !reflect.DeepEqual(err, testCase.expectedError) {
1773				t.Fatalf("The actual error %v is different from the expected one %v", err, testCase.expectedError)
1774			}
1775
1776			if err != nil {
1777				return
1778			}
1779
1780			assignments := s.GetMemoryAssignments()
1781			if !areContainerMemoryAssignmentsEqual(t, assignments, testCase.expectedAssignments) {
1782				t.Fatalf("Actual assignments %v are different from the expected %v", assignments, testCase.expectedAssignments)
1783			}
1784
1785			machineState := s.GetMachineState()
1786			if !areMachineStatesEqual(machineState, testCase.expectedMachineState) {
1787				t.Fatalf("The actual machine state %v is different from the expected %v", machineState, testCase.expectedMachineState)
1788			}
1789		})
1790	}
1791}
1792
1793func TestStaticPolicyAllocateWithInitContainers(t *testing.T) {
1794	testCases := []testStaticPolicy{
1795		{
1796			description: "should re-use init containers memory, init containers requests 1Gi and 2Gi, apps containers 3Gi and 4Gi",
1797			assignments: state.ContainerMemoryAssignments{},
1798			expectedAssignments: state.ContainerMemoryAssignments{
1799				"pod1": map[string][]state.Block{
1800					"initContainer1": {
1801						{
1802							NUMAAffinity: []int{0},
1803							Type:         v1.ResourceMemory,
1804							Size:         0,
1805						},
1806						{
1807							NUMAAffinity: []int{0},
1808							Type:         hugepages1Gi,
1809							Size:         0,
1810						},
1811					},
1812					"initContainer2": {
1813						{
1814							NUMAAffinity: []int{0},
1815							Type:         v1.ResourceMemory,
1816							Size:         0,
1817						},
1818						{
1819							NUMAAffinity: []int{0},
1820							Type:         hugepages1Gi,
1821							Size:         0,
1822						},
1823					},
1824					"container1": {
1825						{
1826							NUMAAffinity: []int{0},
1827							Type:         v1.ResourceMemory,
1828							Size:         3 * gb,
1829						},
1830						{
1831							NUMAAffinity: []int{0},
1832							Type:         hugepages1Gi,
1833							Size:         3 * gb,
1834						},
1835					},
1836					"container2": {
1837						{
1838							NUMAAffinity: []int{0},
1839							Type:         v1.ResourceMemory,
1840							Size:         4 * gb,
1841						},
1842						{
1843							NUMAAffinity: []int{0},
1844							Type:         hugepages1Gi,
1845							Size:         4 * gb,
1846						},
1847					},
1848				},
1849			},
1850			machineState: state.NUMANodeMap{
1851				0: &state.NUMANodeState{
1852					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1853						v1.ResourceMemory: {
1854							Allocatable:    7680 * mb,
1855							Free:           7680 * mb,
1856							Reserved:       0,
1857							SystemReserved: 512 * mb,
1858							TotalMemSize:   8 * gb,
1859						},
1860						hugepages1Gi: {
1861							Allocatable:    8 * gb,
1862							Free:           8 * gb,
1863							Reserved:       0,
1864							SystemReserved: 0,
1865							TotalMemSize:   8 * gb,
1866						},
1867					},
1868					Cells: []int{0},
1869				},
1870			},
1871			expectedMachineState: state.NUMANodeMap{
1872				0: &state.NUMANodeState{
1873					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
1874						v1.ResourceMemory: {
1875							Allocatable:    7680 * mb,
1876							Free:           512 * mb,
1877							Reserved:       7 * gb,
1878							SystemReserved: 512 * mb,
1879							TotalMemSize:   8 * gb,
1880						},
1881						hugepages1Gi: {
1882							Allocatable:    8 * gb,
1883							Free:           1 * gb,
1884							Reserved:       7 * gb,
1885							SystemReserved: 0,
1886							TotalMemSize:   8 * gb,
1887						},
1888					},
1889					Cells:               []int{0},
1890					NumberOfAssignments: 8,
1891				},
1892			},
1893			systemReserved: systemReservedMemory{
1894				0: map[v1.ResourceName]uint64{
1895					v1.ResourceMemory: 512 * mb,
1896				},
1897			},
1898			pod: getPodWithInitContainers(
1899				"pod1",
1900				[]v1.Container{
1901					{
1902						Name: "container1",
1903						Resources: v1.ResourceRequirements{
1904							Limits: v1.ResourceList{
1905								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1906								v1.ResourceMemory: resource.MustParse("3Gi"),
1907								hugepages1Gi:      resource.MustParse("3Gi"),
1908							},
1909							Requests: v1.ResourceList{
1910								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1911								v1.ResourceMemory: resource.MustParse("3Gi"),
1912								hugepages1Gi:      resource.MustParse("3Gi"),
1913							},
1914						},
1915					},
1916					{
1917						Name: "container2",
1918						Resources: v1.ResourceRequirements{
1919							Limits: v1.ResourceList{
1920								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1921								v1.ResourceMemory: resource.MustParse("4Gi"),
1922								hugepages1Gi:      resource.MustParse("4Gi"),
1923							},
1924							Requests: v1.ResourceList{
1925								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1926								v1.ResourceMemory: resource.MustParse("4Gi"),
1927								hugepages1Gi:      resource.MustParse("4Gi"),
1928							},
1929						},
1930					},
1931				},
1932				[]v1.Container{
1933					{
1934						Name: "initContainer1",
1935						Resources: v1.ResourceRequirements{
1936							Limits: v1.ResourceList{
1937								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1938								v1.ResourceMemory: resource.MustParse("1Gi"),
1939								hugepages1Gi:      resource.MustParse("1Gi"),
1940							},
1941							Requests: v1.ResourceList{
1942								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1943								v1.ResourceMemory: resource.MustParse("1Gi"),
1944								hugepages1Gi:      resource.MustParse("1Gi"),
1945							},
1946						},
1947					},
1948					{
1949						Name: "initContainer2",
1950						Resources: v1.ResourceRequirements{
1951							Limits: v1.ResourceList{
1952								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1953								v1.ResourceMemory: resource.MustParse("2Gi"),
1954								hugepages1Gi:      resource.MustParse("2Gi"),
1955							},
1956							Requests: v1.ResourceList{
1957								v1.ResourceCPU:    resource.MustParse("1000Mi"),
1958								v1.ResourceMemory: resource.MustParse("2Gi"),
1959								hugepages1Gi:      resource.MustParse("2Gi"),
1960							},
1961						},
1962					},
1963				},
1964			),
1965			topologyHint: &topologymanager.TopologyHint{},
1966		},
1967		{
1968			description: "should re-use init containers memory, init containers requests 4Gi and 3Gi, apps containers 2Gi and 1Gi",
1969			assignments: state.ContainerMemoryAssignments{},
1970			expectedAssignments: state.ContainerMemoryAssignments{
1971				"pod1": map[string][]state.Block{
1972					"initContainer1": {
1973						{
1974							NUMAAffinity: []int{0},
1975							Type:         v1.ResourceMemory,
1976							Size:         0,
1977						},
1978						{
1979							NUMAAffinity: []int{0},
1980							Type:         hugepages1Gi,
1981							Size:         0,
1982						},
1983					},
1984					"initContainer2": {
1985						{
1986							NUMAAffinity: []int{0},
1987							Type:         v1.ResourceMemory,
1988							Size:         gb,
1989						},
1990						{
1991							NUMAAffinity: []int{0},
1992							Type:         hugepages1Gi,
1993							Size:         gb,
1994						},
1995					},
1996					"container1": {
1997						{
1998							NUMAAffinity: []int{0},
1999							Type:         v1.ResourceMemory,
2000							Size:         2 * gb,
2001						},
2002						{
2003							NUMAAffinity: []int{0},
2004							Type:         hugepages1Gi,
2005							Size:         2 * gb,
2006						},
2007					},
2008					"container2": {
2009						{
2010							NUMAAffinity: []int{0},
2011							Type:         v1.ResourceMemory,
2012							Size:         gb,
2013						},
2014						{
2015							NUMAAffinity: []int{0},
2016							Type:         hugepages1Gi,
2017							Size:         gb,
2018						},
2019					},
2020				},
2021			},
2022			machineState: state.NUMANodeMap{
2023				0: &state.NUMANodeState{
2024					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2025						v1.ResourceMemory: {
2026							Allocatable:    7680 * mb,
2027							Free:           7680 * mb,
2028							Reserved:       0,
2029							SystemReserved: 512 * mb,
2030							TotalMemSize:   8 * gb,
2031						},
2032						hugepages1Gi: {
2033							Allocatable:    8 * gb,
2034							Free:           8 * gb,
2035							Reserved:       0,
2036							SystemReserved: 0,
2037							TotalMemSize:   8 * gb,
2038						},
2039					},
2040					Cells: []int{0},
2041				},
2042			},
2043			expectedMachineState: state.NUMANodeMap{
2044				0: &state.NUMANodeState{
2045					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2046						v1.ResourceMemory: {
2047							Allocatable:    7680 * mb,
2048							Free:           3584 * mb,
2049							Reserved:       4 * gb,
2050							SystemReserved: 512 * mb,
2051							TotalMemSize:   8 * gb,
2052						},
2053						hugepages1Gi: {
2054							Allocatable:    8 * gb,
2055							Free:           4 * gb,
2056							Reserved:       4 * gb,
2057							SystemReserved: 0,
2058							TotalMemSize:   8 * gb,
2059						},
2060					},
2061					Cells:               []int{0},
2062					NumberOfAssignments: 8,
2063				},
2064			},
2065			systemReserved: systemReservedMemory{
2066				0: map[v1.ResourceName]uint64{
2067					v1.ResourceMemory: 512 * mb,
2068				},
2069			},
2070			pod: getPodWithInitContainers(
2071				"pod1",
2072				[]v1.Container{
2073					{
2074						Name: "container1",
2075						Resources: v1.ResourceRequirements{
2076							Limits: v1.ResourceList{
2077								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2078								v1.ResourceMemory: resource.MustParse("2Gi"),
2079								hugepages1Gi:      resource.MustParse("2Gi"),
2080							},
2081							Requests: v1.ResourceList{
2082								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2083								v1.ResourceMemory: resource.MustParse("2Gi"),
2084								hugepages1Gi:      resource.MustParse("2Gi"),
2085							},
2086						},
2087					},
2088					{
2089						Name: "container2",
2090						Resources: v1.ResourceRequirements{
2091							Limits: v1.ResourceList{
2092								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2093								v1.ResourceMemory: resource.MustParse("1Gi"),
2094								hugepages1Gi:      resource.MustParse("1Gi"),
2095							},
2096							Requests: v1.ResourceList{
2097								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2098								v1.ResourceMemory: resource.MustParse("1Gi"),
2099								hugepages1Gi:      resource.MustParse("1Gi"),
2100							},
2101						},
2102					},
2103				},
2104				[]v1.Container{
2105					{
2106						Name: "initContainer1",
2107						Resources: v1.ResourceRequirements{
2108							Limits: v1.ResourceList{
2109								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2110								v1.ResourceMemory: resource.MustParse("4Gi"),
2111								hugepages1Gi:      resource.MustParse("4Gi"),
2112							},
2113							Requests: v1.ResourceList{
2114								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2115								v1.ResourceMemory: resource.MustParse("4Gi"),
2116								hugepages1Gi:      resource.MustParse("4Gi"),
2117							},
2118						},
2119					},
2120					{
2121						Name: "initContainer2",
2122						Resources: v1.ResourceRequirements{
2123							Limits: v1.ResourceList{
2124								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2125								v1.ResourceMemory: resource.MustParse("3Gi"),
2126								hugepages1Gi:      resource.MustParse("3Gi"),
2127							},
2128							Requests: v1.ResourceList{
2129								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2130								v1.ResourceMemory: resource.MustParse("3Gi"),
2131								hugepages1Gi:      resource.MustParse("3Gi"),
2132							},
2133						},
2134					},
2135				},
2136			),
2137			topologyHint: &topologymanager.TopologyHint{},
2138		},
2139		{
2140			description: "should re-use init containers memory, init containers requests 7Gi and 4Gi, apps containers 4Gi and 3Gi",
2141			assignments: state.ContainerMemoryAssignments{},
2142			expectedAssignments: state.ContainerMemoryAssignments{
2143				"pod1": map[string][]state.Block{
2144					"initContainer1": {
2145						{
2146							NUMAAffinity: []int{0},
2147							Type:         v1.ResourceMemory,
2148							Size:         0,
2149						},
2150						{
2151							NUMAAffinity: []int{0},
2152							Type:         hugepages1Gi,
2153							Size:         0,
2154						},
2155					},
2156					"initContainer2": {
2157						{
2158							NUMAAffinity: []int{0},
2159							Type:         v1.ResourceMemory,
2160							Size:         0,
2161						},
2162						{
2163							NUMAAffinity: []int{0},
2164							Type:         hugepages1Gi,
2165							Size:         0,
2166						},
2167					},
2168					"container1": {
2169						{
2170							NUMAAffinity: []int{0},
2171							Type:         v1.ResourceMemory,
2172							Size:         4 * gb,
2173						},
2174						{
2175							NUMAAffinity: []int{0},
2176							Type:         hugepages1Gi,
2177							Size:         4 * gb,
2178						},
2179					},
2180					"container2": {
2181						{
2182							NUMAAffinity: []int{0},
2183							Type:         v1.ResourceMemory,
2184							Size:         3 * gb,
2185						},
2186						{
2187							NUMAAffinity: []int{0},
2188							Type:         hugepages1Gi,
2189							Size:         3 * gb,
2190						},
2191					},
2192				},
2193			},
2194			machineState: state.NUMANodeMap{
2195				0: &state.NUMANodeState{
2196					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2197						v1.ResourceMemory: {
2198							Allocatable:    7680 * mb,
2199							Free:           7680 * mb,
2200							Reserved:       0,
2201							SystemReserved: 512 * mb,
2202							TotalMemSize:   8 * gb,
2203						},
2204						hugepages1Gi: {
2205							Allocatable:    8 * gb,
2206							Free:           8 * gb,
2207							Reserved:       0,
2208							SystemReserved: 0,
2209							TotalMemSize:   8 * gb,
2210						},
2211					},
2212					Cells: []int{0},
2213				},
2214			},
2215			expectedMachineState: state.NUMANodeMap{
2216				0: &state.NUMANodeState{
2217					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2218						v1.ResourceMemory: {
2219							Allocatable:    7680 * mb,
2220							Free:           512 * mb,
2221							Reserved:       7 * gb,
2222							SystemReserved: 512 * mb,
2223							TotalMemSize:   8 * gb,
2224						},
2225						hugepages1Gi: {
2226							Allocatable:    8 * gb,
2227							Free:           1 * gb,
2228							Reserved:       7 * gb,
2229							SystemReserved: 0,
2230							TotalMemSize:   8 * gb,
2231						},
2232					},
2233					Cells:               []int{0},
2234					NumberOfAssignments: 8,
2235				},
2236			},
2237			systemReserved: systemReservedMemory{
2238				0: map[v1.ResourceName]uint64{
2239					v1.ResourceMemory: 512 * mb,
2240				},
2241			},
2242			pod: getPodWithInitContainers(
2243				"pod1",
2244				[]v1.Container{
2245					{
2246						Name: "container1",
2247						Resources: v1.ResourceRequirements{
2248							Limits: v1.ResourceList{
2249								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2250								v1.ResourceMemory: resource.MustParse("4Gi"),
2251								hugepages1Gi:      resource.MustParse("4Gi"),
2252							},
2253							Requests: v1.ResourceList{
2254								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2255								v1.ResourceMemory: resource.MustParse("4Gi"),
2256								hugepages1Gi:      resource.MustParse("4Gi"),
2257							},
2258						},
2259					},
2260					{
2261						Name: "container2",
2262						Resources: v1.ResourceRequirements{
2263							Limits: v1.ResourceList{
2264								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2265								v1.ResourceMemory: resource.MustParse("3Gi"),
2266								hugepages1Gi:      resource.MustParse("3Gi"),
2267							},
2268							Requests: v1.ResourceList{
2269								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2270								v1.ResourceMemory: resource.MustParse("3Gi"),
2271								hugepages1Gi:      resource.MustParse("3Gi"),
2272							},
2273						},
2274					},
2275				},
2276				[]v1.Container{
2277					{
2278						Name: "initContainer1",
2279						Resources: v1.ResourceRequirements{
2280							Limits: v1.ResourceList{
2281								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2282								v1.ResourceMemory: resource.MustParse("7Gi"),
2283								hugepages1Gi:      resource.MustParse("7Gi"),
2284							},
2285							Requests: v1.ResourceList{
2286								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2287								v1.ResourceMemory: resource.MustParse("7Gi"),
2288								hugepages1Gi:      resource.MustParse("7Gi"),
2289							},
2290						},
2291					},
2292					{
2293						Name: "initContainer2",
2294						Resources: v1.ResourceRequirements{
2295							Limits: v1.ResourceList{
2296								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2297								v1.ResourceMemory: resource.MustParse("4Gi"),
2298								hugepages1Gi:      resource.MustParse("4Gi"),
2299							},
2300							Requests: v1.ResourceList{
2301								v1.ResourceCPU:    resource.MustParse("1000Mi"),
2302								v1.ResourceMemory: resource.MustParse("4Gi"),
2303								hugepages1Gi:      resource.MustParse("4Gi"),
2304							},
2305						},
2306					},
2307				},
2308			),
2309			topologyHint: &topologymanager.TopologyHint{},
2310		},
2311	}
2312
2313	for _, testCase := range testCases {
2314		t.Run(testCase.description, func(t *testing.T) {
2315			klog.InfoS("TestStaticPolicyAllocateWithInitContainers", "test name", testCase.description)
2316			p, s, err := initTests(t, &testCase, testCase.topologyHint)
2317			if err != nil {
2318				t.Fatalf("Unexpected error: %v", err)
2319			}
2320
2321			for i := range testCase.pod.Spec.InitContainers {
2322				err = p.Allocate(s, testCase.pod, &testCase.pod.Spec.InitContainers[i])
2323				if !reflect.DeepEqual(err, testCase.expectedError) {
2324					t.Fatalf("The actual error %v is different from the expected one %v", err, testCase.expectedError)
2325				}
2326			}
2327
2328			for i := range testCase.pod.Spec.Containers {
2329				err = p.Allocate(s, testCase.pod, &testCase.pod.Spec.Containers[i])
2330				if !reflect.DeepEqual(err, testCase.expectedError) {
2331					t.Fatalf("The actual error %v is different from the expected one %v", err, testCase.expectedError)
2332				}
2333			}
2334
2335			assignments := s.GetMemoryAssignments()
2336			if !areContainerMemoryAssignmentsEqual(t, assignments, testCase.expectedAssignments) {
2337				t.Fatalf("Actual assignments %v are different from the expected %v", assignments, testCase.expectedAssignments)
2338			}
2339
2340			machineState := s.GetMachineState()
2341			if !areMachineStatesEqual(machineState, testCase.expectedMachineState) {
2342				t.Fatalf("The actual machine state %v is different from the expected %v", machineState, testCase.expectedMachineState)
2343			}
2344		})
2345	}
2346}
2347
2348func TestStaticPolicyRemoveContainer(t *testing.T) {
2349	testCases := []testStaticPolicy{
2350		{
2351			description:         "should do nothing when the container does not exist under the state",
2352			expectedAssignments: state.ContainerMemoryAssignments{},
2353			machineState: state.NUMANodeMap{
2354				0: &state.NUMANodeState{
2355					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2356						v1.ResourceMemory: {
2357							Allocatable:    1536 * mb,
2358							Free:           1536 * mb,
2359							Reserved:       0,
2360							SystemReserved: 512 * mb,
2361							TotalMemSize:   2 * gb,
2362						},
2363						hugepages1Gi: {
2364							Allocatable:    gb,
2365							Free:           gb,
2366							Reserved:       0,
2367							SystemReserved: 0,
2368							TotalMemSize:   gb,
2369						},
2370					},
2371					Cells: []int{},
2372				},
2373			},
2374			expectedMachineState: state.NUMANodeMap{
2375				0: &state.NUMANodeState{
2376					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2377						v1.ResourceMemory: {
2378							Allocatable:    1536 * mb,
2379							Free:           1536 * mb,
2380							Reserved:       0,
2381							SystemReserved: 512 * mb,
2382							TotalMemSize:   2 * gb,
2383						},
2384						hugepages1Gi: {
2385							Allocatable:    gb,
2386							Free:           gb,
2387							Reserved:       0,
2388							SystemReserved: 0,
2389							TotalMemSize:   gb,
2390						},
2391					},
2392					Cells: []int{},
2393				},
2394			},
2395			systemReserved: systemReservedMemory{
2396				0: map[v1.ResourceName]uint64{
2397					v1.ResourceMemory: 512 * mb,
2398				},
2399			},
2400		},
2401		{
2402			description: "should delete the container assignment and update the machine state",
2403			assignments: state.ContainerMemoryAssignments{
2404				"pod1": map[string][]state.Block{
2405					"container1": {
2406						{
2407							NUMAAffinity: []int{0},
2408							Type:         v1.ResourceMemory,
2409							Size:         gb,
2410						},
2411						{
2412							NUMAAffinity: []int{0},
2413							Type:         hugepages1Gi,
2414							Size:         gb,
2415						},
2416					},
2417				},
2418			},
2419			expectedAssignments: state.ContainerMemoryAssignments{},
2420			machineState: state.NUMANodeMap{
2421				0: &state.NUMANodeState{
2422					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2423						v1.ResourceMemory: {
2424							Allocatable:    1536 * mb,
2425							Free:           512 * mb,
2426							Reserved:       1024 * mb,
2427							SystemReserved: 512 * mb,
2428							TotalMemSize:   2 * gb,
2429						},
2430						hugepages1Gi: {
2431							Allocatable:    gb,
2432							Free:           0,
2433							Reserved:       gb,
2434							SystemReserved: 0,
2435							TotalMemSize:   gb,
2436						},
2437					},
2438					NumberOfAssignments: 2,
2439					Cells:               []int{0},
2440				},
2441			},
2442			expectedMachineState: state.NUMANodeMap{
2443				0: &state.NUMANodeState{
2444					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2445						v1.ResourceMemory: {
2446							Allocatable:    1536 * mb,
2447							Free:           1536 * mb,
2448							Reserved:       0,
2449							SystemReserved: 512 * mb,
2450							TotalMemSize:   2 * gb,
2451						},
2452						hugepages1Gi: {
2453							Allocatable:    gb,
2454							Free:           gb,
2455							Reserved:       0,
2456							SystemReserved: 0,
2457							TotalMemSize:   gb,
2458						},
2459					},
2460					Cells:               []int{0},
2461					NumberOfAssignments: 0,
2462				},
2463			},
2464			systemReserved: systemReservedMemory{
2465				0: map[v1.ResourceName]uint64{
2466					v1.ResourceMemory: 512 * mb,
2467				},
2468			},
2469		},
2470		{
2471			description: "should delete the cross NUMA container assignment and update the machine state",
2472			assignments: state.ContainerMemoryAssignments{
2473				"pod1": map[string][]state.Block{
2474					"container1": {
2475						{
2476							NUMAAffinity: []int{0, 1},
2477							Type:         v1.ResourceMemory,
2478							Size:         gb,
2479						},
2480						{
2481							NUMAAffinity: []int{0, 1},
2482							Type:         hugepages1Gi,
2483							Size:         gb,
2484						},
2485					},
2486				},
2487			},
2488			expectedAssignments: state.ContainerMemoryAssignments{},
2489			machineState: state.NUMANodeMap{
2490				0: &state.NUMANodeState{
2491					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2492						v1.ResourceMemory: {
2493							Allocatable:    512 * mb,
2494							Free:           0,
2495							Reserved:       512 * mb,
2496							SystemReserved: 512 * mb,
2497							TotalMemSize:   gb,
2498						},
2499						hugepages1Gi: {
2500							Allocatable:    gb,
2501							Free:           0,
2502							Reserved:       gb,
2503							SystemReserved: 0,
2504							TotalMemSize:   gb,
2505						},
2506					},
2507					NumberOfAssignments: 2,
2508					Cells:               []int{0, 1},
2509				},
2510				1: &state.NUMANodeState{
2511					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2512						v1.ResourceMemory: {
2513							Allocatable:    512 * mb,
2514							Free:           0,
2515							Reserved:       512 * mb,
2516							SystemReserved: 512 * mb,
2517							TotalMemSize:   gb,
2518						},
2519						hugepages1Gi: {
2520							Allocatable:    gb,
2521							Free:           gb,
2522							Reserved:       0,
2523							SystemReserved: 0,
2524							TotalMemSize:   gb,
2525						},
2526					},
2527					NumberOfAssignments: 2,
2528					Cells:               []int{0, 1},
2529				},
2530			},
2531			expectedMachineState: state.NUMANodeMap{
2532				0: &state.NUMANodeState{
2533					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2534						v1.ResourceMemory: {
2535							Allocatable:    512 * mb,
2536							Free:           512 * mb,
2537							Reserved:       0,
2538							SystemReserved: 512 * mb,
2539							TotalMemSize:   gb,
2540						},
2541						hugepages1Gi: {
2542							Allocatable:    gb,
2543							Free:           gb,
2544							Reserved:       0,
2545							SystemReserved: 0,
2546							TotalMemSize:   gb,
2547						},
2548					},
2549					NumberOfAssignments: 0,
2550					Cells:               []int{0},
2551				},
2552				1: &state.NUMANodeState{
2553					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2554						v1.ResourceMemory: {
2555							Allocatable:    512 * mb,
2556							Free:           512 * mb,
2557							Reserved:       0,
2558							SystemReserved: 512 * mb,
2559							TotalMemSize:   gb,
2560						},
2561						hugepages1Gi: {
2562							Allocatable:    gb,
2563							Free:           gb,
2564							Reserved:       0,
2565							SystemReserved: 0,
2566							TotalMemSize:   gb,
2567						},
2568					},
2569					NumberOfAssignments: 0,
2570					Cells:               []int{1},
2571				},
2572			},
2573			systemReserved: systemReservedMemory{
2574				0: map[v1.ResourceName]uint64{
2575					v1.ResourceMemory: 512 * mb,
2576				},
2577				1: map[v1.ResourceName]uint64{
2578					v1.ResourceMemory: 512 * mb,
2579				},
2580			},
2581		},
2582	}
2583
2584	for _, testCase := range testCases {
2585		t.Run(testCase.description, func(t *testing.T) {
2586			p, s, err := initTests(t, &testCase, nil)
2587			if err != nil {
2588				t.Fatalf("Unexpected error: %v", err)
2589			}
2590
2591			p.RemoveContainer(s, "pod1", "container1")
2592			assignments := s.GetMemoryAssignments()
2593			if !areContainerMemoryAssignmentsEqual(t, assignments, testCase.expectedAssignments) {
2594				t.Fatalf("Actual assignments %v are different from the expected %v", assignments, testCase.expectedAssignments)
2595			}
2596
2597			machineState := s.GetMachineState()
2598			if !areMachineStatesEqual(machineState, testCase.expectedMachineState) {
2599				t.Fatalf("The actual machine state %v is different from the expected %v", machineState, testCase.expectedMachineState)
2600			}
2601		})
2602	}
2603}
2604
2605func TestStaticPolicyGetTopologyHints(t *testing.T) {
2606	testCases := []testStaticPolicy{
2607		{
2608			description: "should not provide topology hints for non-guaranteed pods",
2609			pod:         getPod("pod1", "container1", requirementsBurstable),
2610			systemReserved: systemReservedMemory{
2611				0: map[v1.ResourceName]uint64{
2612					v1.ResourceMemory: 512 * mb,
2613				},
2614			},
2615			expectedTopologyHints: nil,
2616		},
2617		{
2618			description: "should provide topology hints based on the existent memory assignment",
2619			assignments: state.ContainerMemoryAssignments{
2620				"pod1": map[string][]state.Block{
2621					"container1": {
2622						{
2623							NUMAAffinity: []int{0},
2624							Type:         v1.ResourceMemory,
2625							Size:         gb,
2626						},
2627						{
2628							NUMAAffinity: []int{0},
2629							Type:         hugepages1Gi,
2630							Size:         gb,
2631						},
2632					},
2633				},
2634			},
2635			pod: getPod("pod1", "container1", requirementsGuaranteed),
2636			systemReserved: systemReservedMemory{
2637				0: map[v1.ResourceName]uint64{
2638					v1.ResourceMemory: 512 * mb,
2639				},
2640			},
2641			expectedTopologyHints: map[string][]topologymanager.TopologyHint{
2642				string(v1.ResourceMemory): {
2643					{
2644						NUMANodeAffinity: newNUMAAffinity(0),
2645						Preferred:        true,
2646					},
2647				},
2648				string(hugepages1Gi): {
2649					{
2650						NUMANodeAffinity: newNUMAAffinity(0),
2651						Preferred:        true,
2652					},
2653				},
2654			},
2655		},
2656		{
2657			description: "should calculate new topology hints, when the container does not exist under assignments",
2658			assignments: state.ContainerMemoryAssignments{
2659				"pod1": map[string][]state.Block{
2660					"container1": {
2661						{
2662							NUMAAffinity: []int{0, 1},
2663							Type:         v1.ResourceMemory,
2664							Size:         2 * gb,
2665						},
2666						{
2667							NUMAAffinity: []int{0, 1},
2668							Type:         hugepages1Gi,
2669							Size:         2 * gb,
2670						},
2671					},
2672				},
2673			},
2674			machineState: state.NUMANodeMap{
2675				0: &state.NUMANodeState{
2676					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2677						v1.ResourceMemory: {
2678							Allocatable:    1536 * mb,
2679							Free:           0,
2680							Reserved:       1536 * mb,
2681							SystemReserved: 512 * mb,
2682							TotalMemSize:   2 * gb,
2683						},
2684						hugepages1Gi: {
2685							Allocatable:    gb,
2686							Free:           0,
2687							Reserved:       gb,
2688							SystemReserved: 0,
2689							TotalMemSize:   gb,
2690						},
2691					},
2692					Cells:               []int{0, 1},
2693					NumberOfAssignments: 2,
2694				},
2695				1: &state.NUMANodeState{
2696					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2697						v1.ResourceMemory: {
2698							Allocatable:    1536 * mb,
2699							Free:           gb,
2700							Reserved:       512 * mb,
2701							SystemReserved: 512 * mb,
2702							TotalMemSize:   2 * gb,
2703						},
2704						hugepages1Gi: {
2705							Allocatable:    gb,
2706							Free:           0,
2707							Reserved:       gb,
2708							SystemReserved: 0,
2709							TotalMemSize:   gb,
2710						},
2711					},
2712					Cells:               []int{0, 1},
2713					NumberOfAssignments: 2,
2714				},
2715				2: &state.NUMANodeState{
2716					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2717						v1.ResourceMemory: {
2718							Allocatable:    1536 * mb,
2719							Free:           1536 * mb,
2720							Reserved:       0,
2721							SystemReserved: 512 * mb,
2722							TotalMemSize:   2 * gb,
2723						},
2724						hugepages1Gi: {
2725							Allocatable:    gb,
2726							Free:           gb,
2727							Reserved:       0,
2728							SystemReserved: 0,
2729							TotalMemSize:   gb,
2730						},
2731					},
2732					Cells:               []int{2},
2733					NumberOfAssignments: 0,
2734				},
2735				3: &state.NUMANodeState{
2736					MemoryMap: map[v1.ResourceName]*state.MemoryTable{
2737						v1.ResourceMemory: {
2738							Allocatable:    1536 * mb,
2739							Free:           1536 * mb,
2740							Reserved:       0,
2741							SystemReserved: 512 * mb,
2742							TotalMemSize:   2 * gb,
2743						},
2744						hugepages1Gi: {
2745							Allocatable:    gb,
2746							Free:           gb,
2747							Reserved:       0,
2748							SystemReserved: 0,
2749							TotalMemSize:   gb,
2750						},
2751					},
2752					Cells:               []int{3},
2753					NumberOfAssignments: 0,
2754				},
2755			},
2756			pod: getPod("pod2", "container2", requirementsGuaranteed),
2757			systemReserved: systemReservedMemory{
2758				0: map[v1.ResourceName]uint64{
2759					v1.ResourceMemory: 512 * mb,
2760				},
2761				1: map[v1.ResourceName]uint64{
2762					v1.ResourceMemory: 512 * mb,
2763				},
2764				2: map[v1.ResourceName]uint64{
2765					v1.ResourceMemory: 512 * mb,
2766				},
2767				3: map[v1.ResourceName]uint64{
2768					v1.ResourceMemory: 512 * mb,
2769				},
2770			},
2771			expectedTopologyHints: map[string][]topologymanager.TopologyHint{
2772				string(v1.ResourceMemory): {
2773					{
2774						NUMANodeAffinity: newNUMAAffinity(2),
2775						Preferred:        true,
2776					},
2777					{
2778						NUMANodeAffinity: newNUMAAffinity(3),
2779						Preferred:        true,
2780					},
2781					{
2782						NUMANodeAffinity: newNUMAAffinity(2, 3),
2783						Preferred:        false,
2784					},
2785				},
2786				string(hugepages1Gi): {
2787					{
2788						NUMANodeAffinity: newNUMAAffinity(2),
2789						Preferred:        true,
2790					},
2791					{
2792						NUMANodeAffinity: newNUMAAffinity(3),
2793						Preferred:        true,
2794					},
2795					{
2796						NUMANodeAffinity: newNUMAAffinity(2, 3),
2797						Preferred:        false,
2798					},
2799				},
2800			},
2801		},
2802		{
2803			description: "should fail when number of existing memory assignment resources are different from resources requested by container",
2804			assignments: state.ContainerMemoryAssignments{
2805				"pod1": map[string][]state.Block{
2806					"container1": {
2807						{
2808							NUMAAffinity: []int{0},
2809							Type:         v1.ResourceMemory,
2810							Size:         gb,
2811						},
2812					},
2813				},
2814			},
2815			pod: getPod("pod1", "container1", requirementsGuaranteed),
2816			systemReserved: systemReservedMemory{
2817				0: map[v1.ResourceName]uint64{
2818					v1.ResourceMemory: 512 * mb,
2819				},
2820			},
2821			expectedTopologyHints: nil,
2822		},
2823		{
2824			description: "should fail when existing memory assignment resources are different from resources requested by container",
2825			assignments: state.ContainerMemoryAssignments{
2826				"pod1": map[string][]state.Block{
2827					"container1": {
2828						{
2829							NUMAAffinity: []int{0},
2830							Type:         v1.ResourceMemory,
2831							Size:         gb,
2832						},
2833						{
2834							NUMAAffinity: []int{0},
2835							Type:         hugepages2M,
2836							Size:         gb,
2837						},
2838					},
2839				},
2840			},
2841			pod: getPod("pod1", "container1", requirementsGuaranteed),
2842			systemReserved: systemReservedMemory{
2843				0: map[v1.ResourceName]uint64{
2844					v1.ResourceMemory: 512 * mb,
2845				},
2846			},
2847			expectedTopologyHints: nil,
2848		},
2849		{
2850			description: "should fail when existing memory assignment size is different from one requested by the container",
2851			assignments: state.ContainerMemoryAssignments{
2852				"pod1": map[string][]state.Block{
2853					"container1": {
2854						{
2855							NUMAAffinity: []int{0},
2856							Type:         v1.ResourceMemory,
2857							Size:         512 * mb,
2858						},
2859						{
2860							NUMAAffinity: []int{0},
2861							Type:         hugepages1Gi,
2862							Size:         gb,
2863						},
2864					},
2865				},
2866			},
2867			pod: getPod("pod1", "container1", requirementsGuaranteed),
2868			systemReserved: systemReservedMemory{
2869				0: map[v1.ResourceName]uint64{
2870					v1.ResourceMemory: 512 * mb,
2871				},
2872			},
2873			expectedTopologyHints: nil,
2874		},
2875	}
2876
2877	for _, testCase := range testCases {
2878		t.Run(testCase.description, func(t *testing.T) {
2879			p, s, err := initTests(t, &testCase, nil)
2880			if err != nil {
2881				t.Fatalf("Unexpected error: %v", err)
2882			}
2883
2884			topologyHints := p.GetTopologyHints(s, testCase.pod, &testCase.pod.Spec.Containers[0])
2885			if !reflect.DeepEqual(topologyHints, testCase.expectedTopologyHints) {
2886				t.Fatalf("The actual topology hints: '%+v' are different from the expected one: '%+v'", topologyHints, testCase.expectedTopologyHints)
2887			}
2888		})
2889	}
2890}
2891