package kube
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
"time"
"github.com/agiledragon/gomonkey/v2"
"github.com/smartystreets/goconvey/convey"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"ascend-common/api"
"ascend-common/common-utils/hwlog"
)
const (
testCMName = "test-cm-name"
testPodName = "test-pod-name"
testNS = "default"
testKey1 = "key1"
testKey2 = "key2"
testKey3 = "key3"
testValue1 = "value1"
testValue2 = "value2"
testValue3 = "value3"
testName = "test-name"
testJobType = "test-job-type"
testJob1 = "test-job1"
testNode1 = "test-node1"
testJob2 = "test-job2"
testNode2 = "test-node2"
)
var (
cm *v1.ConfigMap
pod *v1.Pod
)
func TestPatchCMData(t *testing.T) {
createCM(t)
defer func() {
if err := DeleteConfigMap(testCMName, testNS); err != nil {
t.Error(err)
}
}()
convey.Convey("test func 'PatchCMData' success", t, func() {
patchData := map[string]string{testKey1: testValue1, testKey2: testValue2}
newCM, err := PatchCMData(testCMName, testNS, patchData)
expData := map[string]string{testKey1: testValue1, testKey2: testValue2}
convey.So(err, convey.ShouldBeNil)
convey.So(newCM.Data, convey.ShouldResemble, expData)
patchData = map[string]string{testKey2: testValue1, testKey3: testValue3}
newCM, err = PatchCMData(testCMName, testNS, patchData)
expData = map[string]string{testKey1: testValue1, testKey2: testValue1, testKey3: testValue3}
convey.So(err, convey.ShouldBeNil)
convey.So(newCM.Data, convey.ShouldResemble, expData)
})
convey.Convey("test func 'PatchCMData' failed, marshal data failed", t, func() {
p1 := gomonkey.ApplyFuncReturn(json.Marshal, nil, testErr)
defer p1.Reset()
patchData := map[string]string{testKey1: testValue1}
_, err := PatchCMData(testCMName, testNS, patchData)
expErr := fmt.Errorf("marshal cm data failed")
convey.So(err, convey.ShouldResemble, expErr)
})
}
func TestCheckNodeExist(t *testing.T) {
convey.Convey("Test checkNodeExist function", t, func() {
convey.Convey("When JSON unmarshal fails, should return false and log error", func() {
patch := gomonkey.ApplyFunc(hwlog.RunLog.Errorf, func(format string, args ...interface{}) {
convey.So(format, convey.ShouldContainSubstring, "unmarshal fault info failed")
})
defer patch.Reset()
result := checkNodeExist("invalidJsonData", "node1")
convey.So(result, convey.ShouldBeFalse)
})
convey.Convey("When node exists in fault info, should return true", func() {
faultInfos := map[int]api.SuperPodFaultInfos{0: {FaultNodes: sets.NewString("node1", "node2")}}
jsonData, err := json.Marshal(faultInfos)
if err != nil {
fmt.Printf("marshal fault info failed, error: %v", err)
}
result := checkNodeExist(string(jsonData), "node1")
convey.So(result, convey.ShouldBeTrue)
})
convey.Convey("When node does not exist in fault info, should return false and log info", func() {
patch := gomonkey.ApplyFunc(hwlog.RunLog.Infof, func(format string, args ...interface{}) {
convey.So(format, convey.ShouldContainSubstring, "node %s not in configmap fault-job-info")
})
defer patch.Reset()
faultInfos := map[int]api.SuperPodFaultInfos{0: {FaultNodes: sets.NewString("node1", "node2")}}
jsonData, err := json.Marshal(faultInfos)
if err != nil {
fmt.Printf("marshal fault info failed, error: %v", err)
}
result := checkNodeExist(string(jsonData), "node3")
convey.So(result, convey.ShouldBeFalse)
})
})
}
func createCM(t *testing.T) {
cm = &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: testCMName,
Namespace: testNS,
},
Data: nil,
}
_, err := CreateConfigMap(cm)
if err != nil {
t.Errorf("create test cm failed, error: %v", err)
}
}
func TestCreateCM(t *testing.T) {
cm = &v1.ConfigMap{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: testCMName,
Namespace: testNS,
},
Data: nil,
}
defer func() {
if err := DeleteConfigMap(testCMName, testNS); err != nil {
t.Error(err)
}
}()
convey.Convey("test func 'CreateConfigMap' success", t, func() {
resCM, err := CreateConfigMap(cm)
convey.So(err, convey.ShouldBeNil)
convey.So(resCM, convey.ShouldResemble, cm)
})
convey.Convey("test func 'CreateConfigMap' failed, input cm is nil", t, func() {
_, err := CreateConfigMap(nil)
expErr := fmt.Errorf("param cm is nil")
convey.So(err, convey.ShouldResemble, expErr)
})
}
func TestUpdateCM(t *testing.T) {
createCM(t)
defer func() {
if err := DeleteConfigMap(testCMName, testNS); err != nil {
t.Error(err)
}
}()
convey.Convey("test func 'UpdateConfigMap' success", t, func() {
resCM, err := UpdateConfigMap(cm)
convey.So(err, convey.ShouldBeNil)
convey.So(resCM, convey.ShouldResemble, cm)
})
convey.Convey("test func 'UpdateConfigMap' failed, input cm is nil", t, func() {
_, err := UpdateConfigMap(nil)
expErr := fmt.Errorf("param cm is nil")
convey.So(err, convey.ShouldResemble, expErr)
})
}
func TestGetCM(t *testing.T) {
createCM(t)
defer func() {
if err := DeleteConfigMap(testCMName, testNS); err != nil {
t.Error(err)
}
}()
convey.Convey("test func 'UpdateConfigMap' success", t, func() {
resCM, err := GetConfigMap(testCMName, testNS)
convey.So(err, convey.ShouldBeNil)
convey.So(resCM, convey.ShouldResemble, cm)
})
}
func TestDeleteCM(t *testing.T) {
createCM(t)
convey.Convey("test func 'DeleteConfigMap' success", t, func() {
err := DeleteConfigMap(testCMName, testNS)
convey.So(err, convey.ShouldBeNil)
})
}
func TestCreateOrUpdateCM(t *testing.T) {
convey.Convey("test func 'CreateOrUpdateConfigMap' success. cm does not exist, create success", t, func() {
DeleteConfigMap(testCMName, testNS)
err := CreateOrUpdateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("test func 'CreateOrUpdateConfigMap' failed. cm does not exist, create error", t, func() {
DeleteConfigMap(testCMName, testNS)
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().ConfigMaps(v1.NamespaceAll),
"Create", &v1.ConfigMap{}, testErr)
defer p1.Reset()
err := CreateOrUpdateConfigMap(testCMName, testNS, nil, nil)
expErr := fmt.Errorf("unable to create ConfigMap: %v", testErr)
convey.So(err, convey.ShouldResemble, expErr)
})
convey.Convey("test func 'CreateOrUpdateConfigMap' success. cm existed, update success", t, func() {
DeleteConfigMap(testCMName, testNS)
createCM(t)
err := CreateOrUpdateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("test func 'CreateOrUpdateConfigMap' failed. cm existed, update error", t, func() {
DeleteConfigMap(testCMName, testNS)
createCM(t)
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().ConfigMaps(v1.NamespaceAll),
"Update", nil, testErr)
defer p1.Reset()
err := CreateOrUpdateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldResemble, testErr)
})
}
func TestUpdateOrCreateCM(t *testing.T) {
convey.Convey("test func 'UpdateOrCreateConfigMap' success. cm existed, update success", t, func() {
DeleteConfigMap(testCMName, testNS)
createCM(t)
err := UpdateOrCreateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("test func 'UpdateOrCreateConfigMap' failed. cm existed, update error", t, func() {
DeleteConfigMap(testCMName, testNS)
createCM(t)
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().ConfigMaps(v1.NamespaceAll),
"Update", nil, testErr)
defer p1.Reset()
err := UpdateOrCreateConfigMap(testCMName, testNS, nil, nil)
expErr := fmt.Errorf("unable to update ConfigMap: %v", testErr)
convey.So(err, convey.ShouldResemble, expErr)
})
convey.Convey("test func 'UpdateOrCreateConfigMap' success. cm does not exist, create success", t, func() {
DeleteConfigMap(testCMName, testNS)
err := UpdateOrCreateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("test func 'UpdateOrCreateConfigMap' failed. cm does not exist, create error", t, func() {
DeleteConfigMap(testCMName, testNS)
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().ConfigMaps(v1.NamespaceAll),
"Create", &v1.ConfigMap{}, testErr)
defer p1.Reset()
err := UpdateOrCreateConfigMap(testCMName, testNS, nil, nil)
convey.So(err, convey.ShouldResemble, testErr)
})
}
func createPod(t *testing.T) {
pod = &v1.Pod{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: testPodName,
Namespace: testNS,
},
Spec: v1.PodSpec{},
Status: v1.PodStatus{},
}
_, err := GetClientK8s().ClientSet.CoreV1().Pods(testNS).Create(context.TODO(), pod, metav1.CreateOptions{})
if err != nil {
t.Errorf("create test pod failed, error: %v", err)
}
}
func deletePod(t *testing.T) {
if err := GetClientK8s().ClientSet.CoreV1().Pods(testNS).Delete(context.TODO(),
testPodName, metav1.DeleteOptions{}); err != nil {
t.Error(err)
}
}
func TestPatchPodLabel(t *testing.T) {
createPod(t)
defer deletePod(t)
convey.Convey("test func 'PatchPodLabel' success", t, func() {
patchData := map[string]string{testKey1: testValue1, testKey2: testValue2}
newPod, err := PatchPodLabel(testPodName, testNS, patchData)
expData := map[string]string{testKey1: testValue1, testKey2: testValue2}
convey.So(err, convey.ShouldBeNil)
convey.So(newPod.Labels, convey.ShouldResemble, expData)
patchData = map[string]string{testKey2: testValue1, testKey3: testValue3}
newPod, err = PatchPodLabel(testPodName, testNS, patchData)
expData = map[string]string{testKey1: testValue1, testKey2: testValue1, testKey3: testValue3}
convey.So(err, convey.ShouldBeNil)
convey.So(newPod.Labels, convey.ShouldResemble, expData)
})
convey.Convey("test func 'PatchPodLabel' failed, marshal data failed", t, func() {
p1 := gomonkey.ApplyFuncReturn(json.Marshal, nil, testErr)
defer p1.Reset()
patchData := map[string]string{testKey1: testValue1}
_, err := PatchPodLabel(testPodName, testNS, patchData)
convey.So(err, convey.ShouldResemble, testErr)
})
}
func TestRetryPatchPodLabels(t *testing.T) {
convey.Convey("test func 'RetryPatchPodLabels' success", t, func() {
p1 := gomonkey.ApplyFuncReturn(PatchPodLabel, pod, nil)
defer p1.Reset()
err := RetryPatchPodLabels(testPodName, testNS, 1, nil)
convey.So(err, convey.ShouldBeNil)
})
convey.Convey("test func 'RetryPatchPodLabels' failed, PatchPodLabel error", t, func() {
p1 := gomonkey.ApplyFuncReturn(PatchPodLabel, nil, testErr)
defer p1.Reset()
err := RetryPatchPodLabels(testPodName, testNS, 1, nil)
convey.So(err, convey.ShouldResemble, testErr)
})
}
func TestGetJobEvent(t *testing.T) {
convey.Convey("get event error", t, func() {
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().Events(v1.NamespaceAll),
"List", nil, testErr)
defer p1.Reset()
_, err := GetJobEvent(testNS, testName, testJobType)
convey.So(err, convey.ShouldResemble, testErr)
})
convey.Convey("get event ok", t, func() {
p1 := gomonkey.ApplyMethodReturn(GetClientK8s().ClientSet.CoreV1().Events(v1.NamespaceAll),
"List", nil, nil)
defer p1.Reset()
_, err := GetJobEvent(testNS, testName, testJobType)
convey.So(err, convey.ShouldBeNil)
})
}
func TestFaultJobReleaseInfoConsumer(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
const (
duration = 200 * time.Millisecond
newDuration = 100 * time.Millisecond
)
patch := gomonkey.ApplyFuncReturn(getPreReleaseWaitDuration, duration).
ApplyFuncReturn(getReleaseWaitDuration, duration)
defer patch.Reset()
go StartFaultJobReleaseInfoConsumer(ctx)
convey.Convey("test RecoverFaultJobInfoCm", t, func() {
called := false
patch1 := gomonkey.ApplyFunc(GetConfigMap, func(string, string) (*v1.ConfigMap, error) {
called = true
return nil, errors.New("fake error")
})
defer patch1.Reset()
RecoverFaultJobInfoCm(testJob1, testNode1)
time.Sleep(newDuration)
RecoverFaultJobInfoCm(testJob1, testNode1)
time.Sleep(duration + newDuration)
convey.So(called, convey.ShouldBeTrue)
})
convey.Convey("test RecoverFaultJobInfoCmWithSync", t, func() {
called := false
patch1 := gomonkey.ApplyFunc(GetConfigMap, func(string, string) (*v1.ConfigMap, error) {
called = true
return nil, errors.New("fake error")
})
defer patch1.Reset()
RecoverFaultJobInfoCmWithSync(testJob1, testNode1)
time.Sleep(newDuration)
RecoverFaultJobInfoCmWithSync(testJob2, testNode2)
time.Sleep(duration + duration + duration + duration + newDuration)
convey.So(called, convey.ShouldBeTrue)
})
}
func TestRunRecoverFaultJobInfoCmTask(t *testing.T) {
convey.Convey("test func 'UpdateFaultJobInfoCmWhenJobDelete' success", t, func() {
testJobId := "test-job-id"
testCm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: api.FaultJobCmName,
Namespace: api.ClusterNS,
},
Data: map[string]string{
testJobId: "test-data",
},
}
p1 := gomonkey.ApplyFuncReturn(GetConfigMap, testCm, nil)
defer p1.Reset()
p2 := gomonkey.ApplyFuncReturn(UpdateConfigMap, testCm, nil)
defer p2.Reset()
runRecoverFaultJobInfoCmTask(testJobId, "")
_, exists := testCm.Data[testJobId]
convey.So(exists, convey.ShouldBeFalse)
})
convey.Convey("test func 'UpdateFaultJobInfoCmWhenJobDelete' with GetConfigMap error", t, func() {
p1 := gomonkey.ApplyFuncReturn(GetConfigMap, nil, testErr)
defer p1.Reset()
runRecoverFaultJobInfoCmTask("test-job-id", "")
})
convey.Convey("test func 'UpdateFaultJobInfoCmWhenJobDelete' with UpdateConfigMap error", t, func() {
testJobId := "test-job-id"
testCm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: api.FaultJobCmName,
Namespace: api.ClusterNS,
},
Data: map[string]string{
testJobId: "test-data",
},
}
p1 := gomonkey.ApplyFuncReturn(GetConfigMap, testCm, nil)
defer p1.Reset()
p2 := gomonkey.ApplyFuncReturn(UpdateConfigMap, nil, testErr)
defer p2.Reset()
runRecoverFaultJobInfoCmTask(testJobId, "")
})
}
func TestCreateOrUpdateSuperPodFaultInfo(t *testing.T) {
const fakeTime = 123456789
convey.Convey("Test CreateOrUpdateSuperPodFaultInfo", t, func() {
testJobId := "test-job-id"
testFaultInfos := map[int]api.SuperPodFaultInfos{
1: {FaultTimes: fakeTime},
}
convey.Convey("Should update existing configmap failed", func() {
testCm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: api.FaultJobCmName,
Namespace: api.ClusterNS,
},
Data: nil,
}
patch1 := gomonkey.ApplyFuncReturn(GetConfigMap, testCm, nil)
defer patch1.Reset()
patch2 := gomonkey.ApplyFuncReturn(UpdateConfigMap, testCm, errors.New("fake error"))
defer patch2.Reset()
patch3 := gomonkey.ApplyFuncReturn(json.Marshal, []byte(`{}`), nil)
defer patch3.Reset()
CreateOrUpdateSuperPodFaultInfo(testJobId, testFaultInfos)
_, exists := testCm.Data[testJobId]
convey.So(exists, convey.ShouldBeFalse)
})
convey.Convey("Should handle GetConfigMap error", func() {
patch1 := gomonkey.ApplyFuncReturn(GetConfigMap, nil, testErr)
defer patch1.Reset()
patch2 := gomonkey.ApplyFunc(hwlog.RunLog.Errorf, func(format string, args ...interface{}) {
convey.So(format, convey.ShouldContainSubstring, "get configmap fault-job-info err")
})
defer patch2.Reset()
CreateOrUpdateSuperPodFaultInfo(testJobId, testFaultInfos)
})
})
}