volcano/pkg/controllers/job/job_controller_util_test.go

719 lines
15 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 (
"testing"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"volcano.sh/volcano/pkg/apis/batch/v1alpha1"
busv1alpha1 "volcano.sh/volcano/pkg/apis/bus/v1alpha1"
"volcano.sh/volcano/pkg/controllers/apis"
)
func TestMakePodName(t *testing.T) {
testcases := []struct {
Name string
TaskName string
JobName string
Index int
ReturnVal string
}{
{
Name: "Test MakePodName function",
TaskName: "task1",
JobName: "job1",
Index: 1,
ReturnVal: "job1-task1-1",
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
podName := MakePodName(testcase.JobName, testcase.TaskName, testcase.Index)
if podName != testcase.ReturnVal {
t.Errorf("Expected Return value to be: %s, but got: %s in case %d", testcase.ReturnVal, podName, i)
}
})
}
}
func TestCreateJobPod(t *testing.T) {
namespace := "test"
testcases := []struct {
Name string
Job *v1alpha1.Job
PodTemplate *v1.PodTemplateSpec
Index int
ReturnVal *v1.Pod
}{
{
Name: "Test Create Job Pod",
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",
},
},
},
},
},
},
},
},
PodTemplate: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "Containers",
},
},
},
},
Index: 0,
ReturnVal: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "job1-task1-0",
Namespace: namespace,
},
},
},
{
Name: "Test Create Job Pod with volumes",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
Volumes: []v1alpha1.VolumeSpec{
{
VolumeClaimName: "vc1",
},
{
VolumeClaimName: "vc2",
},
},
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",
},
},
},
},
},
},
},
},
PodTemplate: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "Containers",
},
},
},
},
Index: 0,
ReturnVal: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "job1-task1-0",
Namespace: namespace,
},
},
},
{
Name: "Test Create Job Pod with volumes added to controlled resources",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
Volumes: []v1alpha1.VolumeSpec{
{
VolumeClaimName: "vc1",
VolumeClaim: &v1.PersistentVolumeClaimSpec{
VolumeName: "v1",
},
},
{
VolumeClaimName: "vc2",
},
},
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",
},
},
},
},
},
},
},
},
PodTemplate: &v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "Containers",
},
},
},
},
Index: 0,
ReturnVal: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "job1-task1-0",
Namespace: namespace,
},
},
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
pod := createJobPod(testcase.Job, testcase.PodTemplate, testcase.Index)
if testcase.ReturnVal != nil && pod != nil && pod.Name != testcase.ReturnVal.Name && pod.Namespace != testcase.ReturnVal.Namespace {
t.Errorf("Expected Return Value to be %v but got %v in case %d", testcase.ReturnVal, pod, i)
}
})
}
}
func TestApplyPolicies(t *testing.T) {
namespace := "test"
errorCode0 := int32(0)
testcases := []struct {
Name string
Job *v1alpha1.Job
Request *apis.Request
ReturnVal busv1alpha1.Action
}{
{
Name: "Test Apply policies where Action is not empty",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
},
},
Request: &apis.Request{
Action: busv1alpha1.EnqueueAction,
},
ReturnVal: busv1alpha1.EnqueueAction,
},
{
Name: "Test Apply policies where event is OutOfSync",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
},
},
Request: &apis.Request{
Event: busv1alpha1.OutOfSyncEvent,
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies where version is outdated",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
},
},
Request: &apis.Request{
JobVersion: 1,
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies where overriding job level policies and with exitcode",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
Policies: []v1alpha1.LifecyclePolicy{
{
Action: busv1alpha1.SyncJobAction,
Event: busv1alpha1.CommandIssuedEvent,
ExitCode: &errorCode0,
},
},
},
},
},
},
Request: &apis.Request{
TaskName: "task1",
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies where overriding job level policies and without exitcode",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
Policies: []v1alpha1.LifecyclePolicy{
{
Action: busv1alpha1.SyncJobAction,
Event: busv1alpha1.CommandIssuedEvent,
},
},
},
},
},
},
Request: &apis.Request{
TaskName: "task1",
Event: busv1alpha1.CommandIssuedEvent,
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies with job level policies",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
},
},
Request: &apis.Request{
TaskName: "task1",
Event: busv1alpha1.CommandIssuedEvent,
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies with job level policies",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
Policies: []v1alpha1.LifecyclePolicy{
{
Action: busv1alpha1.SyncJobAction,
Event: busv1alpha1.CommandIssuedEvent,
},
},
},
},
Request: &apis.Request{
Event: busv1alpha1.CommandIssuedEvent,
},
ReturnVal: busv1alpha1.SyncJobAction,
},
{
Name: "Test Apply policies with job level policies with exitcode",
Job: &v1alpha1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "job1",
Namespace: namespace,
},
Spec: v1alpha1.JobSpec{
SchedulerName: "volcano",
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",
},
},
},
},
},
},
Policies: []v1alpha1.LifecyclePolicy{
{
Action: busv1alpha1.SyncJobAction,
Event: busv1alpha1.CommandIssuedEvent,
ExitCode: &errorCode0,
},
},
},
},
Request: &apis.Request{},
ReturnVal: busv1alpha1.SyncJobAction,
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
action := applyPolicies(testcase.Job, testcase.Request)
if testcase.ReturnVal != "" && action != "" && testcase.ReturnVal != action {
t.Errorf("Expected return value to be %s but got %s in case %d", testcase.ReturnVal, action, i)
}
})
}
}
func TestAddResourceList(t *testing.T) {
testcases := []struct {
Name string
List v1.ResourceList
New v1.ResourceList
}{
{
Name: "Already Present resource",
List: map[v1.ResourceName]resource.Quantity{
"cpu": *resource.NewQuantity(100, ""),
},
New: map[v1.ResourceName]resource.Quantity{
"cpu": *resource.NewQuantity(100, ""),
},
},
{
Name: "New resource",
List: map[v1.ResourceName]resource.Quantity{
"cpu": *resource.NewQuantity(100, ""),
},
New: map[v1.ResourceName]resource.Quantity{
"memory": *resource.NewQuantity(100, ""),
},
},
}
for _, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
addResourceList(testcase.List, testcase.New, nil)
})
}
}
func TestTasksPriority_Less(t *testing.T) {
testcases := []struct {
Name string
TasksPriority TasksPriority
Task1Index int
Task2Index int
ReturnVal bool
}{
{
Name: "False Case",
TasksPriority: []TaskPriority{
{
priority: 1,
},
{
priority: 2,
},
{
priority: 3,
},
},
Task1Index: 1,
Task2Index: 2,
ReturnVal: false,
},
{
Name: "True Case",
TasksPriority: []TaskPriority{
{
priority: 1,
},
{
priority: 2,
},
{
priority: 3,
},
},
Task1Index: 2,
Task2Index: 1,
ReturnVal: true,
},
}
for i, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
less := testcase.TasksPriority.Less(testcase.Task1Index, testcase.Task2Index)
if less != testcase.ReturnVal {
t.Errorf("Expected Return Value to be %t, but got %t in case %d", testcase.ReturnVal, less, i)
}
})
}
}
func TestTasksPriority_Swap(t *testing.T) {
testcases := []struct {
Name string
TasksPriority TasksPriority
Task1Index int
Task2Index int
ReturnVal bool
}{
{
Name: "False Case",
TasksPriority: []TaskPriority{
{
priority: 1,
},
{
priority: 2,
},
{
priority: 3,
},
},
Task1Index: 1,
Task2Index: 2,
},
{
Name: "True Case",
TasksPriority: []TaskPriority{
{
priority: 1,
},
{
priority: 2,
},
{
priority: 3,
},
},
Task1Index: 2,
Task2Index: 1,
},
}
for _, testcase := range testcases {
t.Run(testcase.Name, func(t *testing.T) {
testcase.TasksPriority.Swap(testcase.Task1Index, testcase.Task2Index)
})
}
}