volcano.sh/volcano@v1.9.0/pkg/controllers/job/job_controller_handler_test.go (about) 1 /* 2 Copyright 2019 The Volcano Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package job 18 19 import ( 20 "fmt" 21 "testing" 22 23 v1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/types" 26 "k8s.io/apimachinery/pkg/util/uuid" 27 "k8s.io/client-go/informers" 28 kubeclientset "k8s.io/client-go/kubernetes" 29 "k8s.io/client-go/rest" 30 31 batch "volcano.sh/apis/pkg/apis/batch/v1alpha1" 32 bus "volcano.sh/apis/pkg/apis/bus/v1alpha1" 33 "volcano.sh/apis/pkg/apis/helpers" 34 scheduling "volcano.sh/apis/pkg/apis/scheduling/v1beta1" 35 vcclientset "volcano.sh/apis/pkg/client/clientset/versioned" 36 "volcano.sh/volcano/pkg/controllers/framework" 37 ) 38 39 func newController() *jobcontroller { 40 kubeClientSet := kubeclientset.NewForConfigOrDie(&rest.Config{ 41 Host: "", 42 ContentConfig: rest.ContentConfig{ 43 GroupVersion: &v1.SchemeGroupVersion, 44 }, 45 }, 46 ) 47 48 vcclient := vcclientset.NewForConfigOrDie(&rest.Config{ 49 Host: "", 50 ContentConfig: rest.ContentConfig{ 51 GroupVersion: &batch.SchemeGroupVersion, 52 }, 53 }) 54 55 sharedInformers := informers.NewSharedInformerFactory(kubeClientSet, 0) 56 57 controller := &jobcontroller{} 58 opt := &framework.ControllerOption{ 59 VolcanoClient: vcclient, 60 KubeClient: kubeClientSet, 61 SharedInformerFactory: sharedInformers, 62 WorkerNum: 3, 63 } 64 65 controller.Initialize(opt) 66 67 return controller 68 } 69 70 func buildPod(namespace, name string, p v1.PodPhase, labels map[string]string) *v1.Pod { 71 boolValue := true 72 return &v1.Pod{ 73 ObjectMeta: metav1.ObjectMeta{ 74 UID: types.UID(fmt.Sprintf("%v-%v", namespace, name)), 75 Name: name, 76 Namespace: namespace, 77 Labels: labels, 78 ResourceVersion: string(uuid.NewUUID()), 79 OwnerReferences: []metav1.OwnerReference{ 80 { 81 APIVersion: helpers.JobKind.GroupVersion().String(), 82 Kind: helpers.JobKind.Kind, 83 Controller: &boolValue, 84 }, 85 }, 86 }, 87 Status: v1.PodStatus{ 88 Phase: p, 89 }, 90 Spec: v1.PodSpec{ 91 Containers: []v1.Container{ 92 { 93 Name: "nginx", 94 Image: "nginx:latest", 95 }, 96 }, 97 }, 98 } 99 } 100 101 func addPodAnnotation(pod *v1.Pod, annotations map[string]string) *v1.Pod { 102 podWithAnnotation := pod 103 for key, value := range annotations { 104 if podWithAnnotation.Annotations == nil { 105 podWithAnnotation.Annotations = make(map[string]string) 106 } 107 podWithAnnotation.Annotations[key] = value 108 } 109 return podWithAnnotation 110 } 111 112 func TestAddCommandFunc(t *testing.T) { 113 114 namespace := "test" 115 116 testCases := []struct { 117 Name string 118 command interface{} 119 ExpectValue int 120 }{ 121 { 122 Name: "AddCommand Success Case", 123 command: &bus.Command{ 124 ObjectMeta: metav1.ObjectMeta{ 125 Name: "Valid Command", 126 Namespace: namespace, 127 }, 128 }, 129 ExpectValue: 1, 130 }, 131 { 132 Name: "AddCommand Failure Case", 133 command: "Command", 134 ExpectValue: 0, 135 }, 136 } 137 138 for i, testcase := range testCases { 139 t.Run(testcase.Name, func(t *testing.T) { 140 controller := newController() 141 controller.addCommand(testcase.command) 142 len := controller.commandQueue.Len() 143 if testcase.ExpectValue != len { 144 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len) 145 } 146 }) 147 } 148 } 149 150 func TestJobAddFunc(t *testing.T) { 151 namespace := "test" 152 153 testCases := []struct { 154 Name string 155 job *batch.Job 156 ExpectValue int 157 }{ 158 { 159 Name: "AddJob Success", 160 job: &batch.Job{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Name: "Job1", 163 Namespace: namespace, 164 }, 165 }, 166 ExpectValue: 1, 167 }, 168 } 169 for i, testcase := range testCases { 170 t.Run(testcase.Name, func(t *testing.T) { 171 controller := newController() 172 controller.addJob(testcase.job) 173 key := fmt.Sprintf("%s/%s", testcase.job.Namespace, testcase.job.Name) 174 job, err := controller.cache.Get(key) 175 if job == nil || err != nil { 176 t.Errorf("Error while Adding Job in case %d with error %s", i, err) 177 } 178 queue := controller.getWorkerQueue(key) 179 len := queue.Len() 180 if testcase.ExpectValue != len { 181 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len) 182 } 183 }) 184 } 185 } 186 187 func TestUpdateJobFunc(t *testing.T) { 188 namespace := "test" 189 190 testcases := []struct { 191 Name string 192 oldJob *batch.Job 193 newJob *batch.Job 194 }{ 195 { 196 Name: "Job Update Success Case", 197 oldJob: &batch.Job{ 198 ObjectMeta: metav1.ObjectMeta{ 199 Name: "job1", 200 Namespace: namespace, 201 ResourceVersion: "54467984", 202 }, 203 Spec: batch.JobSpec{ 204 SchedulerName: "volcano", 205 MinAvailable: 5, 206 }, 207 Status: batch.JobStatus{ 208 State: batch.JobState{ 209 Phase: batch.Pending, 210 }, 211 }, 212 }, 213 newJob: &batch.Job{ 214 ObjectMeta: metav1.ObjectMeta{ 215 Name: "job1", 216 Namespace: namespace, 217 ResourceVersion: "54469999", 218 }, 219 Spec: batch.JobSpec{ 220 SchedulerName: "volcano", 221 MinAvailable: 5, 222 }, 223 Status: batch.JobStatus{ 224 State: batch.JobState{ 225 Phase: batch.Running, 226 }, 227 }, 228 }, 229 }, 230 { 231 Name: "Job Update Failure Case", 232 oldJob: &batch.Job{ 233 ObjectMeta: metav1.ObjectMeta{ 234 Name: "job1", 235 Namespace: namespace, 236 ResourceVersion: "54469999", 237 }, 238 Spec: batch.JobSpec{ 239 SchedulerName: "volcano", 240 MinAvailable: 5, 241 }, 242 Status: batch.JobStatus{ 243 State: batch.JobState{ 244 Phase: batch.Pending, 245 }, 246 }, 247 }, 248 newJob: &batch.Job{ 249 ObjectMeta: metav1.ObjectMeta{ 250 Name: "job1", 251 Namespace: namespace, 252 ResourceVersion: "54469999", 253 }, 254 Spec: batch.JobSpec{ 255 SchedulerName: "volcano", 256 MinAvailable: 5, 257 }, 258 Status: batch.JobStatus{ 259 State: batch.JobState{ 260 Phase: batch.Pending, 261 }, 262 }, 263 }, 264 }, 265 } 266 267 for i, testcase := range testcases { 268 t.Run(testcase.Name, func(t *testing.T) { 269 controller := newController() 270 controller.addJob(testcase.oldJob) 271 controller.updateJob(testcase.oldJob, testcase.newJob) 272 key := fmt.Sprintf("%s/%s", testcase.newJob.Namespace, testcase.newJob.Name) 273 job, err := controller.cache.Get(key) 274 275 if job == nil || job.Job == nil || err != nil { 276 t.Errorf("Error while Updating Job in case %d with error %s", i, err) 277 } 278 279 if job.Job.Status.State.Phase != testcase.newJob.Status.State.Phase { 280 t.Errorf("Error while Updating Job in case %d with error %s", i, err) 281 } 282 }) 283 } 284 } 285 286 func TestAddPodFunc(t *testing.T) { 287 namespace := "test" 288 289 testcases := []struct { 290 Name string 291 Job *batch.Job 292 pods []*v1.Pod 293 Annotation map[string]string 294 ExpectedValue int 295 }{ 296 { 297 Name: "AddPod Success case", 298 Job: &batch.Job{ 299 ObjectMeta: metav1.ObjectMeta{ 300 Name: "job1", 301 Namespace: namespace, 302 }, 303 }, 304 pods: []*v1.Pod{ 305 buildPod(namespace, "pod1", v1.PodPending, nil), 306 }, 307 Annotation: map[string]string{ 308 batch.JobNameKey: "job1", 309 batch.JobVersion: "0", 310 batch.TaskSpecKey: "task1", 311 }, 312 ExpectedValue: 1, 313 }, 314 { 315 Name: "AddPod Duplicate Pod case", 316 Job: &batch.Job{ 317 ObjectMeta: metav1.ObjectMeta{ 318 Name: "job1", 319 Namespace: namespace, 320 }, 321 }, 322 pods: []*v1.Pod{ 323 buildPod(namespace, "pod1", v1.PodPending, nil), 324 buildPod(namespace, "pod1", v1.PodPending, nil), 325 }, 326 Annotation: map[string]string{ 327 batch.JobNameKey: "job1", 328 batch.JobVersion: "0", 329 batch.TaskSpecKey: "task1", 330 }, 331 ExpectedValue: 1, 332 }, 333 } 334 335 for i, testcase := range testcases { 336 337 t.Run(testcase.Name, func(t *testing.T) { 338 controller := newController() 339 controller.addJob(testcase.Job) 340 for _, pod := range testcase.pods { 341 addPodAnnotation(pod, testcase.Annotation) 342 controller.addPod(pod) 343 } 344 345 key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name) 346 job, err := controller.cache.Get(key) 347 348 if job == nil || job.Pods == nil || err != nil { 349 t.Errorf("Error while Getting Job in case %d with error %s", i, err) 350 } 351 352 var totalPods int 353 for _, task := range job.Pods { 354 totalPods = len(task) 355 } 356 if totalPods != testcase.ExpectedValue { 357 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, totalPods) 358 } 359 }) 360 } 361 } 362 363 func TestUpdatePodFunc(t *testing.T) { 364 namespace := "test" 365 366 testcases := []struct { 367 Name string 368 Job *batch.Job 369 oldPod *v1.Pod 370 newPod *v1.Pod 371 Annotation map[string]string 372 ExpectedValue v1.PodPhase 373 }{ 374 { 375 Name: "UpdatePod Success case", 376 Job: &batch.Job{ 377 ObjectMeta: metav1.ObjectMeta{ 378 Name: "job1", 379 Namespace: namespace, 380 }, 381 }, 382 oldPod: buildPod(namespace, "pod1", v1.PodPending, nil), 383 newPod: buildPod(namespace, "pod1", v1.PodRunning, nil), 384 Annotation: map[string]string{ 385 batch.JobNameKey: "job1", 386 batch.JobVersion: "0", 387 batch.TaskSpecKey: "task1", 388 }, 389 ExpectedValue: v1.PodRunning, 390 }, 391 { 392 Name: "UpdatePod Failed case", 393 Job: &batch.Job{ 394 ObjectMeta: metav1.ObjectMeta{ 395 Name: "job1", 396 Namespace: namespace, 397 }, 398 }, 399 oldPod: buildPod(namespace, "pod1", v1.PodPending, nil), 400 newPod: buildPod(namespace, "pod1", v1.PodFailed, nil), 401 Annotation: map[string]string{ 402 batch.JobNameKey: "job1", 403 batch.JobVersion: "0", 404 batch.TaskSpecKey: "task1", 405 }, 406 ExpectedValue: v1.PodFailed, 407 }, 408 } 409 410 for i, testcase := range testcases { 411 t.Run(testcase.Name, func(t *testing.T) { 412 controller := newController() 413 controller.addJob(testcase.Job) 414 addPodAnnotation(testcase.oldPod, testcase.Annotation) 415 addPodAnnotation(testcase.newPod, testcase.Annotation) 416 controller.addPod(testcase.oldPod) 417 controller.updatePod(testcase.oldPod, testcase.newPod) 418 419 key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name) 420 job, err := controller.cache.Get(key) 421 422 if job == nil || job.Pods == nil || err != nil { 423 t.Errorf("Error while Getting Job in case %d with error %s", i, err) 424 } 425 426 pod := job.Pods[testcase.Annotation[batch.TaskSpecKey]][testcase.oldPod.Name] 427 428 if pod.Status.Phase != testcase.ExpectedValue { 429 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, pod.Status.Phase) 430 } 431 }) 432 } 433 } 434 435 func TestDeletePodFunc(t *testing.T) { 436 namespace := "test" 437 438 testcases := []struct { 439 Name string 440 Job *batch.Job 441 availablePods []*v1.Pod 442 deletePod *v1.Pod 443 Annotation map[string]string 444 ExpectedValue int 445 }{ 446 { 447 Name: "DeletePod success case", 448 Job: &batch.Job{ 449 ObjectMeta: metav1.ObjectMeta{ 450 Name: "job1", 451 Namespace: namespace, 452 }, 453 }, 454 availablePods: []*v1.Pod{ 455 buildPod(namespace, "pod1", v1.PodRunning, nil), 456 buildPod(namespace, "pod2", v1.PodRunning, nil), 457 }, 458 deletePod: buildPod(namespace, "pod2", v1.PodRunning, nil), 459 Annotation: map[string]string{ 460 batch.JobNameKey: "job1", 461 batch.JobVersion: "0", 462 batch.TaskSpecKey: "task1", 463 }, 464 ExpectedValue: 1, 465 }, 466 { 467 Name: "DeletePod Pod NotAvailable case", 468 Job: &batch.Job{ 469 ObjectMeta: metav1.ObjectMeta{ 470 Name: "job1", 471 Namespace: namespace, 472 }, 473 }, 474 availablePods: []*v1.Pod{ 475 buildPod(namespace, "pod1", v1.PodRunning, nil), 476 buildPod(namespace, "pod2", v1.PodRunning, nil), 477 }, 478 deletePod: buildPod(namespace, "pod3", v1.PodRunning, nil), 479 Annotation: map[string]string{ 480 batch.JobNameKey: "job1", 481 batch.JobVersion: "0", 482 batch.TaskSpecKey: "task1", 483 }, 484 ExpectedValue: 2, 485 }, 486 } 487 488 for i, testcase := range testcases { 489 t.Run(testcase.Name, func(t *testing.T) { 490 controller := newController() 491 controller.addJob(testcase.Job) 492 for _, pod := range testcase.availablePods { 493 addPodAnnotation(pod, testcase.Annotation) 494 controller.addPod(pod) 495 } 496 497 addPodAnnotation(testcase.deletePod, testcase.Annotation) 498 controller.deletePod(testcase.deletePod) 499 key := fmt.Sprintf("%s/%s", testcase.Job.Namespace, testcase.Job.Name) 500 job, err := controller.cache.Get(key) 501 502 if job == nil || job.Pods == nil || err != nil { 503 t.Errorf("Error while Getting Job in case %d with error %s", i, err) 504 } 505 506 var totalPods int 507 for _, task := range job.Pods { 508 totalPods = len(task) 509 } 510 511 if totalPods != testcase.ExpectedValue { 512 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectedValue, totalPods) 513 } 514 }) 515 } 516 } 517 518 func TestUpdatePodGroupFunc(t *testing.T) { 519 520 namespace := "test" 521 522 testCases := []struct { 523 Name string 524 oldPodGroup *scheduling.PodGroup 525 newPodGroup *scheduling.PodGroup 526 ExpectValue int 527 }{ 528 { 529 Name: "AddCommand Success Case", 530 oldPodGroup: &scheduling.PodGroup{ 531 ObjectMeta: metav1.ObjectMeta{ 532 Name: "pg1", 533 Namespace: namespace, 534 }, 535 Spec: scheduling.PodGroupSpec{ 536 MinMember: 3, 537 }, 538 Status: scheduling.PodGroupStatus{ 539 Phase: scheduling.PodGroupPending, 540 }, 541 }, 542 newPodGroup: &scheduling.PodGroup{ 543 ObjectMeta: metav1.ObjectMeta{ 544 Name: "pg1", 545 Namespace: namespace, 546 }, 547 Spec: scheduling.PodGroupSpec{ 548 MinMember: 3, 549 }, 550 Status: scheduling.PodGroupStatus{ 551 Phase: scheduling.PodGroupRunning, 552 }, 553 }, 554 ExpectValue: 1, 555 }, 556 } 557 558 for i, testcase := range testCases { 559 560 t.Run(testcase.Name, func(t *testing.T) { 561 controller := newController() 562 controller.updatePodGroup(testcase.oldPodGroup, testcase.newPodGroup) 563 key := fmt.Sprintf("%s/%s", testcase.oldPodGroup.Namespace, testcase.oldPodGroup.Name) 564 queue := controller.getWorkerQueue(key) 565 len := queue.Len() 566 if testcase.ExpectValue != len { 567 t.Errorf("case %d (%s): expected: %v, got %v ", i, testcase.Name, testcase.ExpectValue, len) 568 } 569 }) 570 } 571 }