mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
45062754fd
The Kuberenetes pod resource API now exposing the memory and hugepages information for guaranteed pods. We can use this information to update NodeResourceTopology resource with memory and hugepages data. Signed-off-by: Artyom Lukianov <alukiano@redhat.com>
1092 lines
32 KiB
Go
1092 lines
32 KiB
Go
/*
|
|
Copyright 2021 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package resourcemonitor
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
k8sclient "k8s.io/client-go/kubernetes"
|
|
v1 "k8s.io/kubelet/pkg/apis/podresources/v1"
|
|
|
|
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
|
|
"sigs.k8s.io/node-feature-discovery/pkg/podres"
|
|
)
|
|
|
|
func TestPodScanner(t *testing.T) {
|
|
|
|
var resScan ResourcesScanner
|
|
var err error
|
|
|
|
Convey("When I scan for pod resources using fake client and no namespace", t, func() {
|
|
mockPodResClient := new(podres.MockPodResourcesListerClient)
|
|
mockAPIHelper := new(apihelper.MockAPIHelpers)
|
|
mockClient := &k8sclient.Clientset{}
|
|
resScan, err = NewPodResourcesScanner("*", mockPodResClient, mockAPIHelper)
|
|
|
|
Convey("Creating a Resources Scanner using a mock client", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("When I get error", func() {
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(nil, fmt.Errorf("fake error"))
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is present", func() {
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("Return PodResources should be nil", func() {
|
|
So(res, ShouldBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get empty response", func() {
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(&v1.ListPodResourcesResponse{}, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should be zero", func() {
|
|
So(len(res), ShouldEqual, 0)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
Topology: &v1.TopologyInfo{
|
|
Nodes: []*v1.NUMANode{
|
|
{ID: 0},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
CpuIds: []int64{0, 1},
|
|
Memory: []*v1.ContainerMemory{
|
|
{
|
|
MemoryType: "hugepages-2Mi",
|
|
Size_: 512,
|
|
Topology: &v1.TopologyInfo{
|
|
Nodes: []*v1.NUMANode{
|
|
{ID: 1},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
MemoryType: "memory",
|
|
Size_: 512,
|
|
Topology: &v1.TopologyInfo{
|
|
Nodes: []*v1.NUMANode{
|
|
{ID: 0},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(512, resource.DecimalSI),
|
|
"hugepages-2Mi": *resource.NewQuantity(512, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(512, resource.DecimalSI),
|
|
"hugepages-2Mi": *resource.NewQuantity(512, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "cpu",
|
|
Data: []string{"0", "1"},
|
|
},
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
NumaNodeIds: []int{0},
|
|
},
|
|
{
|
|
Name: "hugepages-2Mi",
|
|
Data: []string{"512"},
|
|
NumaNodeIds: []int{1},
|
|
},
|
|
{
|
|
Name: "memory",
|
|
Data: []string{"512"},
|
|
NumaNodeIds: []int{0},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, podresource := range res {
|
|
for _, container := range podresource.Containers {
|
|
sort.Slice(res, func(i, j int) bool {
|
|
return container.Resources[i].Name < container.Resources[j].Name
|
|
})
|
|
}
|
|
}
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response without topology", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
CpuIds: []int64{0, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Image: "nginx",
|
|
ImagePullPolicy: "Always",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "cpu",
|
|
Data: []string{"0", "1"},
|
|
},
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response without devices", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
CpuIds: []int64{0, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "cpu",
|
|
Data: []string{"0", "1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response without cpus", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Image: "nginx",
|
|
ImagePullPolicy: "Always",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response for (non-guaranteed) pods with devices without cpus", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
})
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
|
|
Convey("When I successfully get valid response for (non-guaranteed) pods with devices with cpus", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
CpuIds: []int64{0, 1},
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
corev1.ResourceCPU: resource.MustParse("1500m"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
corev1.ResourceCPU: resource.MustParse("1500m"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
})
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
|
|
})
|
|
|
|
Convey("When I scan for pod resources using fake client and given namespace", t, func() {
|
|
mockPodResClient := new(podres.MockPodResourcesListerClient)
|
|
mockAPIHelper := new(apihelper.MockAPIHelpers)
|
|
mockClient := &k8sclient.Clientset{}
|
|
resScan, err = NewPodResourcesScanner("pod-res-test", mockPodResClient, mockAPIHelper)
|
|
|
|
Convey("Creating a Resources Scanner using a mock client", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("When I get error", func() {
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(nil, fmt.Errorf("fake error"))
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is present", func() {
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("Return PodResources should be nil", func() {
|
|
So(res, ShouldBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get empty response", func() {
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(&v1.ListPodResourcesResponse{}, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should be zero", func() {
|
|
So(len(res), ShouldEqual, 0)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
Topology: &v1.TopologyInfo{
|
|
Nodes: []*v1.NUMANode{
|
|
{ID: 0},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
CpuIds: []int64{0, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should be zero", func() {
|
|
So(len(res), ShouldEqual, 0)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response when pod is in the monitoring namespace", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
CpuIds: []int64{0, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Image: "nginx",
|
|
ImagePullPolicy: "Always",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "pod-res-test", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "cpu",
|
|
Data: []string{"0", "1"},
|
|
},
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid empty response for a pod not in the monitoring namespace without devices", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
CpuIds: []int64{0, 1},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
Convey("Return PodResources should be zero", func() {
|
|
So(len(res), ShouldEqual, 0)
|
|
})
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get an empty valid response for a pod without cpus when pod is not in the monitoring namespace", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "default",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Image: "nginx",
|
|
ImagePullPolicy: "Always",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI),
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "default", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldEqual, 0)
|
|
})
|
|
})
|
|
|
|
Convey("When I successfully get valid response for (non-guaranteed) pods with devices without cpus", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "pod-res-test", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
})
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
|
|
Convey("When I successfully get valid response for (non-guaranteed) pods with devices with cpus", func() {
|
|
resp := &v1.ListPodResourcesResponse{
|
|
PodResources: []*v1.PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []*v1.ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
CpuIds: []int64{0, 1},
|
|
Devices: []*v1.ContainerDevices{
|
|
{
|
|
ResourceName: "fake.io/resource",
|
|
DeviceIds: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockPodResClient.On("List", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*v1.ListPodResourcesRequest")).Return(resp, nil)
|
|
pod := &corev1.Pod{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "Pod",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: corev1.ResourceRequirements{
|
|
Requests: corev1.ResourceList{
|
|
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
corev1.ResourceCPU: resource.MustParse("1500m"),
|
|
},
|
|
Limits: corev1.ResourceList{
|
|
corev1.ResourceName("fake.io/resource"): *resource.NewQuantity(1, resource.DecimalSI),
|
|
corev1.ResourceMemory: *resource.NewQuantity(100, resource.DecimalSI),
|
|
corev1.ResourceCPU: resource.MustParse("1500m"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
|
mockAPIHelper.On("GetPod", mock.AnythingOfType("*kubernetes.Clientset"), "pod-res-test", "test-pod-0").Return(pod, nil)
|
|
res, err := resScan.Scan()
|
|
|
|
Convey("Error is nil", func() {
|
|
So(err, ShouldBeNil)
|
|
})
|
|
Convey("Return PodResources should have values", func() {
|
|
So(len(res), ShouldBeGreaterThan, 0)
|
|
})
|
|
|
|
expected := []PodResources{
|
|
{
|
|
Name: "test-pod-0",
|
|
Namespace: "pod-res-test",
|
|
Containers: []ContainerResources{
|
|
{
|
|
Name: "test-cnt-0",
|
|
Resources: []ResourceInfo{
|
|
{
|
|
Name: "fake.io/resource",
|
|
Data: []string{"devA"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
So(reflect.DeepEqual(res, expected), ShouldBeTrue)
|
|
})
|
|
|
|
})
|
|
}
|