volcano.sh/volcano@v1.9.0/pkg/controllers/job/job_state_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 "context" 21 "fmt" 22 "reflect" 23 "testing" 24 25 "github.com/agiledragon/gomonkey/v2" 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 29 "volcano.sh/apis/pkg/apis/batch/v1alpha1" 30 busv1alpha1 "volcano.sh/apis/pkg/apis/bus/v1alpha1" 31 schedulingapi "volcano.sh/apis/pkg/apis/scheduling/v1beta1" 32 "volcano.sh/volcano/pkg/controllers/apis" 33 "volcano.sh/volcano/pkg/controllers/job/state" 34 ) 35 36 func TestAbortedState_Execute(t *testing.T) { 37 namespace := "test" 38 39 testcases := []struct { 40 Name string 41 JobInfo *apis.JobInfo 42 Action busv1alpha1.Action 43 ExpectedVal error 44 }{ 45 { 46 Name: "AbortedState-ResumeAction case", 47 JobInfo: &apis.JobInfo{ 48 Namespace: namespace, 49 Name: "jobinfo1", 50 Job: &v1alpha1.Job{ 51 ObjectMeta: metav1.ObjectMeta{ 52 Name: "Job1", 53 Namespace: namespace, 54 ResourceVersion: "100", 55 }, 56 Status: v1alpha1.JobStatus{ 57 State: v1alpha1.JobState{ 58 Phase: v1alpha1.Aborted, 59 }, 60 }, 61 }, 62 }, 63 Action: busv1alpha1.ResumeJobAction, 64 ExpectedVal: nil, 65 }, 66 { 67 Name: "AbortedState-AnyOtherAction case", 68 JobInfo: &apis.JobInfo{ 69 Namespace: namespace, 70 Name: "jobinfo1", 71 Job: &v1alpha1.Job{ 72 ObjectMeta: metav1.ObjectMeta{ 73 Name: "Job1", 74 Namespace: namespace, 75 ResourceVersion: "100", 76 }, 77 Status: v1alpha1.JobStatus{ 78 State: v1alpha1.JobState{ 79 Phase: v1alpha1.Aborted, 80 }, 81 }, 82 }, 83 }, 84 Action: busv1alpha1.RestartJobAction, 85 ExpectedVal: nil, 86 }, 87 } 88 89 for _, testcase := range testcases { 90 t.Run(testcase.Name, func(t *testing.T) { 91 absState := state.NewState(testcase.JobInfo) 92 93 fakecontroller := newFakeController() 94 state.KillJob = fakecontroller.killJob 95 96 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 97 if err != nil { 98 t.Error("Error while creating Job") 99 } 100 101 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 102 if err != nil { 103 t.Error("Error while adding Job in cache") 104 } 105 106 err = absState.Execute(testcase.Action) 107 if err != nil { 108 t.Errorf("Expected Error not to occur but got: %s", err) 109 } 110 if testcase.Action == busv1alpha1.ResumeJobAction { 111 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 112 if err != nil { 113 t.Error("Error while retrieving value from Cache") 114 } 115 116 if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting { 117 t.Error("Expected Phase to be equal to restarting phase") 118 } 119 } 120 }) 121 } 122 } 123 124 func TestAbortingState_Execute(t *testing.T) { 125 namespace := "test" 126 127 testcases := []struct { 128 Name string 129 JobInfo *apis.JobInfo 130 Action busv1alpha1.Action 131 ExpectedVal error 132 }{ 133 { 134 Name: "AbortedState-ResumeAction case", 135 JobInfo: &apis.JobInfo{ 136 Namespace: namespace, 137 Name: "jobinfo1", 138 Job: &v1alpha1.Job{ 139 ObjectMeta: metav1.ObjectMeta{ 140 Name: "Job1", 141 Namespace: namespace, 142 ResourceVersion: "100", 143 }, 144 Status: v1alpha1.JobStatus{ 145 State: v1alpha1.JobState{ 146 Phase: v1alpha1.Aborting, 147 }, 148 }, 149 }, 150 }, 151 Action: busv1alpha1.ResumeJobAction, 152 ExpectedVal: nil, 153 }, 154 { 155 Name: "AbortedState-AnyOtherAction case with pods count equal to 0", 156 JobInfo: &apis.JobInfo{ 157 Namespace: namespace, 158 Name: "jobinfo1", 159 Job: &v1alpha1.Job{ 160 ObjectMeta: metav1.ObjectMeta{ 161 Name: "Job1", 162 Namespace: namespace, 163 ResourceVersion: "100", 164 }, 165 Status: v1alpha1.JobStatus{ 166 State: v1alpha1.JobState{ 167 Phase: v1alpha1.Aborting, 168 }, 169 }, 170 }, 171 }, 172 Action: busv1alpha1.RestartJobAction, 173 ExpectedVal: nil, 174 }, 175 { 176 Name: "AbortedState-AnyOtherAction case with Pods count not equal to 0", 177 JobInfo: &apis.JobInfo{ 178 Namespace: namespace, 179 Name: "jobinfo1", 180 Job: &v1alpha1.Job{ 181 ObjectMeta: metav1.ObjectMeta{ 182 Name: "Job1", 183 Namespace: namespace, 184 ResourceVersion: "100", 185 }, 186 Status: v1alpha1.JobStatus{ 187 Pending: 1, 188 State: v1alpha1.JobState{ 189 Phase: v1alpha1.Aborting, 190 }, 191 }, 192 }, 193 Pods: map[string]map[string]*v1.Pod{ 194 "task1": { 195 "pod1": buildPod(namespace, "pod1", v1.PodPending, nil), 196 }, 197 }, 198 }, 199 Action: busv1alpha1.RestartJobAction, 200 ExpectedVal: nil, 201 }, 202 } 203 204 for _, testcase := range testcases { 205 t.Run(testcase.Name, func(t *testing.T) { 206 absState := state.NewState(testcase.JobInfo) 207 208 fakecontroller := newFakeController() 209 state.KillJob = fakecontroller.killJob 210 211 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 212 if err != nil { 213 t.Error("Error while creating Job") 214 } 215 216 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 217 if err != nil { 218 t.Error("Error while adding Job in cache") 219 } 220 221 err = absState.Execute(testcase.Action) 222 if err != nil { 223 t.Errorf("Expected Error not to occur but got: %s", err) 224 } 225 if testcase.Action == busv1alpha1.ResumeJobAction { 226 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 227 if err != nil { 228 t.Error("Error while retrieving value from Cache") 229 } 230 231 if jobInfo.Job.Status.RetryCount == 0 { 232 t.Error("Retry Count should not be zero") 233 } 234 } 235 236 if testcase.Action != busv1alpha1.ResumeJobAction { 237 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 238 if err != nil { 239 t.Error("Error while retrieving value from Cache") 240 } 241 242 if testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Terminating == 0 { 243 if jobInfo.Job.Status.State.Phase != v1alpha1.Aborted { 244 t.Error("Phase Should be aborted") 245 } 246 } else { 247 if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting { 248 t.Error("Phase Should be aborted") 249 } 250 } 251 } 252 }) 253 } 254 } 255 256 func TestCompletingState_Execute(t *testing.T) { 257 258 namespace := "test" 259 260 testcases := []struct { 261 Name string 262 JobInfo *apis.JobInfo 263 Action busv1alpha1.Action 264 ExpectedVal error 265 }{ 266 { 267 Name: "CompletingState- With pod count not equal to zero", 268 JobInfo: &apis.JobInfo{ 269 Namespace: namespace, 270 Name: "jobinfo1", 271 Job: &v1alpha1.Job{ 272 ObjectMeta: metav1.ObjectMeta{ 273 Name: "Job1", 274 Namespace: namespace, 275 ResourceVersion: "100", 276 }, 277 Status: v1alpha1.JobStatus{ 278 Running: 2, 279 State: v1alpha1.JobState{ 280 Phase: v1alpha1.Completing, 281 }, 282 }, 283 }, 284 Pods: map[string]map[string]*v1.Pod{ 285 "task1": { 286 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 287 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 288 }, 289 }, 290 }, 291 Action: busv1alpha1.ResumeJobAction, 292 ExpectedVal: nil, 293 }, 294 { 295 Name: "CompletingState- With pod count equal to zero", 296 JobInfo: &apis.JobInfo{ 297 Namespace: namespace, 298 Name: "jobinfo1", 299 Job: &v1alpha1.Job{ 300 ObjectMeta: metav1.ObjectMeta{ 301 Name: "Job1", 302 Namespace: namespace, 303 ResourceVersion: "100", 304 }, 305 Status: v1alpha1.JobStatus{ 306 State: v1alpha1.JobState{ 307 Phase: v1alpha1.Completing, 308 }, 309 }, 310 }, 311 }, 312 Action: busv1alpha1.ResumeJobAction, 313 ExpectedVal: nil, 314 }, 315 } 316 317 for i, testcase := range testcases { 318 t.Run(testcase.Name, func(t *testing.T) { 319 testState := state.NewState(testcase.JobInfo) 320 321 fakecontroller := newFakeController() 322 state.KillJob = fakecontroller.killJob 323 324 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 325 if err != nil { 326 t.Error("Error while creating Job") 327 } 328 329 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 330 if err != nil { 331 t.Error("Error while adding Job in cache") 332 } 333 334 err = testState.Execute(testcase.Action) 335 if err != nil { 336 t.Errorf("Expected Error not to occur but got: %s", err) 337 } 338 339 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 340 if err != nil { 341 t.Error("Error while retrieving value from Cache") 342 } 343 344 if testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Terminating == 0 { 345 if jobInfo.Job.Status.State.Phase != v1alpha1.Completed { 346 fmt.Println(jobInfo.Job.Status.State.Phase) 347 t.Errorf("Expected Phase to be Completed State in test case: %d", i) 348 } 349 } else { 350 if jobInfo.Job.Status.State.Phase != v1alpha1.Completing { 351 t.Errorf("Expected Phase to be completing state in test case: %d", i) 352 } 353 } 354 }) 355 } 356 } 357 358 func TestFinishedState_Execute(t *testing.T) { 359 namespace := "test" 360 361 testcases := []struct { 362 Name string 363 JobInfo *apis.JobInfo 364 Action busv1alpha1.Action 365 ExpectedVal error 366 }{ 367 { 368 Name: "FinishedState Test Case", 369 JobInfo: &apis.JobInfo{ 370 Namespace: namespace, 371 Name: "jobinfo1", 372 Job: &v1alpha1.Job{ 373 ObjectMeta: metav1.ObjectMeta{ 374 Name: "Job1", 375 Namespace: namespace, 376 ResourceVersion: "100", 377 }, 378 Status: v1alpha1.JobStatus{ 379 State: v1alpha1.JobState{ 380 Phase: v1alpha1.Completed, 381 }, 382 }, 383 }, 384 }, 385 Action: busv1alpha1.ResumeJobAction, 386 ExpectedVal: nil, 387 }, 388 } 389 390 for _, testcase := range testcases { 391 t.Run(testcase.Name, func(t *testing.T) { 392 testState := state.NewState(testcase.JobInfo) 393 394 fakecontroller := newFakeController() 395 state.KillJob = fakecontroller.killJob 396 397 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 398 if err != nil { 399 t.Error("Error while creating Job") 400 } 401 402 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 403 if err != nil { 404 t.Error("Error while adding Job in cache") 405 } 406 407 err = testState.Execute(testcase.Action) 408 if err != nil { 409 t.Errorf("Expected Error not to occur but got: %s", err) 410 } 411 }) 412 } 413 } 414 415 func TestPendingState_Execute(t *testing.T) { 416 namespace := "test" 417 418 testcases := []struct { 419 Name string 420 JobInfo *apis.JobInfo 421 Action busv1alpha1.Action 422 ExpectedVal error 423 }{ 424 { 425 Name: "PendingState- RestartJobAction case With terminating pod count equal to zero", 426 JobInfo: &apis.JobInfo{ 427 Namespace: namespace, 428 Name: "jobinfo1", 429 Job: &v1alpha1.Job{ 430 ObjectMeta: metav1.ObjectMeta{ 431 Name: "Job1", 432 Namespace: namespace, 433 ResourceVersion: "100", 434 }, 435 Status: v1alpha1.JobStatus{ 436 State: v1alpha1.JobState{ 437 Phase: v1alpha1.Pending, 438 }, 439 }, 440 }, 441 }, 442 Action: busv1alpha1.RestartJobAction, 443 ExpectedVal: nil, 444 }, 445 { 446 Name: "PendingState- RestartJobAction case With terminating pod count not equal to zero", 447 JobInfo: &apis.JobInfo{ 448 Namespace: namespace, 449 Name: "jobinfo1", 450 Job: &v1alpha1.Job{ 451 ObjectMeta: metav1.ObjectMeta{ 452 Name: "Job1", 453 Namespace: namespace, 454 ResourceVersion: "100", 455 }, 456 Status: v1alpha1.JobStatus{ 457 Terminating: 2, 458 State: v1alpha1.JobState{ 459 Phase: v1alpha1.Pending, 460 }, 461 }, 462 }, 463 Pods: map[string]map[string]*v1.Pod{ 464 "task1": { 465 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 466 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 467 }, 468 }, 469 }, 470 Action: busv1alpha1.RestartJobAction, 471 ExpectedVal: nil, 472 }, 473 { 474 Name: "PendingState- AbortJobAction case With terminating pod count equal to zero", 475 JobInfo: &apis.JobInfo{ 476 Namespace: namespace, 477 Name: "jobinfo1", 478 Job: &v1alpha1.Job{ 479 ObjectMeta: metav1.ObjectMeta{ 480 Name: "Job1", 481 Namespace: namespace, 482 ResourceVersion: "100", 483 }, 484 Status: v1alpha1.JobStatus{ 485 State: v1alpha1.JobState{ 486 Phase: v1alpha1.Pending, 487 }, 488 }, 489 }, 490 }, 491 Action: busv1alpha1.AbortJobAction, 492 ExpectedVal: nil, 493 }, 494 { 495 Name: "PendingState- AbortJobAction case With terminating pod count not equal to zero", 496 JobInfo: &apis.JobInfo{ 497 Namespace: namespace, 498 Name: "jobinfo1", 499 Job: &v1alpha1.Job{ 500 ObjectMeta: metav1.ObjectMeta{ 501 Name: "Job1", 502 Namespace: namespace, 503 ResourceVersion: "100", 504 }, 505 Status: v1alpha1.JobStatus{ 506 Terminating: 2, 507 State: v1alpha1.JobState{ 508 Phase: v1alpha1.Pending, 509 }, 510 }, 511 }, 512 Pods: map[string]map[string]*v1.Pod{ 513 "task1": { 514 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 515 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 516 }, 517 }, 518 }, 519 Action: busv1alpha1.AbortJobAction, 520 ExpectedVal: nil, 521 }, 522 { 523 Name: "PendingState- TerminateJobAction case With terminating pod count not equal to zero", 524 JobInfo: &apis.JobInfo{ 525 Namespace: namespace, 526 Name: "jobinfo1", 527 Job: &v1alpha1.Job{ 528 ObjectMeta: metav1.ObjectMeta{ 529 Name: "Job1", 530 Namespace: namespace, 531 ResourceVersion: "100", 532 }, 533 Status: v1alpha1.JobStatus{ 534 Terminating: 2, 535 State: v1alpha1.JobState{ 536 Phase: v1alpha1.Pending, 537 }, 538 }, 539 }, 540 Pods: map[string]map[string]*v1.Pod{ 541 "task1": { 542 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 543 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 544 }, 545 }, 546 }, 547 Action: busv1alpha1.TerminateJobAction, 548 ExpectedVal: nil, 549 }, 550 { 551 Name: "PendingState- CompleteJobAction case With terminating pod count equal to zero", 552 JobInfo: &apis.JobInfo{ 553 Namespace: namespace, 554 Name: "jobinfo1", 555 Job: &v1alpha1.Job{ 556 ObjectMeta: metav1.ObjectMeta{ 557 Name: "Job1", 558 Namespace: namespace, 559 ResourceVersion: "100", 560 }, 561 Status: v1alpha1.JobStatus{ 562 State: v1alpha1.JobState{ 563 Phase: v1alpha1.Pending, 564 }, 565 }, 566 }, 567 }, 568 Action: busv1alpha1.CompleteJobAction, 569 ExpectedVal: nil, 570 }, 571 { 572 Name: "PendingState- CompleteJobAction case With terminating pod count not equal to zero", 573 JobInfo: &apis.JobInfo{ 574 Namespace: namespace, 575 Name: "jobinfo1", 576 Job: &v1alpha1.Job{ 577 ObjectMeta: metav1.ObjectMeta{ 578 Name: "Job1", 579 Namespace: namespace, 580 ResourceVersion: "100", 581 }, 582 Status: v1alpha1.JobStatus{ 583 Terminating: 2, 584 State: v1alpha1.JobState{ 585 Phase: v1alpha1.Pending, 586 }, 587 }, 588 }, 589 Pods: map[string]map[string]*v1.Pod{ 590 "task1": { 591 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 592 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 593 }, 594 }, 595 }, 596 Action: busv1alpha1.CompleteJobAction, 597 ExpectedVal: nil, 598 }, 599 { 600 Name: "PendingState- EnqueueAction case With Min Available equal to running pods", 601 JobInfo: &apis.JobInfo{ 602 Namespace: namespace, 603 Name: "jobinfo1", 604 Job: &v1alpha1.Job{ 605 ObjectMeta: metav1.ObjectMeta{ 606 Name: "Job1", 607 Namespace: namespace, 608 ResourceVersion: "100", 609 }, 610 Spec: v1alpha1.JobSpec{ 611 MinAvailable: 3, 612 }, 613 Status: v1alpha1.JobStatus{ 614 Running: 3, 615 State: v1alpha1.JobState{ 616 Phase: v1alpha1.Pending, 617 }, 618 }, 619 }, 620 Pods: map[string]map[string]*v1.Pod{ 621 "task1": { 622 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 623 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 624 "pod3": buildPod(namespace, "pod3", v1.PodRunning, nil), 625 }, 626 }, 627 }, 628 Action: busv1alpha1.EnqueueAction, 629 ExpectedVal: nil, 630 }, 631 { 632 Name: "PendingState- EnqueueAction case With Min Available not equal to running pods", 633 JobInfo: &apis.JobInfo{ 634 Namespace: namespace, 635 Name: "jobinfo1", 636 Job: &v1alpha1.Job{ 637 ObjectMeta: metav1.ObjectMeta{ 638 Name: "Job1", 639 Namespace: namespace, 640 ResourceVersion: "100", 641 }, 642 Spec: v1alpha1.JobSpec{ 643 MinAvailable: 3, 644 }, 645 Status: v1alpha1.JobStatus{ 646 Running: 2, 647 State: v1alpha1.JobState{ 648 Phase: v1alpha1.Pending, 649 }, 650 }, 651 }, 652 Pods: map[string]map[string]*v1.Pod{ 653 "task1": { 654 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 655 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 656 }, 657 }, 658 }, 659 Action: busv1alpha1.EnqueueAction, 660 ExpectedVal: nil, 661 }, 662 { 663 Name: "PendingState- Default case With Min Available equal to running pods", 664 JobInfo: &apis.JobInfo{ 665 Namespace: namespace, 666 Name: "jobinfo1", 667 Job: &v1alpha1.Job{ 668 ObjectMeta: metav1.ObjectMeta{ 669 Name: "Job1", 670 Namespace: namespace, 671 ResourceVersion: "100", 672 }, 673 Spec: v1alpha1.JobSpec{ 674 MinAvailable: 3, 675 }, 676 Status: v1alpha1.JobStatus{ 677 Running: 2, 678 State: v1alpha1.JobState{ 679 Phase: v1alpha1.Pending, 680 }, 681 }, 682 }, 683 Pods: map[string]map[string]*v1.Pod{ 684 "task1": { 685 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 686 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 687 }, 688 }, 689 }, 690 Action: busv1alpha1.SyncJobAction, 691 ExpectedVal: nil, 692 }, 693 } 694 695 for i, testcase := range testcases { 696 t.Run(testcase.Name, func(t *testing.T) { 697 testState := state.NewState(testcase.JobInfo) 698 699 fakecontroller := newFakeController() 700 state.KillJob = fakecontroller.killJob 701 702 patches := gomonkey.ApplyMethod(reflect.TypeOf(fakecontroller), "GetQueueInfo", func(_ *jobcontroller, _ string) (*schedulingapi.Queue, error) { 703 return &schedulingapi.Queue{}, nil 704 }) 705 706 defer patches.Reset() 707 708 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 709 if err != nil { 710 t.Error("Error while creating Job") 711 } 712 713 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 714 if err != nil { 715 t.Error("Error while adding Job in cache") 716 } 717 718 err = testState.Execute(testcase.Action) 719 if err != nil { 720 t.Errorf("Expected Error not to occur but got: %s", err) 721 } 722 723 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 724 if err != nil { 725 t.Error("Error while retrieving value from Cache") 726 } 727 728 if testcase.Action == busv1alpha1.RestartJobAction { 729 // always jump to restarting firstly 730 if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting { 731 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i) 732 } 733 } else if testcase.Action == busv1alpha1.AbortJobAction { 734 // always jump to aborting firstly 735 if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting { 736 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Aborting, jobInfo.Job.Status.State.Phase, i) 737 } 738 } else if testcase.Action == busv1alpha1.TerminateJobAction { 739 // always jump to completing firstly 740 if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating { 741 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Terminating, jobInfo.Job.Status.State.Phase, i) 742 } 743 } else if testcase.Action == busv1alpha1.CompleteJobAction { 744 // always jump to completing firstly 745 if jobInfo.Job.Status.State.Phase != v1alpha1.Completing { 746 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Completing, jobInfo.Job.Status.State.Phase, i) 747 } 748 } else if testcase.Action == busv1alpha1.EnqueueAction { 749 if jobInfo.Job.Spec.MinAvailable <= jobInfo.Job.Status.Running+jobInfo.Job.Status.Succeeded+jobInfo.Job.Status.Failed { 750 if jobInfo.Job.Status.State.Phase != v1alpha1.Running { 751 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Running, jobInfo.Job.Status.State.Phase, i) 752 } 753 } 754 } else { 755 if jobInfo.Job.Status.State.Phase != v1alpha1.Pending { 756 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i) 757 } 758 } 759 }) 760 } 761 } 762 763 func TestRestartingState_Execute(t *testing.T) { 764 namespace := "test" 765 766 testcases := []struct { 767 Name string 768 JobInfo *apis.JobInfo 769 Action busv1alpha1.Action 770 ExpectedVal error 771 }{ 772 { 773 Name: "RestartingState- RetryCount is equal to or greater than MaxRetry", 774 JobInfo: &apis.JobInfo{ 775 Namespace: namespace, 776 Name: "jobinfo1", 777 Job: &v1alpha1.Job{ 778 ObjectMeta: metav1.ObjectMeta{ 779 Name: "Job1", 780 Namespace: namespace, 781 ResourceVersion: "100", 782 }, 783 Spec: v1alpha1.JobSpec{ 784 MaxRetry: 3, 785 }, 786 Status: v1alpha1.JobStatus{ 787 RetryCount: 3, 788 State: v1alpha1.JobState{ 789 Phase: v1alpha1.Restarting, 790 }, 791 }, 792 }, 793 }, 794 Action: busv1alpha1.RestartJobAction, 795 ExpectedVal: nil, 796 }, 797 { 798 Name: "RestartingState- RetryCount is less than MaxRetry", 799 JobInfo: &apis.JobInfo{ 800 Namespace: namespace, 801 Name: "jobinfo1", 802 Job: &v1alpha1.Job{ 803 ObjectMeta: metav1.ObjectMeta{ 804 Name: "Job1", 805 Namespace: namespace, 806 ResourceVersion: "100", 807 }, 808 Spec: v1alpha1.JobSpec{ 809 MaxRetry: 3, 810 Tasks: []v1alpha1.TaskSpec{ 811 { 812 Name: "task1", 813 Replicas: 1, 814 }, 815 { 816 Name: "task2", 817 Replicas: 1, 818 }, 819 }, 820 }, 821 Status: v1alpha1.JobStatus{ 822 RetryCount: 1, 823 MinAvailable: 1, 824 Terminating: 0, 825 State: v1alpha1.JobState{ 826 Phase: v1alpha1.Restarting, 827 }, 828 }, 829 }, 830 }, 831 Action: busv1alpha1.RestartJobAction, 832 ExpectedVal: nil, 833 }, 834 } 835 836 for i, testcase := range testcases { 837 t.Run(testcase.Name, func(t *testing.T) { 838 testState := state.NewState(testcase.JobInfo) 839 840 fakecontroller := newFakeController() 841 state.KillJob = fakecontroller.killJob 842 843 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 844 if err != nil { 845 t.Error("Error while creating Job") 846 } 847 848 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 849 if err != nil { 850 t.Error("Error while adding Job in cache") 851 } 852 853 err = testState.Execute(testcase.Action) 854 if err != nil { 855 t.Errorf("Expected Error not to occur but got: %s", err) 856 } 857 858 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 859 if err != nil { 860 t.Error("Error while retrieving value from Cache") 861 } 862 863 if testcase.JobInfo.Job.Spec.MaxRetry <= testcase.JobInfo.Job.Status.RetryCount { 864 if jobInfo.Job.Status.State.Phase != v1alpha1.Failed { 865 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Failed, jobInfo.Job.Status.State.Phase, i) 866 } 867 } else { 868 if jobInfo.Job.Status.State.Phase != v1alpha1.Pending { 869 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i) 870 } 871 } 872 }) 873 } 874 } 875 876 func TestRunningState_Execute(t *testing.T) { 877 namespace := "test" 878 879 testcases := []struct { 880 Name string 881 JobInfo *apis.JobInfo 882 Action busv1alpha1.Action 883 ExpectedVal error 884 }{ 885 { 886 Name: "RunningState- RestartJobAction case and Terminating Pods not equal to 0", 887 JobInfo: &apis.JobInfo{ 888 Namespace: namespace, 889 Name: "jobinfo1", 890 Job: &v1alpha1.Job{ 891 ObjectMeta: metav1.ObjectMeta{ 892 Name: "Job1", 893 Namespace: namespace, 894 ResourceVersion: "100", 895 }, 896 Spec: v1alpha1.JobSpec{}, 897 Status: v1alpha1.JobStatus{ 898 Terminating: 2, 899 State: v1alpha1.JobState{ 900 Phase: v1alpha1.Running, 901 }, 902 }, 903 }, 904 Pods: map[string]map[string]*v1.Pod{ 905 "task1": { 906 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 907 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 908 }, 909 }, 910 }, 911 Action: busv1alpha1.RestartJobAction, 912 ExpectedVal: nil, 913 }, 914 { 915 Name: "RunningState- RestartJobAction case and Terminating Pods equal to 0", 916 JobInfo: &apis.JobInfo{ 917 Namespace: namespace, 918 Name: "jobinfo1", 919 Job: &v1alpha1.Job{ 920 ObjectMeta: metav1.ObjectMeta{ 921 Name: "Job1", 922 Namespace: namespace, 923 ResourceVersion: "100", 924 }, 925 Spec: v1alpha1.JobSpec{}, 926 Status: v1alpha1.JobStatus{ 927 Terminating: 0, 928 State: v1alpha1.JobState{ 929 Phase: v1alpha1.Running, 930 }, 931 }, 932 }, 933 }, 934 Action: busv1alpha1.RestartJobAction, 935 ExpectedVal: nil, 936 }, 937 { 938 Name: "RunningState- AbortAction case and Terminating Pods equal to 0", 939 JobInfo: &apis.JobInfo{ 940 Namespace: namespace, 941 Name: "jobinfo1", 942 Job: &v1alpha1.Job{ 943 ObjectMeta: metav1.ObjectMeta{ 944 Name: "Job1", 945 Namespace: namespace, 946 ResourceVersion: "100", 947 }, 948 Spec: v1alpha1.JobSpec{}, 949 Status: v1alpha1.JobStatus{ 950 Terminating: 0, 951 State: v1alpha1.JobState{ 952 Phase: v1alpha1.Running, 953 }, 954 }, 955 }, 956 }, 957 Action: busv1alpha1.AbortJobAction, 958 ExpectedVal: nil, 959 }, 960 { 961 Name: "RunningState- AbortAction case and Terminating Pods not equal to 0", 962 JobInfo: &apis.JobInfo{ 963 Namespace: namespace, 964 Name: "jobinfo1", 965 Job: &v1alpha1.Job{ 966 ObjectMeta: metav1.ObjectMeta{ 967 Name: "Job1", 968 Namespace: namespace, 969 ResourceVersion: "100", 970 }, 971 Spec: v1alpha1.JobSpec{}, 972 Status: v1alpha1.JobStatus{ 973 Terminating: 2, 974 State: v1alpha1.JobState{ 975 Phase: v1alpha1.Running, 976 }, 977 }, 978 }, 979 Pods: map[string]map[string]*v1.Pod{ 980 "task1": { 981 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 982 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 983 }, 984 }, 985 }, 986 Action: busv1alpha1.AbortJobAction, 987 ExpectedVal: nil, 988 }, 989 { 990 Name: "RunningState- TerminateJobAction case and Terminating Pods equal to 0", 991 JobInfo: &apis.JobInfo{ 992 Namespace: namespace, 993 Name: "jobinfo1", 994 Job: &v1alpha1.Job{ 995 ObjectMeta: metav1.ObjectMeta{ 996 Name: "Job1", 997 Namespace: namespace, 998 ResourceVersion: "100", 999 }, 1000 Spec: v1alpha1.JobSpec{}, 1001 Status: v1alpha1.JobStatus{ 1002 Terminating: 0, 1003 State: v1alpha1.JobState{ 1004 Phase: v1alpha1.Running, 1005 }, 1006 }, 1007 }, 1008 }, 1009 Action: busv1alpha1.TerminateJobAction, 1010 ExpectedVal: nil, 1011 }, 1012 { 1013 Name: "RunningState- TerminateJobAction case and Terminating Pods not equal to 0", 1014 JobInfo: &apis.JobInfo{ 1015 Namespace: namespace, 1016 Name: "jobinfo1", 1017 Job: &v1alpha1.Job{ 1018 ObjectMeta: metav1.ObjectMeta{ 1019 Name: "Job1", 1020 Namespace: namespace, 1021 ResourceVersion: "100", 1022 }, 1023 Spec: v1alpha1.JobSpec{}, 1024 Status: v1alpha1.JobStatus{ 1025 Terminating: 2, 1026 State: v1alpha1.JobState{ 1027 Phase: v1alpha1.Running, 1028 }, 1029 }, 1030 }, 1031 Pods: map[string]map[string]*v1.Pod{ 1032 "task1": { 1033 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 1034 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 1035 }, 1036 }, 1037 }, 1038 Action: busv1alpha1.TerminateJobAction, 1039 ExpectedVal: nil, 1040 }, 1041 { 1042 Name: "RunningState- CompleteJobAction case and Terminating Pods equal to 0", 1043 JobInfo: &apis.JobInfo{ 1044 Namespace: namespace, 1045 Name: "jobinfo1", 1046 Job: &v1alpha1.Job{ 1047 ObjectMeta: metav1.ObjectMeta{ 1048 Name: "Job1", 1049 Namespace: namespace, 1050 ResourceVersion: "100", 1051 }, 1052 Spec: v1alpha1.JobSpec{}, 1053 Status: v1alpha1.JobStatus{ 1054 Terminating: 0, 1055 State: v1alpha1.JobState{ 1056 Phase: v1alpha1.Running, 1057 }, 1058 }, 1059 }, 1060 }, 1061 Action: busv1alpha1.CompleteJobAction, 1062 ExpectedVal: nil, 1063 }, 1064 { 1065 Name: "RunningState- CompleteJobAction case and Terminating Pods not equal to 0", 1066 JobInfo: &apis.JobInfo{ 1067 Namespace: namespace, 1068 Name: "jobinfo1", 1069 Job: &v1alpha1.Job{ 1070 ObjectMeta: metav1.ObjectMeta{ 1071 Name: "Job1", 1072 Namespace: namespace, 1073 ResourceVersion: "100", 1074 }, 1075 Spec: v1alpha1.JobSpec{}, 1076 Status: v1alpha1.JobStatus{ 1077 Terminating: 2, 1078 State: v1alpha1.JobState{ 1079 Phase: v1alpha1.Running, 1080 }, 1081 }, 1082 }, 1083 Pods: map[string]map[string]*v1.Pod{ 1084 "task1": { 1085 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 1086 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 1087 }, 1088 }, 1089 }, 1090 Action: busv1alpha1.CompleteJobAction, 1091 ExpectedVal: nil, 1092 }, 1093 { 1094 Name: "RunningState- Default case and Total is equal to failed+succeeded", 1095 JobInfo: &apis.JobInfo{ 1096 Namespace: namespace, 1097 Name: "jobinfo1", 1098 Job: &v1alpha1.Job{ 1099 ObjectMeta: metav1.ObjectMeta{ 1100 Name: "job1", 1101 Namespace: namespace, 1102 ResourceVersion: "100", 1103 }, 1104 Spec: v1alpha1.JobSpec{ 1105 Tasks: []v1alpha1.TaskSpec{ 1106 { 1107 Name: "task1", 1108 Replicas: 2, 1109 Template: v1.PodTemplateSpec{ 1110 ObjectMeta: metav1.ObjectMeta{ 1111 Name: "task1", 1112 }, 1113 }, 1114 }, 1115 }, 1116 }, 1117 Status: v1alpha1.JobStatus{ 1118 Succeeded: 2, 1119 State: v1alpha1.JobState{ 1120 Phase: v1alpha1.Running, 1121 }, 1122 }, 1123 }, 1124 Pods: map[string]map[string]*v1.Pod{ 1125 "task1": { 1126 "job1-task1-0": buildPod(namespace, "pod1", v1.PodSucceeded, nil), 1127 "job1-task1-1": buildPod(namespace, "pod2", v1.PodSucceeded, nil), 1128 }, 1129 }, 1130 }, 1131 Action: busv1alpha1.SyncJobAction, 1132 ExpectedVal: nil, 1133 }, 1134 { 1135 Name: "RunningState- Default case and Total is not equal to failed+succeeded", 1136 JobInfo: &apis.JobInfo{ 1137 Namespace: namespace, 1138 Name: "jobinfo1", 1139 Job: &v1alpha1.Job{ 1140 ObjectMeta: metav1.ObjectMeta{ 1141 Name: "job1", 1142 Namespace: namespace, 1143 ResourceVersion: "100", 1144 }, 1145 Spec: v1alpha1.JobSpec{ 1146 Tasks: []v1alpha1.TaskSpec{ 1147 { 1148 Name: "task1", 1149 Replicas: 2, 1150 Template: v1.PodTemplateSpec{ 1151 ObjectMeta: metav1.ObjectMeta{ 1152 Name: "task1", 1153 }, 1154 }, 1155 }, 1156 }, 1157 }, 1158 Status: v1alpha1.JobStatus{ 1159 Succeeded: 1, 1160 State: v1alpha1.JobState{ 1161 Phase: v1alpha1.Running, 1162 }, 1163 }, 1164 }, 1165 Pods: map[string]map[string]*v1.Pod{ 1166 "task1": { 1167 "job1-task1-0": buildPod(namespace, "pod1", v1.PodSucceeded, nil), 1168 }, 1169 }, 1170 }, 1171 Action: busv1alpha1.SyncJobAction, 1172 ExpectedVal: nil, 1173 }, 1174 { 1175 Name: "RunningState- Default case and running back to pending When pending equal to total", 1176 JobInfo: &apis.JobInfo{ 1177 Namespace: namespace, 1178 Name: "jobinfo1", 1179 Job: &v1alpha1.Job{ 1180 ObjectMeta: metav1.ObjectMeta{ 1181 Name: "job1", 1182 Namespace: namespace, 1183 ResourceVersion: "100", 1184 }, 1185 Spec: v1alpha1.JobSpec{ 1186 MinAvailable: 3, 1187 Tasks: []v1alpha1.TaskSpec{ 1188 { 1189 Name: "task1", 1190 Replicas: 5, 1191 Template: v1.PodTemplateSpec{ 1192 ObjectMeta: metav1.ObjectMeta{ 1193 Name: "task1", 1194 }, 1195 }, 1196 }, 1197 }, 1198 }, 1199 Status: v1alpha1.JobStatus{ 1200 Pending: 5, 1201 State: v1alpha1.JobState{ 1202 Phase: v1alpha1.Running, 1203 }, 1204 }, 1205 }, 1206 Pods: map[string]map[string]*v1.Pod{ 1207 "task1": { 1208 "job1-task1-0": buildPod(namespace, "pod1", v1.PodPending, nil), 1209 "job1-task1-1": buildPod(namespace, "pod2", v1.PodPending, nil), 1210 "job1-task1-2": buildPod(namespace, "pod3", v1.PodPending, nil), 1211 "job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil), 1212 "job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil), 1213 }, 1214 }, 1215 }, 1216 Action: busv1alpha1.SyncJobAction, 1217 ExpectedVal: nil, 1218 }, 1219 { 1220 Name: "RunningState- Default case and running back to pending When pods status pending>(total-minAvailable)", 1221 JobInfo: &apis.JobInfo{ 1222 Namespace: namespace, 1223 Name: "jobinfo1", 1224 Job: &v1alpha1.Job{ 1225 ObjectMeta: metav1.ObjectMeta{ 1226 Name: "job1", 1227 Namespace: namespace, 1228 ResourceVersion: "100", 1229 }, 1230 Spec: v1alpha1.JobSpec{ 1231 MinAvailable: 3, 1232 Tasks: []v1alpha1.TaskSpec{ 1233 { 1234 Name: "task1", 1235 Replicas: 5, 1236 Template: v1.PodTemplateSpec{ 1237 ObjectMeta: metav1.ObjectMeta{ 1238 Name: "task1", 1239 }, 1240 }, 1241 }, 1242 }, 1243 }, 1244 Status: v1alpha1.JobStatus{ 1245 Pending: 3, 1246 Running: 2, 1247 State: v1alpha1.JobState{ 1248 Phase: v1alpha1.Running, 1249 }, 1250 }, 1251 }, 1252 Pods: map[string]map[string]*v1.Pod{ 1253 "task1": { 1254 "job1-task1-0": buildPod(namespace, "pod1", v1.PodRunning, nil), 1255 "job1-task1-1": buildPod(namespace, "pod2", v1.PodRunning, nil), 1256 "job1-task1-2": buildPod(namespace, "pod3", v1.PodPending, nil), 1257 "job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil), 1258 "job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil), 1259 }, 1260 }, 1261 }, 1262 Action: busv1alpha1.SyncJobAction, 1263 ExpectedVal: nil, 1264 }, 1265 { 1266 Name: "RunningState- Default case and keep running When pods status pending<=(total-minAvailable)", 1267 JobInfo: &apis.JobInfo{ 1268 Namespace: namespace, 1269 Name: "jobinfo1", 1270 Job: &v1alpha1.Job{ 1271 ObjectMeta: metav1.ObjectMeta{ 1272 Name: "job1", 1273 Namespace: namespace, 1274 ResourceVersion: "100", 1275 }, 1276 Spec: v1alpha1.JobSpec{ 1277 MinAvailable: 3, 1278 Tasks: []v1alpha1.TaskSpec{ 1279 { 1280 Name: "task1", 1281 Replicas: 5, 1282 Template: v1.PodTemplateSpec{ 1283 ObjectMeta: metav1.ObjectMeta{ 1284 Name: "task1", 1285 }, 1286 }, 1287 }, 1288 }, 1289 }, 1290 Status: v1alpha1.JobStatus{ 1291 Pending: 2, 1292 Running: 1, 1293 Succeeded: 1, 1294 Failed: 1, 1295 State: v1alpha1.JobState{ 1296 Phase: v1alpha1.Running, 1297 }, 1298 }, 1299 }, 1300 Pods: map[string]map[string]*v1.Pod{ 1301 "task1": { 1302 "job1-task1-0": buildPod(namespace, "pod1", v1.PodRunning, nil), 1303 "job1-task1-1": buildPod(namespace, "pod2", v1.PodSucceeded, nil), 1304 "job1-task1-2": buildPod(namespace, "pod3", v1.PodFailed, nil), 1305 "job1-task1-3": buildPod(namespace, "pod4", v1.PodPending, nil), 1306 "job1-task1-4": buildPod(namespace, "pod5", v1.PodPending, nil), 1307 }, 1308 }, 1309 }, 1310 Action: busv1alpha1.SyncJobAction, 1311 ExpectedVal: nil, 1312 }, 1313 } 1314 1315 for i, testcase := range testcases { 1316 t.Run(testcase.Name, func(t *testing.T) { 1317 testState := state.NewState(testcase.JobInfo) 1318 1319 fakecontroller := newFakeController() 1320 state.KillJob = fakecontroller.killJob 1321 1322 patches := gomonkey.ApplyMethod(reflect.TypeOf(fakecontroller), "GetQueueInfo", func(_ *jobcontroller, _ string) (*schedulingapi.Queue, error) { 1323 return &schedulingapi.Queue{}, nil 1324 }) 1325 1326 defer patches.Reset() 1327 1328 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 1329 if err != nil { 1330 t.Error("Error while creating Job") 1331 } 1332 1333 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 1334 if err != nil { 1335 t.Error("Error while adding Job in cache") 1336 } 1337 1338 err = testState.Execute(testcase.Action) 1339 if err != nil { 1340 t.Errorf("Expected Error not to occur but got: %s", err) 1341 } 1342 1343 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 1344 if err != nil { 1345 t.Error("Error while retrieving value from Cache") 1346 } 1347 1348 if testcase.Action == busv1alpha1.RestartJobAction { 1349 // always jump to restarting firstly 1350 if jobInfo.Job.Status.State.Phase != v1alpha1.Restarting { 1351 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i) 1352 } 1353 } else if testcase.Action == busv1alpha1.AbortJobAction { 1354 // always jump to aborting firstly 1355 if jobInfo.Job.Status.State.Phase != v1alpha1.Aborting { 1356 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i) 1357 } 1358 } else if testcase.Action == busv1alpha1.TerminateJobAction { 1359 // always jump to terminating firstly 1360 if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating { 1361 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Terminating, jobInfo.Job.Status.State.Phase, i) 1362 } 1363 } else if testcase.Action == busv1alpha1.CompleteJobAction { 1364 // always jump to completing firstly 1365 if jobInfo.Job.Status.State.Phase != v1alpha1.Completing { 1366 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Restarting, jobInfo.Job.Status.State.Phase, i) 1367 } 1368 } else { 1369 total := state.TotalTasks(testcase.JobInfo.Job) 1370 if total == testcase.JobInfo.Job.Status.Succeeded+testcase.JobInfo.Job.Status.Failed { 1371 if jobInfo.Job.Status.State.Phase != v1alpha1.Completed { 1372 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Completed, jobInfo.Job.Status.State.Phase, i) 1373 } 1374 } else if testcase.JobInfo.Job.Status.Pending > total-testcase.JobInfo.Job.Spec.MinAvailable { 1375 if jobInfo.Job.Status.State.Phase != v1alpha1.Pending { 1376 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Pending, jobInfo.Job.Status.State.Phase, i) 1377 } 1378 } else { 1379 if jobInfo.Job.Status.State.Phase != v1alpha1.Running { 1380 t.Errorf("Expected Job phase to %s, but got %s in case %d", v1alpha1.Running, jobInfo.Job.Status.State.Phase, i) 1381 } 1382 } 1383 } 1384 }) 1385 } 1386 } 1387 1388 func TestTerminatingState_Execute(t *testing.T) { 1389 namespace := "test" 1390 1391 testcases := []struct { 1392 Name string 1393 JobInfo *apis.JobInfo 1394 Action busv1alpha1.Action 1395 ExpectedVal error 1396 }{ 1397 { 1398 Name: "TerminatingState- With pod count not equal to zero", 1399 JobInfo: &apis.JobInfo{ 1400 Namespace: namespace, 1401 Name: "jobinfo1", 1402 Job: &v1alpha1.Job{ 1403 ObjectMeta: metav1.ObjectMeta{ 1404 Name: "Job1", 1405 Namespace: namespace, 1406 ResourceVersion: "100", 1407 }, 1408 Status: v1alpha1.JobStatus{ 1409 Running: 2, 1410 State: v1alpha1.JobState{ 1411 Phase: v1alpha1.Terminating, 1412 }, 1413 }, 1414 }, 1415 Pods: map[string]map[string]*v1.Pod{ 1416 "task1": { 1417 "pod1": buildPod(namespace, "pod1", v1.PodRunning, nil), 1418 "pod2": buildPod(namespace, "pod2", v1.PodRunning, nil), 1419 }, 1420 }, 1421 }, 1422 Action: busv1alpha1.TerminateJobAction, 1423 ExpectedVal: nil, 1424 }, 1425 { 1426 Name: "TerminatingState- With pod count not equal to zero", 1427 JobInfo: &apis.JobInfo{ 1428 Namespace: namespace, 1429 Name: "jobinfo1", 1430 Job: &v1alpha1.Job{ 1431 ObjectMeta: metav1.ObjectMeta{ 1432 Name: "Job1", 1433 Namespace: namespace, 1434 ResourceVersion: "100", 1435 }, 1436 Status: v1alpha1.JobStatus{ 1437 State: v1alpha1.JobState{ 1438 Phase: v1alpha1.Terminating, 1439 }, 1440 }, 1441 }, 1442 }, 1443 Action: busv1alpha1.TerminateJobAction, 1444 ExpectedVal: nil, 1445 }, 1446 } 1447 1448 for i, testcase := range testcases { 1449 t.Run(testcase.Name, func(t *testing.T) { 1450 testState := state.NewState(testcase.JobInfo) 1451 1452 fakecontroller := newFakeController() 1453 state.KillJob = fakecontroller.killJob 1454 1455 _, err := fakecontroller.vcClient.BatchV1alpha1().Jobs(namespace).Create(context.TODO(), testcase.JobInfo.Job, metav1.CreateOptions{}) 1456 if err != nil { 1457 t.Error("Error while creating Job") 1458 } 1459 1460 err = fakecontroller.cache.Add(testcase.JobInfo.Job) 1461 if err != nil { 1462 t.Error("Error while adding Job in cache") 1463 } 1464 1465 err = testState.Execute(testcase.Action) 1466 if err != nil { 1467 t.Errorf("Expected Error not to occur but got: %s", err) 1468 } 1469 1470 jobInfo, err := fakecontroller.cache.Get(fmt.Sprintf("%s/%s", testcase.JobInfo.Job.Namespace, testcase.JobInfo.Job.Name)) 1471 if err != nil { 1472 t.Error("Error while retrieving value from Cache") 1473 } 1474 1475 if testcase.JobInfo.Job.Status.Running == 0 && testcase.JobInfo.Job.Status.Pending == 0 && testcase.JobInfo.Job.Status.Terminating == 0 { 1476 1477 if jobInfo.Job.Status.State.Phase != v1alpha1.Terminated { 1478 fmt.Println(jobInfo.Job.Status.State.Phase) 1479 t.Errorf("Expected Phase to be Terminated State in test case: %d", i) 1480 } 1481 } else { 1482 if jobInfo.Job.Status.State.Phase != v1alpha1.Terminating { 1483 t.Errorf("Expected Phase to be Terminating state in test case: %d", i) 1484 } 1485 } 1486 }) 1487 } 1488 }