
It will return memory and hugepages hints for the whole pod. Signed-off-by: Pawel Rapacz <p.rapacz@partner.samsung.com>
247 lines
7.3 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|