kubelet podresources: add unit tests for DyanmicResource and Get method

Signed-off-by: Moshe Levi <moshele@nvidia.com>
This commit is contained in:
Moshe Levi 2023-03-09 02:03:00 +02:00
parent 2a568bcfc8
commit 67a71c0bd7

View File

@ -18,6 +18,7 @@ package podresources
import ( import (
"context" "context"
"fmt"
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
@ -66,12 +67,41 @@ func TestListPodResourcesV1(t *testing.T) {
}, },
} }
containers := []v1.Container{
{
Name: containerName,
},
}
pods := []*v1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: podNamespace,
UID: podUID,
},
Spec: v1.PodSpec{
Containers: containers,
},
},
}
pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}}
draDevs := []*podresourcesapi.DynamicResource{
{
ClassName: "resource-class",
ClaimName: "claim-name",
ClaimNamespace: "default",
ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}},
},
}
for _, tc := range []struct { for _, tc := range []struct {
desc string desc string
pods []*v1.Pod pods []*v1.Pod
devices []*podresourcesapi.ContainerDevices devices []*podresourcesapi.ContainerDevices
cpus []int64 cpus []int64
memory []*podresourcesapi.ContainerMemory memory []*podresourcesapi.ContainerMemory
dynamicResources []*podresourcesapi.DynamicResource
expectedResponse *podresourcesapi.ListPodResourcesResponse expectedResponse *podresourcesapi.ListPodResourcesResponse
}{ }{
{ {
@ -80,29 +110,16 @@ func TestListPodResourcesV1(t *testing.T) {
devices: []*podresourcesapi.ContainerDevices{}, devices: []*podresourcesapi.ContainerDevices{},
cpus: []int64{}, cpus: []int64{},
memory: []*podresourcesapi.ContainerMemory{}, memory: []*podresourcesapi.ContainerMemory{},
dynamicResources: []*podresourcesapi.DynamicResource{},
expectedResponse: &podresourcesapi.ListPodResourcesResponse{}, expectedResponse: &podresourcesapi.ListPodResourcesResponse{},
}, },
{ {
desc: "pod without devices", desc: "pod without devices",
pods: []*v1.Pod{ pods: pods,
{ devices: []*podresourcesapi.ContainerDevices{},
ObjectMeta: metav1.ObjectMeta{ cpus: []int64{},
Name: podName, memory: []*podresourcesapi.ContainerMemory{},
Namespace: podNamespace, dynamicResources: []*podresourcesapi.DynamicResource{},
UID: podUID,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: containerName,
},
},
},
},
},
devices: []*podresourcesapi.ContainerDevices{},
cpus: []int64{},
memory: []*podresourcesapi.ContainerMemory{},
expectedResponse: &podresourcesapi.ListPodResourcesResponse{ expectedResponse: &podresourcesapi.ListPodResourcesResponse{
PodResources: []*podresourcesapi.PodResources{ PodResources: []*podresourcesapi.PodResources{
{ {
@ -110,8 +127,9 @@ func TestListPodResourcesV1(t *testing.T) {
Namespace: podNamespace, Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{ Containers: []*podresourcesapi.ContainerResources{
{ {
Name: containerName, Name: containerName,
Devices: []*podresourcesapi.ContainerDevices{}, Devices: []*podresourcesapi.ContainerDevices{},
DynamicResources: []*podresourcesapi.DynamicResource{},
}, },
}, },
}, },
@ -119,26 +137,12 @@ func TestListPodResourcesV1(t *testing.T) {
}, },
}, },
{ {
desc: "pod with devices", desc: "pod with devices",
pods: []*v1.Pod{ pods: pods,
{ devices: devs,
ObjectMeta: metav1.ObjectMeta{ cpus: cpus,
Name: podName, memory: memory,
Namespace: podNamespace, dynamicResources: []*podresourcesapi.DynamicResource{},
UID: podUID,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: containerName,
},
},
},
},
},
devices: devs,
cpus: cpus,
memory: memory,
expectedResponse: &podresourcesapi.ListPodResourcesResponse{ expectedResponse: &podresourcesapi.ListPodResourcesResponse{
PodResources: []*podresourcesapi.PodResources{ PodResources: []*podresourcesapi.PodResources{
{ {
@ -146,10 +150,61 @@ func TestListPodResourcesV1(t *testing.T) {
Namespace: podNamespace, Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{ Containers: []*podresourcesapi.ContainerResources{
{ {
Name: containerName, Name: containerName,
Devices: devs, Devices: devs,
CpuIds: cpus, CpuIds: cpus,
Memory: memory, Memory: memory,
DynamicResources: []*podresourcesapi.DynamicResource{},
},
},
},
},
},
},
{
desc: "pod with dynamic resources",
pods: pods,
devices: []*podresourcesapi.ContainerDevices{},
cpus: cpus,
memory: memory,
dynamicResources: draDevs,
expectedResponse: &podresourcesapi.ListPodResourcesResponse{
PodResources: []*podresourcesapi.PodResources{
{
Name: podName,
Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{
{
Name: containerName,
Devices: []*podresourcesapi.ContainerDevices{},
CpuIds: cpus,
Memory: memory,
DynamicResources: draDevs,
},
},
},
},
},
},
{
desc: "pod with dynamic resources and devices",
pods: pods,
devices: devs,
cpus: cpus,
memory: memory,
dynamicResources: draDevs,
expectedResponse: &podresourcesapi.ListPodResourcesResponse{
PodResources: []*podresourcesapi.PodResources{
{
Name: podName,
Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{
{
Name: containerName,
Devices: devs,
CpuIds: cpus,
Memory: memory,
DynamicResources: draDevs,
}, },
}, },
}, },
@ -158,25 +213,29 @@ func TestListPodResourcesV1(t *testing.T) {
}, },
} { } {
t.Run(tc.desc, func(t *testing.T) { t.Run(tc.desc, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true)()
mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl) mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl)
mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl) mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl)
mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl) mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl)
mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl) mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl)
mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl)
mockPodsProvider.EXPECT().GetPods().Return(tc.pods).AnyTimes().AnyTimes() mockPodsProvider.EXPECT().GetPods().Return(tc.pods).AnyTimes().AnyTimes()
mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes() mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes()
mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes() mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes()
mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes() mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes()
mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pods[0], &containers[0]).Return(tc.dynamicResources).AnyTimes()
mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes() mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes()
mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes() mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes()
mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes() mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes()
mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes() mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes()
providers := PodResourcesProviders{ providers := PodResourcesProviders{
Pods: mockPodsProvider, Pods: mockPodsProvider,
Devices: mockDevicesProvider, Devices: mockDevicesProvider,
Cpus: mockCPUsProvider, Cpus: mockCPUsProvider,
Memory: mockMemoryProvider, Memory: mockMemoryProvider,
DynamicResources: mockDynamicResourcesProvider,
} }
server := NewV1PodResourcesServer(providers) server := NewV1PodResourcesServer(providers)
resp, err := server.List(context.TODO(), &podresourcesapi.ListPodResourcesRequest{}) resp, err := server.List(context.TODO(), &podresourcesapi.ListPodResourcesRequest{})
@ -485,6 +544,185 @@ func TestAllocatableResources(t *testing.T) {
} }
} }
func TestGetPodResourcesV1(t *testing.T) {
podName := "pod-name"
podNamespace := "pod-namespace"
podUID := types.UID("pod-uid")
containerName := "container-name"
numaID := int64(1)
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
devs := []*podresourcesapi.ContainerDevices{
{
ResourceName: "resource",
DeviceIds: []string{"dev0", "dev1"},
Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
},
}
cpus := []int64{12, 23, 30}
memory := []*podresourcesapi.ContainerMemory{
{
MemoryType: "memory",
Size_: 1073741824,
Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
},
{
MemoryType: "hugepages-1Gi",
Size_: 1073741824,
Topology: &podresourcesapi.TopologyInfo{Nodes: []*podresourcesapi.NUMANode{{ID: numaID}}},
},
}
containers := []v1.Container{
{
Name: containerName,
},
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Namespace: podNamespace,
UID: podUID,
},
Spec: v1.PodSpec{
Containers: containers,
},
}
pluginCDIDevices := []*podresourcesapi.CDIDevice{{Name: "dra-dev0"}, {Name: "dra-dev1"}}
draDevs := []*podresourcesapi.DynamicResource{
{
ClassName: "resource-class",
ClaimName: "claim-name",
ClaimNamespace: "default",
ClaimResources: []*podresourcesapi.ClaimResource{{CDIDevices: pluginCDIDevices}},
},
}
for _, tc := range []struct {
desc string
err error
exist bool
pod *v1.Pod
devices []*podresourcesapi.ContainerDevices
cpus []int64
memory []*podresourcesapi.ContainerMemory
dynamicResources []*podresourcesapi.DynamicResource
expectedResponse *podresourcesapi.GetPodResourcesResponse
}{
{
desc: "pod not exist",
err: fmt.Errorf("pod %s in namespace %s not found", podName, podNamespace),
exist: false,
pod: nil,
devices: []*podresourcesapi.ContainerDevices{},
cpus: []int64{},
memory: []*podresourcesapi.ContainerMemory{},
dynamicResources: []*podresourcesapi.DynamicResource{},
expectedResponse: &podresourcesapi.GetPodResourcesResponse{},
},
{
desc: "pod without devices",
err: nil,
exist: true,
pod: pod,
devices: []*podresourcesapi.ContainerDevices{},
cpus: []int64{},
memory: []*podresourcesapi.ContainerMemory{},
dynamicResources: []*podresourcesapi.DynamicResource{},
expectedResponse: &podresourcesapi.GetPodResourcesResponse{
PodResources: &podresourcesapi.PodResources{
Name: podName,
Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{
{
Name: containerName,
Devices: []*podresourcesapi.ContainerDevices{},
DynamicResources: []*podresourcesapi.DynamicResource{},
},
},
},
},
},
{
desc: "pod with devices",
err: nil,
exist: true,
pod: pod,
devices: devs,
cpus: cpus,
memory: memory,
dynamicResources: draDevs,
expectedResponse: &podresourcesapi.GetPodResourcesResponse{
PodResources: &podresourcesapi.PodResources{
Name: podName,
Namespace: podNamespace,
Containers: []*podresourcesapi.ContainerResources{
{
Name: containerName,
Devices: devs,
CpuIds: cpus,
Memory: memory,
DynamicResources: draDevs,
},
},
},
},
},
} {
t.Run(tc.desc, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesGet, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.KubeletPodResourcesDynamicResources, true)()
mockDevicesProvider := podresourcetest.NewMockDevicesProvider(mockCtrl)
mockPodsProvider := podresourcetest.NewMockPodsProvider(mockCtrl)
mockCPUsProvider := podresourcetest.NewMockCPUsProvider(mockCtrl)
mockMemoryProvider := podresourcetest.NewMockMemoryProvider(mockCtrl)
mockDynamicResourcesProvider := podresourcetest.NewMockDynamicResourcesProvider(mockCtrl)
mockPodsProvider.EXPECT().GetPodByName(podNamespace, podName).Return(tc.pod, tc.exist).AnyTimes()
mockDevicesProvider.EXPECT().GetDevices(string(podUID), containerName).Return(tc.devices).AnyTimes()
mockCPUsProvider.EXPECT().GetCPUs(string(podUID), containerName).Return(tc.cpus).AnyTimes()
mockMemoryProvider.EXPECT().GetMemory(string(podUID), containerName).Return(tc.memory).AnyTimes()
mockDynamicResourcesProvider.EXPECT().GetDynamicResources(pod, &containers[0]).Return(tc.dynamicResources).AnyTimes()
mockDevicesProvider.EXPECT().UpdateAllocatedDevices().Return().AnyTimes()
mockCPUsProvider.EXPECT().GetAllocatableCPUs().Return([]int64{}).AnyTimes()
mockDevicesProvider.EXPECT().GetAllocatableDevices().Return([]*podresourcesapi.ContainerDevices{}).AnyTimes()
mockMemoryProvider.EXPECT().GetAllocatableMemory().Return([]*podresourcesapi.ContainerMemory{}).AnyTimes()
providers := PodResourcesProviders{
Pods: mockPodsProvider,
Devices: mockDevicesProvider,
Cpus: mockCPUsProvider,
Memory: mockMemoryProvider,
DynamicResources: mockDynamicResourcesProvider,
}
server := NewV1PodResourcesServer(providers)
podReq := &podresourcesapi.GetPodResourcesRequest{PodName: podName, PodNamespace: podNamespace}
resp, err := server.Get(context.TODO(), podReq)
if err != nil {
if err.Error() != tc.err.Error() {
t.Errorf("want exit = %v, got %v", tc.err, err)
}
} else {
if err != err {
t.Errorf("want exit = %v, got %v", tc.err, err)
} else {
if !equalGetResponse(tc.expectedResponse, resp) {
t.Errorf("want resp = %s, got %s", tc.expectedResponse.String(), resp.String())
}
}
}
})
}
}
func equalListResponse(respA, respB *podresourcesapi.ListPodResourcesResponse) bool { func equalListResponse(respA, respB *podresourcesapi.ListPodResourcesResponse) bool {
if len(respA.PodResources) != len(respB.PodResources) { if len(respA.PodResources) != len(respB.PodResources) {
return false return false
@ -515,11 +753,54 @@ func equalListResponse(respA, respB *podresourcesapi.ListPodResourcesResponse) b
if !equalContainerDevices(cntA.Devices, cntB.Devices) { if !equalContainerDevices(cntA.Devices, cntB.Devices) {
return false return false
} }
if !euqalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) {
return false
}
} }
} }
return true return true
} }
func euqalDynamicResources(draResA, draResB []*podresourcesapi.DynamicResource) bool {
if len(draResA) != len(draResB) {
return false
}
for idx := 0; idx < len(draResA); idx++ {
cntDraResA := draResA[idx]
cntDraResB := draResB[idx]
if cntDraResA.ClassName != cntDraResB.ClassName {
return false
}
if cntDraResA.ClaimName != cntDraResB.ClaimName {
return false
}
if cntDraResA.ClaimNamespace != cntDraResB.ClaimNamespace {
return false
}
if len(cntDraResA.ClaimResources) != len(cntDraResB.ClaimResources) {
return false
}
for i := 0; i < len(cntDraResA.ClaimResources); i++ {
claimResA := cntDraResA.ClaimResources[i]
claimResB := cntDraResB.ClaimResources[i]
if len(claimResA.CDIDevices) != len(claimResB.CDIDevices) {
return false
}
for y := 0; y < len(claimResA.CDIDevices); y++ {
cdiDeviceA := claimResA.CDIDevices[y]
cdiDeviceB := claimResB.CDIDevices[y]
if cdiDeviceA.Name != cdiDeviceB.Name {
return false
}
}
}
}
return true
}
func equalContainerDevices(devA, devB []*podresourcesapi.ContainerDevices) bool { func equalContainerDevices(devA, devB []*podresourcesapi.ContainerDevices) bool {
if len(devA) != len(devB) { if len(devA) != len(devB) {
return false return false
@ -581,3 +862,38 @@ func equalAllocatableResourcesResponse(respA, respB *podresourcesapi.Allocatable
} }
return equalContainerDevices(respA.Devices, respB.Devices) return equalContainerDevices(respA.Devices, respB.Devices)
} }
func equalGetResponse(ResA, ResB *podresourcesapi.GetPodResourcesResponse) bool {
podResA := ResA.PodResources
podResB := ResB.PodResources
if podResA.Name != podResB.Name {
return false
}
if podResA.Namespace != podResB.Namespace {
return false
}
if len(podResA.Containers) != len(podResB.Containers) {
return false
}
for jdx := 0; jdx < len(podResA.Containers); jdx++ {
cntA := podResA.Containers[jdx]
cntB := podResB.Containers[jdx]
if cntA.Name != cntB.Name {
return false
}
if !equalInt64s(cntA.CpuIds, cntB.CpuIds) {
return false
}
if !equalContainerDevices(cntA.Devices, cntB.Devices) {
return false
}
if !euqalDynamicResources(cntA.DynamicResources, cntB.DynamicResources) {
return false
}
}
return true
}