563 lines
14 KiB
Go
563 lines
14 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 (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/apimachinery/pkg/util/uuid"
|
|
"k8s.io/client-go/informers"
|
|
kubeclientset "k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
|
|
batch "volcano.sh/volcano/pkg/apis/batch/v1alpha1"
|
|
bus "volcano.sh/volcano/pkg/apis/bus/v1alpha1"
|
|
"volcano.sh/volcano/pkg/apis/helpers"
|
|
scheduling "volcano.sh/volcano/pkg/apis/scheduling/v1beta1"
|
|
vcclientset "volcano.sh/volcano/pkg/client/clientset/versioned"
|
|
)
|
|
|
|
func newController() *Controller {
|
|
kubeClientSet := kubeclientset.NewForConfigOrDie(&rest.Config{
|
|
Host: "",
|
|
ContentConfig: rest.ContentConfig{
|
|
GroupVersion: &v1.SchemeGroupVersion,
|
|
},
|
|
},
|
|
)
|
|
|
|
vcclient := vcclientset.NewForConfigOrDie(&rest.Config{
|
|
Host: "",
|
|
ContentConfig: rest.ContentConfig{
|
|
GroupVersion: &batch.SchemeGroupVersion,
|
|
},
|
|
})
|
|
|
|
sharedInformers := informers.NewSharedInformerFactory(kubeClientSet, 0)
|
|
|
|
controller := NewJobController(kubeClientSet, vcclient, sharedInformers, 3)
|
|
|
|
return controller
|
|
}
|
|
|
|
func buildPod(namespace, name string, p v1.PodPhase, labels map[string]string) *v1.Pod {
|
|
boolValue := true
|
|
return &v1.Pod{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: types.UID(fmt.Sprintf("%v-%v", namespace, name)),
|
|
Name: name,
|
|
Namespace: namespace,
|
|
Labels: labels,
|
|
ResourceVersion: string(uuid.NewUUID()),
|
|
OwnerReferences: []metav1.OwnerReference{
|
|
{
|
|
APIVersion: helpers.JobKind.GroupVersion().String(),
|
|
Kind: helpers.JobKind.Kind,
|
|
Controller: &boolValue,
|
|
},
|
|
},
|
|
},
|
|
Status: v1.PodStatus{
|
|
Phase: p,
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx:latest",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func addPodAnnotation(pod *v1.Pod, annotations map[string]string) *v1.Pod {
|
|
podWithAnnotation := pod
|
|
for key, value := range annotations {
|
|
if podWithAnnotation.Annotations == nil {
|
|
podWithAnnotation.Annotations = make(map[string]string)
|
|
}
|
|
podWithAnnotation.Annotations[key] = value
|
|
}
|
|
return podWithAnnotation
|
|
}
|
|
|
|
func TestAddCommandFunc(t *testing.T) {
|
|
|
|
namespace := "test"
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
command interface{}
|
|
ExpectValue int
|
|
}{
|
|
{
|
|
Name: "AddCommand Success Case",
|
|
command: &bus.Command{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "Valid Command",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
ExpectValue: 1,
|
|
},
|
|
{
|
|
Name: "AddCommand Failure Case",
|
|
command: "Command",
|
|
ExpectValue: 0,
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testCases {
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addCommand(testcase.command)
|
|
len := controller.commandQueue.Len()
|
|
if testcase.ExpectValue != len {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJobAddFunc(t *testing.T) {
|
|
namespace := "test"
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
job *batch.Job
|
|
ExpectValue int
|
|
}{
|
|
{
|
|
Name: "AddJob Success",
|
|
job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "Job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
ExpectValue: 1,
|
|
},
|
|
}
|
|
for i, testcase := range testCases {
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addJob(testcase.job)
|
|
key := fmt.Sprintf("%s/%s", testcase.job.Namespace, testcase.job.Name)
|
|
job, err := controller.cache.Get(key)
|
|
if job == nil || err != nil {
|
|
t.Errorf("Error while Adding Job in case %d with error %s", i, err)
|
|
}
|
|
queue := controller.getWorkerQueue(key)
|
|
len := queue.Len()
|
|
if testcase.ExpectValue != len {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateJobFunc(t *testing.T) {
|
|
namespace := "test"
|
|
|
|
testcases := []struct {
|
|
Name string
|
|
oldJob *batch.Job
|
|
newJob *batch.Job
|
|
}{
|
|
{
|
|
Name: "Job Update Success Case",
|
|
oldJob: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
ResourceVersion: "54467984",
|
|
},
|
|
Spec: batch.JobSpec{
|
|
SchedulerName: "volcano",
|
|
MinAvailable: 5,
|
|
},
|
|
Status: batch.JobStatus{
|
|
State: batch.JobState{
|
|
Phase: batch.Pending,
|
|
},
|
|
},
|
|
},
|
|
newJob: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
ResourceVersion: "54469999",
|
|
},
|
|
Spec: batch.JobSpec{
|
|
SchedulerName: "volcano",
|
|
MinAvailable: 5,
|
|
},
|
|
Status: batch.JobStatus{
|
|
State: batch.JobState{
|
|
Phase: batch.Running,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "Job Update Failure Case",
|
|
oldJob: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
ResourceVersion: "54469999",
|
|
},
|
|
Spec: batch.JobSpec{
|
|
SchedulerName: "volcano",
|
|
MinAvailable: 5,
|
|
},
|
|
Status: batch.JobStatus{
|
|
State: batch.JobState{
|
|
Phase: batch.Pending,
|
|
},
|
|
},
|
|
},
|
|
newJob: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
ResourceVersion: "54469999",
|
|
},
|
|
Spec: batch.JobSpec{
|
|
SchedulerName: "volcano",
|
|
MinAvailable: 5,
|
|
},
|
|
Status: batch.JobStatus{
|
|
State: batch.JobState{
|
|
Phase: batch.Pending,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testcases {
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addJob(testcase.oldJob)
|
|
controller.updateJob(testcase.oldJob, testcase.newJob)
|
|
key := fmt.Sprintf("%s/%s", testcase.newJob.Namespace, testcase.newJob.Name)
|
|
job, err := controller.cache.Get(key)
|
|
|
|
if job == nil || err != nil {
|
|
t.Errorf("Error while Updating Job in case %d with error %s", i, err)
|
|
}
|
|
|
|
if job.Job.Status.State.Phase != testcase.newJob.Status.State.Phase {
|
|
t.Errorf("Error while Updating Job in case %d with error %s", i, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddPodFunc(t *testing.T) {
|
|
namespace := "test"
|
|
|
|
testcases := []struct {
|
|
Name string
|
|
Job *batch.Job
|
|
pods []*v1.Pod
|
|
Annotation map[string]string
|
|
ExpectedValue int
|
|
}{
|
|
{
|
|
Name: "AddPod Success case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
pods: []*v1.Pod{
|
|
buildPod(namespace, "pod1", v1.PodPending, nil),
|
|
},
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: 1,
|
|
},
|
|
{
|
|
Name: "AddPod Duplicate Pod case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
pods: []*v1.Pod{
|
|
buildPod(namespace, "pod1", v1.PodPending, nil),
|
|
buildPod(namespace, "pod1", v1.PodPending, nil),
|
|
},
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: 1,
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testcases {
|
|
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addJob(testcase.Job)
|
|
for _, pod := range testcase.pods {
|
|
addPodAnnotation(pod, testcase.Annotation)
|
|
controller.addPod(pod)
|
|
}
|
|
|
|
key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name)
|
|
job, err := controller.cache.Get(key)
|
|
|
|
if job == nil || err != nil {
|
|
t.Errorf("Error while Getting Job in case %d with error %s", i, err)
|
|
}
|
|
|
|
var totalPods int
|
|
for _, task := range job.Pods {
|
|
totalPods = len(task)
|
|
}
|
|
if totalPods != testcase.ExpectedValue {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, totalPods)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdatePodFunc(t *testing.T) {
|
|
namespace := "test"
|
|
|
|
testcases := []struct {
|
|
Name string
|
|
Job *batch.Job
|
|
oldPod *v1.Pod
|
|
newPod *v1.Pod
|
|
Annotation map[string]string
|
|
ExpectedValue v1.PodPhase
|
|
}{
|
|
{
|
|
Name: "UpdatePod Success case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
oldPod: buildPod(namespace, "pod1", v1.PodPending, nil),
|
|
newPod: buildPod(namespace, "pod1", v1.PodRunning, nil),
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: v1.PodRunning,
|
|
},
|
|
{
|
|
Name: "UpdatePod Failed case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
oldPod: buildPod(namespace, "pod1", v1.PodPending, nil),
|
|
newPod: buildPod(namespace, "pod1", v1.PodFailed, nil),
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: v1.PodFailed,
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testcases {
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addJob(testcase.Job)
|
|
addPodAnnotation(testcase.oldPod, testcase.Annotation)
|
|
addPodAnnotation(testcase.newPod, testcase.Annotation)
|
|
controller.addPod(testcase.oldPod)
|
|
controller.updatePod(testcase.oldPod, testcase.newPod)
|
|
|
|
key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name)
|
|
job, err := controller.cache.Get(key)
|
|
|
|
if job == nil || err != nil {
|
|
t.Errorf("Error while Getting Job in case %d with error %s", i, err)
|
|
}
|
|
|
|
pod := job.Pods[testcase.Annotation[batch.TaskSpecKey]][testcase.oldPod.Name]
|
|
|
|
if pod.Status.Phase != testcase.ExpectedValue {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, pod.Status.Phase)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeletePodFunc(t *testing.T) {
|
|
namespace := "test"
|
|
|
|
testcases := []struct {
|
|
Name string
|
|
Job *batch.Job
|
|
availablePods []*v1.Pod
|
|
deletePod *v1.Pod
|
|
Annotation map[string]string
|
|
ExpectedValue int
|
|
}{
|
|
{
|
|
Name: "DeletePod success case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
availablePods: []*v1.Pod{
|
|
buildPod(namespace, "pod1", v1.PodRunning, nil),
|
|
buildPod(namespace, "pod2", v1.PodRunning, nil),
|
|
},
|
|
deletePod: buildPod(namespace, "pod2", v1.PodRunning, nil),
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: 1,
|
|
},
|
|
{
|
|
Name: "DeletePod Pod NotAvailable case",
|
|
Job: &batch.Job{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "job1",
|
|
Namespace: namespace,
|
|
},
|
|
},
|
|
availablePods: []*v1.Pod{
|
|
buildPod(namespace, "pod1", v1.PodRunning, nil),
|
|
buildPod(namespace, "pod2", v1.PodRunning, nil),
|
|
},
|
|
deletePod: buildPod(namespace, "pod3", v1.PodRunning, nil),
|
|
Annotation: map[string]string{
|
|
batch.JobNameKey: "job1",
|
|
batch.JobVersion: "0",
|
|
batch.TaskSpecKey: "task1",
|
|
},
|
|
ExpectedValue: 2,
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testcases {
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.addJob(testcase.Job)
|
|
for _, pod := range testcase.availablePods {
|
|
addPodAnnotation(pod, testcase.Annotation)
|
|
controller.addPod(pod)
|
|
}
|
|
|
|
addPodAnnotation(testcase.deletePod, testcase.Annotation)
|
|
controller.deletePod(testcase.deletePod)
|
|
key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name)
|
|
job, err := controller.cache.Get(key)
|
|
|
|
if job == nil || err != nil {
|
|
t.Errorf("Error while Getting Job in case %d with error %s", i, err)
|
|
}
|
|
|
|
var totalPods int
|
|
for _, task := range job.Pods {
|
|
totalPods = len(task)
|
|
}
|
|
|
|
if totalPods != testcase.ExpectedValue {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, totalPods)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdatePodGroupFunc(t *testing.T) {
|
|
|
|
namespace := "test"
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
oldPodGroup *scheduling.PodGroup
|
|
newPodGroup *scheduling.PodGroup
|
|
ExpectValue int
|
|
}{
|
|
{
|
|
Name: "AddCommand Success Case",
|
|
oldPodGroup: &scheduling.PodGroup{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pg1",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: scheduling.PodGroupSpec{
|
|
MinMember: 3,
|
|
},
|
|
Status: scheduling.PodGroupStatus{
|
|
Phase: scheduling.PodGroupPending,
|
|
},
|
|
},
|
|
newPodGroup: &scheduling.PodGroup{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "pg1",
|
|
Namespace: namespace,
|
|
},
|
|
Spec: scheduling.PodGroupSpec{
|
|
MinMember: 3,
|
|
},
|
|
Status: scheduling.PodGroupStatus{
|
|
Phase: scheduling.PodGroupRunning,
|
|
},
|
|
},
|
|
ExpectValue: 1,
|
|
},
|
|
}
|
|
|
|
for i, testcase := range testCases {
|
|
|
|
t.Run(testcase.Name, func(t *testing.T) {
|
|
controller := newController()
|
|
controller.updatePodGroup(testcase.oldPodGroup, testcase.newPodGroup)
|
|
key := fmt.Sprintf("%s/%s", testcase.oldPodGroup.Namespace, testcase.oldPodGroup.Name)
|
|
queue := controller.getWorkerQueue(key)
|
|
len := queue.Len()
|
|
if testcase.ExpectValue != len {
|
|
t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len)
|
|
}
|
|
})
|
|
}
|
|
}
|