volcano/pkg/controllers/job/job_controller_actions_test.go

476 lines
13 KiB
Go

/*
Copyright 2019 The Volcano 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 job
import (
"errors"
"fmt"
"testing"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"volcano.sh/volcano/pkg/apis/batch/v1alpha1"
schedulingv1alpha2 "volcano.sh/volcano/pkg/apis/scheduling/v1beta1"
"volcano.sh/volcano/pkg/controllers/apis"
"volcano.sh/volcano/pkg/controllers/job/state"
)
func TestKillJobFunc(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
PodGroup *schedulingv1alpha2.PodGroup
PodRetainPhase state.PhaseMap
UpdateStatus state.UpdateStatusFn
JobInfo *apis.JobInfo
Services []v1.Service
ConfigMaps []v1.ConfigMap
Secrets []v1.Secret
Pods map[string]*v1.Pod
Plugins []string
ExpextVal error
}{
{
Name: "KillJob success Case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
UID: "e7f18111-1cec-11ea-b688-fa163ec79500",
},
},
PodGroup: &schedulingv1alpha2.PodGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
},
PodRetainPhase: state.PodRetainPhaseNone,
UpdateStatus: nil,
JobInfo: &apis.JobInfo{
Namespace: namespace,
Name: "jobinfo1",
Pods: map[string]map[string]*v1.Pod{
"task1": {
"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
},
},
},
Services: []v1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
},
},
Secrets: []v1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Name: "job1-e7f18111-1cec-11ea-b688-fa163ec79500-ssh",
Namespace: namespace,
},
},
},
Pods: map[string]*v1.Pod{
"pod1": buildPod(namespace, "pod1", v1.PodRunning, nil),
"pod2": buildPod(namespace, "pod2", v1.PodRunning, nil),
},
Plugins: []string{"svc", "ssh", "env"},
ExpextVal: nil,
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
jobPlugins := make(map[string][]string)
for _, service := range testcase.Services {
_, err := fakeController.kubeClient.CoreV1().Services(namespace).Create(&service)
if err != nil {
t.Error("Error While Creating Service")
}
}
for _, secret := range testcase.Secrets {
_, err := fakeController.kubeClient.CoreV1().Secrets(namespace).Create(&secret)
if err != nil {
t.Error("Error While Creating Secret.")
}
}
for _, pod := range testcase.Pods {
_, err := fakeController.kubeClient.CoreV1().Pods(namespace).Create(pod)
if err != nil {
t.Error("Error While Creating ConfigMaps")
}
}
_, err := fakeController.vcClient.BatchV1alpha1().Jobs(namespace).Create(testcase.Job)
if err != nil {
t.Error("Error While Creating Jobs")
}
err = fakeController.cache.Add(testcase.Job)
if err != nil {
t.Error("Error While Adding Job in cache")
}
for _, plugin := range testcase.Plugins {
jobPlugins[plugin] = make([]string, 0)
}
testcase.JobInfo.Job = testcase.Job
testcase.JobInfo.Job.Spec.Plugins = jobPlugins
err = fakeController.killJob(testcase.JobInfo, testcase.PodRetainPhase, testcase.UpdateStatus)
if err != nil {
t.Errorf("Case %d (%s): expected: No Error, but got error %v.", i, testcase.Name, err)
}
for _, plugin := range testcase.Plugins {
if plugin == "svc" {
_, err = fakeController.kubeClient.CoreV1().Services(namespace).Get(testcase.Job.Name, metav1.GetOptions{})
if err == nil {
t.Errorf("Case %d (%s): expected: Service to be deleted, but not deleted.", i, testcase.Name)
}
}
if plugin == "ssh" {
_, err := fakeController.kubeClient.CoreV1().ConfigMaps(namespace).Get(
fmt.Sprintf("%s-%s-%s", testcase.Job.Name, testcase.Job.UID, "ssh"), metav1.GetOptions{})
if err == nil {
t.Errorf("Case %d (%s): expected: Secret to be deleted, but not deleted.", i, testcase.Name)
}
}
}
})
}
}
func TestSyncJobFunc(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
PodGroup *schedulingv1alpha2.PodGroup
PodRetainPhase state.PhaseMap
UpdateStatus state.UpdateStatusFn
JobInfo *apis.JobInfo
Pods map[string]*v1.Pod
Plugins []string
TotalNumPods int
ExpextVal error
}{
{
Name: "SyncJob success Case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
Tasks: []v1alpha1.TaskSpec{
{
Name: "task1",
Replicas: 6,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: "pods",
Namespace: namespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "Containers",
},
},
},
},
},
},
},
Status: v1alpha1.JobStatus{
State: v1alpha1.JobState{
Phase: v1alpha1.Pending,
},
},
},
PodGroup: &schedulingv1alpha2.PodGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Status: schedulingv1alpha2.PodGroupStatus{
Phase: schedulingv1alpha2.PodGroupInqueue,
},
},
PodRetainPhase: state.PodRetainPhaseNone,
UpdateStatus: nil,
JobInfo: &apis.JobInfo{
Namespace: namespace,
Name: "jobinfo1",
Pods: map[string]map[string]*v1.Pod{
"task1": {
"job1-task1-0": buildPod(namespace, "job1-task1-0", v1.PodRunning, nil),
"job1-task1-1": buildPod(namespace, "job1-task1-1", v1.PodRunning, nil),
},
},
},
Pods: map[string]*v1.Pod{
"job1-task1-0": buildPod(namespace, "job1-task1-0", v1.PodRunning, nil),
"job1-task1-1": buildPod(namespace, "job1-task1-1", v1.PodRunning, nil),
},
TotalNumPods: 6,
Plugins: []string{"svc", "ssh", "env"},
ExpextVal: nil,
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
jobPlugins := make(map[string][]string)
for _, plugin := range testcase.Plugins {
jobPlugins[plugin] = make([]string, 0)
}
testcase.JobInfo.Job = testcase.Job
testcase.JobInfo.Job.Spec.Plugins = jobPlugins
fakeController.pgInformer.Informer().GetIndexer().Add(testcase.PodGroup)
for _, pod := range testcase.Pods {
_, err := fakeController.kubeClient.CoreV1().Pods(namespace).Create(pod)
if err != nil {
t.Error("Error While Creating pods")
}
}
_, err := fakeController.vcClient.BatchV1alpha1().Jobs(namespace).Create(testcase.Job)
if err != nil {
t.Errorf("Expected no Error while creating job, but got error: %s", err)
}
err = fakeController.cache.Add(testcase.Job)
if err != nil {
t.Error("Error While Adding Job in cache")
}
err = fakeController.syncJob(testcase.JobInfo, nil)
if err != testcase.ExpextVal {
t.Errorf("Expected no error while syncing job, but got error: %s", err)
}
podList, err := fakeController.kubeClient.CoreV1().Pods(namespace).List(metav1.ListOptions{})
if err != nil {
t.Errorf("Expected no error while listing pods, but got error %s in case %d", err, i)
}
if testcase.TotalNumPods != len(podList.Items) {
t.Errorf("Expected Total number of pods to be same as podlist count: Expected: %d, Got: %d in case: %d", testcase.TotalNumPods, len(podList.Items), i)
}
})
}
}
func TestCreateJobIOIfNotExistFunc(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
ExpextVal error
}{
{
Name: "Create Job IO case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
Volumes: []v1alpha1.VolumeSpec{
{
VolumeClaimName: "pvc1",
},
},
},
},
ExpextVal: errors.New("pvc pvc1 is not found, the job will be in the Pending state until the PVC is created"),
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
job, err := fakeController.createJobIOIfNotExist(testcase.Job)
if testcase.ExpextVal == nil {
if err != nil {
t.Errorf("Expected Return value to be : %v, but got: %v in testcase %d", testcase.ExpextVal, err, i)
}
} else {
if err == nil || err.Error() != testcase.ExpextVal.Error() {
t.Errorf("Expected Return value to be : %v, but got: %v in testcase %d", testcase.ExpextVal.Error(), err.Error(), i)
}
}
if len(job.Spec.Volumes) == 0 {
t.Errorf("Expected number of volumes to be greater than 0 but got: %d in case: %d", len(job.Spec.Volumes), i)
}
})
}
}
func TestCreatePVCFunc(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
VolumeClaim *v1.PersistentVolumeClaimSpec
ExpextVal error
}{
{
Name: "CreatePVC success Case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
},
VolumeClaim: &v1.PersistentVolumeClaimSpec{
VolumeName: "vol1",
},
ExpextVal: nil,
},
}
for _, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
err := fakeController.createPVC(testcase.Job, "pvc1", testcase.VolumeClaim)
if err != testcase.ExpextVal {
t.Errorf("Expected return value to be equal to expected: %s, but got: %s", testcase.ExpextVal, err)
}
_, err = fakeController.kubeClient.CoreV1().PersistentVolumeClaims(namespace).Get("pvc1", metav1.GetOptions{})
if err != nil {
t.Error("Expected PVC to get created, but not created")
}
})
}
}
func TestCreatePodGroupIfNotExistFunc(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
ExpextVal error
}{
{
Name: "CreatePodGroup success Case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: "job1",
},
},
ExpextVal: nil,
},
}
for _, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
err := fakeController.createOrUpdatePodGroup(testcase.Job)
if err != testcase.ExpextVal {
t.Errorf("Expected return value to be equal to expected: %s, but got: %s", testcase.ExpextVal, err)
}
_, err = fakeController.vcClient.SchedulingV1beta1().PodGroups(namespace).Get(testcase.Job.Name, metav1.GetOptions{})
if err != nil {
t.Error("Expected PodGroup to get created, but not created")
}
})
}
}
func TestDeleteJobPod(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
Pods map[string]*v1.Pod
DeletePod *v1.Pod
ExpextVal error
}{
{
Name: "DeleteJobPod success case",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
},
Pods: map[string]*v1.Pod{
"job1-task1-0": buildPod(namespace, "job1-task1-0", v1.PodRunning, nil),
"job1-task1-1": buildPod(namespace, "job1-task1-1", v1.PodRunning, nil),
},
DeletePod: buildPod(namespace, "job1-task1-0", v1.PodRunning, nil),
ExpextVal: nil,
},
}
for _, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
fakeController := newFakeController()
for _, pod := range testcase.Pods {
_, err := fakeController.kubeClient.CoreV1().Pods(namespace).Create(pod)
if err != nil {
t.Error("Expected error not to occur")
}
}
err := fakeController.deleteJobPod(testcase.Job.Name, testcase.DeletePod)
if err != testcase.ExpextVal {
t.Errorf("Expected return value to be equal to expected: %s, but got: %s", testcase.ExpextVal, err)
}
_, err = fakeController.kubeClient.CoreV1().Pods(namespace).Get("job1-task1-0", metav1.GetOptions{})
if err == nil {
t.Error("Expected Pod to be deleted but not deleted")
}
})
}
}