kubernetes/pkg/kubelet/cm/memorymanager/memory_manager_test.go
Pawel Rapacz 18c8a821e0 memory manager: implement GetPodTopologyHints method
It will return memory and hugepages hints for the whole pod.

Signed-off-by: Pawel Rapacz <p.rapacz@partner.samsung.com>
2021-02-09 01:09:59 +02:00

247 lines
7.3 KiB
Go

package memorymanager
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/assert"
info "github.com/google/cadvisor/info/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
"k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
)
const (
hugepages2M = "hugepages-2Mi"
hugepages1G = "hugepages-1Gi"
)
type nodeResources map[v1.ResourceName]resource.Quantity
type mockPolicy struct {
err error
}
func (p *mockPolicy) Name() string {
return string(policyTypeMock)
}
func (p *mockPolicy) Start(s state.State) error {
return p.err
}
func (p *mockPolicy) Allocate(s state.State, pod *v1.Pod, container *v1.Container) error {
return p.err
}
func (p *mockPolicy) RemoveContainer(s state.State, podUID string, containerName string) error {
return p.err
}
func (p *mockPolicy) GetTopologyHints(s state.State, pod *v1.Pod, container *v1.Container) map[string][]topologymanager.TopologyHint {
return nil
}
func (p *mockPolicy) GetPodTopologyHints(s state.State, pod *v1.Pod) map[string][]topologymanager.TopologyHint {
return nil
}
type mockRuntimeService struct {
err error
}
func (rt mockRuntimeService) UpdateContainerResources(id string, resources *runtimeapi.LinuxContainerResources) error {
return rt.err
}
type mockPodStatusProvider struct {
podStatus v1.PodStatus
found bool
}
func (psp mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
return psp.podStatus, psp.found
}
func getPod(podUID string, containerName string, requirements *v1.ResourceRequirements) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: types.UID(podUID),
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: containerName,
Resources: *requirements,
},
},
},
}
}
func TestValidatePreReservedMemory(t *testing.T) {
const msgNotEqual = "the total amount of memory of type \"%s\" is not equal to the value determined by Node Allocatable feature"
testCases := []struct {
description string
nodeAllocatableReservation v1.ResourceList
preReservedMemory map[int]map[v1.ResourceName]resource.Quantity
expectedError string
}{
{
"Node Allocatable not set, pre-reserved not set",
v1.ResourceList{},
map[int]map[v1.ResourceName]resource.Quantity{},
"",
},
{
"Node Allocatable set to zero, pre-reserved set to zero",
v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI)},
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI)},
},
"",
},
{
"Node Allocatable not set (equal zero), pre-reserved set",
v1.ResourceList{},
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI)},
},
fmt.Sprintf(msgNotEqual, v1.ResourceMemory),
},
{
"Node Allocatable set, pre-reserved not set",
v1.ResourceList{hugepages2M: *resource.NewQuantity(5, resource.DecimalSI)},
map[int]map[v1.ResourceName]resource.Quantity{},
fmt.Sprintf(msgNotEqual, hugepages2M),
},
{
"Pre-reserved not equal to Node Allocatable",
v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI)},
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI)},
},
fmt.Sprintf(msgNotEqual, v1.ResourceMemory),
},
{
"Pre-reserved total equal to Node Allocatable",
v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(77, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
},
"",
},
{
"Pre-reserved total hugapages-2M not equal to Node Allocatable",
v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(14, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
},
fmt.Sprintf(msgNotEqual, hugepages2M),
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
err := validateReservedMemory(tc.nodeAllocatableReservation, tc.preReservedMemory)
if strings.TrimSpace(tc.expectedError) != "" {
assert.Error(t, err)
assert.Equal(t, err.Error(), tc.expectedError)
}
})
}
}
// validateReservedMemory
func TestConvertPreReserved(t *testing.T) {
machineInfo := info.MachineInfo{
Topology: []info.Node{
info.Node{Id: 0},
info.Node{Id: 1},
},
}
testCases := []struct {
description string
reserved map[int]map[v1.ResourceName]resource.Quantity
reservedExpected reservedMemory
expectedError string
}{
{
"Empty",
map[int]map[v1.ResourceName]resource.Quantity{},
reservedMemory{
0: map[v1.ResourceName]uint64{},
1: map[v1.ResourceName]uint64{},
},
"",
},
{
"Single NUMA node is pre-reserved",
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
},
reservedMemory{
0: map[v1.ResourceName]uint64{
v1.ResourceMemory: 12,
hugepages2M: 70,
hugepages1G: 13,
},
1: map[v1.ResourceName]uint64{},
},
"",
},
{
"Both NUMA nodes are pre-reserved",
map[int]map[v1.ResourceName]resource.Quantity{
0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
},
reservedMemory{
0: map[v1.ResourceName]uint64{
v1.ResourceMemory: 12,
hugepages2M: 70,
hugepages1G: 13,
},
1: map[v1.ResourceName]uint64{
v1.ResourceMemory: 5,
hugepages2M: 7,
},
},
"",
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
reserved, _ := convertReserved(&machineInfo, tc.reserved)
if !reflect.DeepEqual(reserved, tc.reservedExpected) {
t.Errorf("got %v, expected %v", reserved, tc.reservedExpected)
}
})
}
}