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 kubeclient
import (
"context"
"fmt"
"math"
"reflect"
"strconv"
"testing"
"time"
"github.com/agiledragon/gomonkey/v2"
"github.com/smartystreets/goconvey/convey"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"Ascend-device-plugin/pkg/common"
)
func newTestClientK8s() (*ClientK8s, error) {
return &ClientK8s{
Clientset: &kubernetes.Clientset{},
NodeName: "node",
DeviceInfoName: common.DeviceInfoCMNamePrefix + "node",
IsApiErr: false,
}, nil
}
func TestUpdatePodList01(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestUpdatePodList init kubernetes failed")
}
client.Clientset = fake.NewSimpleClientset()
factory := informers.NewSharedInformerFactory(client.Clientset, 0)
client.PodInformer = factory.Core().V1().Pods().Informer()
indexer := client.PodInformer.GetIndexer()
testPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "testUid",
Name: "testPod",
Namespace: "testNamespace",
ResourceVersion: "2",
},
}
podCache = make(map[types.UID]*podInfo)
convey.Convey("when pod is not exist in indexer, should be deleted from cache", t, func() {
client.UpdatePodList(testPod, EventTypeAdd)
convey.So(podCache, convey.ShouldResemble, map[types.UID]*podInfo{})
})
convey.Convey("when pod is exist in indexer, should add to cache", t, func() {
err := indexer.Add(testPod)
convey.So(err, convey.ShouldBeNil)
client.UpdatePodList(testPod, EventTypeAdd)
expectPodCache := map[types.UID]*podInfo{
testPod.UID: {
Pod: testPod,
updateTime: podCache[testPod.UID].updateTime,
},
}
convey.So(podCache, convey.ShouldResemble, expectPodCache)
})
}
func TestUpdatePodPredicateTime01(t *testing.T) {
convey.Convey("Test updatePodPredicateTime", t, func() {
podCache = make(map[types.UID]*podInfo)
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestUpdatePodList init kubernetes failed")
}
convey.Convey("when pod does not exist in cache", func() {
newPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: types.UID("non-existent-uid"),
},
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations, convey.ShouldBeNil)
})
convey.Convey("when cached pod annotations is nil", func() {
podUID := types.UID("test-uid")
podCache[podUID] = &podInfo{
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
},
},
updateTime: time.Now(),
}
newPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
},
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations, convey.ShouldBeNil)
})
})
}
func TestUpdatePodPredicateTime02(t *testing.T) {
convey.Convey("Test updatePodPredicateTime", t, func() {
podCache = make(map[types.UID]*podInfo)
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestUpdatePodList init kubernetes failed")
}
convey.Convey("when cached predicate time is not MaxUint64", func() {
podUID := types.UID("test-uid")
podCache[podUID] = &podInfo{
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
Annotations: map[string]string{
common.PodPredicateTime: "12345",
},
},
},
updateTime: time.Now(),
}
newPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
},
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations, convey.ShouldBeNil)
})
})
}
func TestUpdatePodPredicateTime03(t *testing.T) {
convey.Convey("Test updatePodPredicateTime", t, func() {
podCache = make(map[types.UID]*podInfo)
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestUpdatePodList init kubernetes failed")
}
client.Clientset = fake.NewSimpleClientset()
mockPatchPod := gomonkey.ApplyMethod(reflect.TypeOf(new(ClientK8s)), "PatchPod",
func(_ *ClientK8s, _ *v1.Pod, _ []byte) (*v1.Pod, error) {
return nil, fmt.Errorf("test function errors")
})
defer mockPatchPod.Reset()
convey.Convey("when new pod annotations is nil", func() {
podUID := types.UID("test-uid")
podCache[podUID] = &podInfo{
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
Annotations: map[string]string{
common.PodPredicateTime: strconv.FormatUint(math.MaxUint64, common.BaseDec),
},
},
},
updateTime: time.Now(),
}
newPod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
},
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations, convey.ShouldNotBeNil)
convey.So(newPod.Annotations[common.PodPredicateTime], convey.ShouldEqual, strconv.FormatUint(math.MaxUint64, common.BaseDec))
})
})
}
func TestUpdatePodPredicateTime04(t *testing.T) {
convey.Convey("Test updatePodPredicateTime", t, func() {
podCache = make(map[types.UID]*podInfo)
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestUpdatePodList init kubernetes failed")
}
client.Clientset = fake.NewSimpleClientset()
maxUint64Str := strconv.FormatUint(math.MaxUint64, common.BaseDec)
podUID := types.UID("test-uid")
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
UID: podUID,
Annotations: map[string]string{
common.PodPredicateTime: maxUint64Str,
},
},
}
podCache[podUID] = &podInfo{
Pod: pod,
updateTime: time.Now(),
}
mockPatchPod := gomonkey.ApplyMethod(reflect.TypeOf(new(ClientK8s)), "PatchPod",
func(_ *ClientK8s, _ *v1.Pod, _ []byte) (*v1.Pod, error) {
return nil, fmt.Errorf("test function errors")
})
defer mockPatchPod.Reset()
convey.Convey("when new pod predicate time differs from cached", func() {
newPod := pod.DeepCopy()
newPod.Annotations = map[string]string{
common.PodPredicateTime: "12345",
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations[common.PodPredicateTime], convey.ShouldEqual,
strconv.FormatUint(math.MaxUint64, common.BaseDec))
})
convey.Convey("when new pod predicate time equals cached", func() {
newPod := pod.DeepCopy()
newPod.Annotations = map[string]string{
common.PodPredicateTime: maxUint64Str,
}
client.updatePodPredicateTime(newPod)
convey.So(newPod.Annotations[common.PodPredicateTime], convey.ShouldEqual, maxUint64Str)
})
})
}
func TestRefreshPodList(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestRefreshPodList init kubernetes failed")
}
testPod := v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "testUid",
},
}
convey.Convey("test get pod list when podList is empty", t, func() {
client.IsApiErr = true
mockGetAllPodList := gomonkey.ApplyMethodReturn(&ClientK8s{}, "GetAllPodList",
nil, fmt.Errorf("podList is empty"))
defer mockGetAllPodList.Reset()
client.refreshPodList()
convey.So(client.IsApiErr, convey.ShouldBeTrue)
convey.So(len(podCache), convey.ShouldEqual, 1)
})
convey.Convey("test get pod list by field selector with cache", t, func() {
client.IsApiErr = true
mockGetAllPodList := gomonkey.ApplyMethodReturn(&ClientK8s{}, "GetAllPodList",
&v1.PodList{
Items: []v1.Pod{
testPod,
},
}, nil)
defer mockGetAllPodList.Reset()
client.refreshPodList()
expectPodCache := map[types.UID]*podInfo{
testPod.UID: {
Pod: &testPod,
updateTime: podCache[testPod.UID].updateTime,
},
}
convey.So(client.IsApiErr, convey.ShouldBeFalse)
convey.So(podCache, convey.ShouldResemble, expectPodCache)
})
}
func TestGetAllPodListCache(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestGetAllPodListCache init kubernetes failed")
}
expectPodCache := []v1.Pod{{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "default",
}}}
convey.Convey("test get pod list by field selector with cache", t, func() {
client.IsApiErr = true
mockRefreshPodList := gomonkey.ApplyPrivateMethod(&ClientK8s{}, "refreshPodList", func(_ *ClientK8s) { return })
defer mockRefreshPodList.Reset()
podCache = map[types.UID]*podInfo{
"testPod": {
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "default",
}}}}
testPodList := client.GetAllPodListCache()
convey.So(testPodList, convey.ShouldResemble, expectPodCache)
})
}
func TestGetActivePodListCache01(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestGetActivePodListCache01 init kubernetes failed")
}
expectPodCache := []v1.Pod{{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "default",
}}}
client.IsApiErr = true
mockRefreshPodList := gomonkey.ApplyPrivateMethod(&ClientK8s{}, "refreshPodList", func(_ *ClientK8s) { return })
defer mockRefreshPodList.Reset()
convey.Convey("test get active pod list when pod name err", t, func() {
podCache = map[types.UID]*podInfo{
"testPod": {
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "errorName",
Namespace: "default",
}}}}
testPodList := client.GetActivePodListCache()
convey.So(testPodList, convey.ShouldResemble, make([]v1.Pod, 0, common.GeneralMapSize))
})
convey.Convey("test get active pod list with cache", t, func() {
podCache = map[types.UID]*podInfo{
"testPod": {
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "default",
}}}}
testPodList := client.GetActivePodListCache()
convey.So(testPodList, convey.ShouldResemble, expectPodCache)
})
}
func TestGetActivePodListCache02(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestGetActivePodListCache02 init kubernetes failed")
}
convey.Convey("test get active pod list when pod namespace err", t, func() {
podCache = map[types.UID]*podInfo{
"testPod": {
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "errorNamespace",
}}}}
testPodList := client.GetActivePodListCache()
convey.So(testPodList, convey.ShouldResemble, make([]v1.Pod, 0, common.GeneralMapSize))
})
convey.Convey("test get active pod list when pod status is failed or succeeded", t, func() {
podCache = map[types.UID]*podInfo{
"testPod": {
Pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
Namespace: "default",
},
Status: v1.PodStatus{Phase: v1.PodFailed},
}}}
testPodList := client.GetActivePodListCache()
convey.So(testPodList, convey.ShouldResemble, make([]v1.Pod, 0, common.GeneralMapSize))
})
}
func TestGetNodeIpCache(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestGetNodeIpCache init kubernetes failed")
}
convey.Convey("test get server id failed", t, func() {
patch := gomonkey.ApplyMethodReturn(&ClientK8s{}, "GetNode", &v1.Node{
Status: v1.NodeStatus{Addresses: make([]v1.NodeAddress, common.MaxPodLimit+1)}}, nil)
defer patch.Reset()
id, err := client.GetNodeIpCache()
convey.So(id, convey.ShouldEqual, "")
convey.So(err.Error(), convey.ShouldEqual, "the number of node status in exceeds the upper limit")
})
patch := gomonkey.ApplyMethodReturn(&ClientK8s{}, "GetNode", &v1.Node{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}},
}, nil)
defer patch.Reset()
convey.Convey("test server id", t, func() {
nodeServerIp = "test server id"
id, err := client.GetNodeIpCache()
convey.So(id, convey.ShouldEqual, "test server id")
convey.So(err, convey.ShouldBeNil)
nodeServerIp = ""
})
convey.Convey("test no server id", t, func() {
id, err := client.GetNodeIpCache()
convey.So(id, convey.ShouldEqual, "")
convey.So(err, convey.ShouldBeNil)
})
}
func TestCheckPodInCache01(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestCheckPodInCache01 init kubernetes failed")
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "uid",
Namespace: "default",
Name: "pod",
}}
podUpdateTime := time.Now().Add(-time.Hour).Add(-time.Minute)
expectNewPodCache := map[types.UID]*podInfo{}
convey.Convey("test check pod in cache", t, func() {
mockGetPod := gomonkey.ApplyMethodReturn((&kubernetes.Clientset{}).CoreV1().Pods(v1.NamespaceAll), "Get", pod, nil)
defer mockGetPod.Reset()
podCache = map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{Spec: v1.PodSpec{}},
updateTime: podUpdateTime,
}}
client.checkPodInCache(context.TODO())
convey.So(podCache, convey.ShouldResemble, expectNewPodCache)
})
mockGetPod := gomonkey.ApplyMethodReturn((&kubernetes.Clientset{}).CoreV1().Pods(v1.NamespaceAll),
"Get", pod, fmt.Errorf(common.ApiServerPort))
defer mockGetPod.Reset()
convey.Convey("test check pod in cache when get pod error", t, func() {
podCache = map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{Spec: v1.PodSpec{}},
updateTime: podUpdateTime,
}}
client.checkPodInCache(context.TODO())
convey.ShouldEqual(podCache, expectNewPodCache)
})
convey.Convey("test check pod in cache when get pod error is not found", t, func() {
mockIsNotFound := gomonkey.ApplyFuncReturn(errors.IsNotFound, true)
defer mockIsNotFound.Reset()
podCache = map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{Spec: v1.PodSpec{}},
updateTime: podUpdateTime,
}}
client.checkPodInCache(context.TODO())
convey.ShouldEqual(podCache, expectNewPodCache)
})
}
func TestCheckPodInCache02(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestCheckPodInCache02 init kubernetes failed")
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "uid",
Namespace: "default",
Name: "pod",
},
Spec: v1.PodSpec{NodeName: "node"}}
mockGetPod := gomonkey.ApplyMethodReturn((&kubernetes.Clientset{}).CoreV1().Pods(v1.NamespaceAll), "Get", pod, nil)
defer mockGetPod.Reset()
convey.Convey("test check pod in cache when update time lower than pod cache timeout", t, func() {
podUpdateTime := time.Now().Add(-time.Minute)
expectNewPodCache := map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{},
updateTime: podUpdateTime,
}}
podCache = map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{},
updateTime: podUpdateTime,
}}
client.checkPodInCache(context.TODO())
convey.So(podCache, convey.ShouldResemble, expectNewPodCache)
})
convey.Convey("test check pod in cache when need refresh", t, func() {
podUpdateTime := time.Now().Add(-time.Hour).Add(-time.Minute)
podCache = map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{},
updateTime: podUpdateTime,
}}
client.checkPodInCache(context.TODO())
expectNewPodCache := map[types.UID]*podInfo{
pod.UID: {
Pod: &v1.Pod{},
updateTime: podCache[pod.UID].updateTime,
}}
convey.So(podCache, convey.ShouldResemble, expectNewPodCache)
})
}
func TestGetDeviceInfoCMCache(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestGetDeviceInfoCMCache init kubernetes failed")
}
convey.Convey("test get device info configMap with cache success", t, func() {
var mockCache *common.NodeDeviceInfoCache
cache := client.GetDeviceInfoCMCache()
convey.So(cache, convey.ShouldResemble, mockCache)
})
}
func TestSetNodeDeviceInfoCache(t *testing.T) {
client, err := newTestClientK8s()
if err != nil {
t.Fatal("TestSetNodeDeviceInfoCache init kubernetes failed")
}
convey.Convey("test set device info cache success", t, func() {
mockNodeDeviceInfoCache := nodeDeviceInfoCache
client.SetNodeDeviceInfoCache(&common.NodeDeviceInfoCache{})
convey.So(nodeDeviceInfoCache, convey.ShouldResemble, &common.NodeDeviceInfoCache{})
nodeDeviceInfoCache = mockNodeDeviceInfoCache
})
}