github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/jenkins/controller_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes 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 jenkins 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 23 "testing" 24 "text/template" 25 "time" 26 27 "k8s.io/test-infra/prow/config" 28 "k8s.io/test-infra/prow/github" 29 "k8s.io/test-infra/prow/kube" 30 "k8s.io/test-infra/prow/pjutil" 31 ) 32 33 type fca struct { 34 sync.Mutex 35 c *config.Config 36 } 37 38 func newFakeConfigAgent(t *testing.T, maxConcurrency int) *fca { 39 presubmits := []config.Presubmit{ 40 { 41 Name: "test-bazel-build", 42 RunAfterSuccess: []config.Presubmit{ 43 { 44 Name: "test-kubeadm-cloud", 45 RunIfChanged: "^(cmd/kubeadm|build/debs).*$", 46 }, 47 }, 48 }, 49 { 50 Name: "test-e2e", 51 RunAfterSuccess: []config.Presubmit{ 52 { 53 Name: "push-image", 54 }, 55 }, 56 }, 57 { 58 Name: "test-bazel-test", 59 }, 60 } 61 if err := config.SetRegexes(presubmits); err != nil { 62 t.Fatal(err) 63 } 64 presubmitMap := map[string][]config.Presubmit{ 65 "kubernetes/kubernetes": presubmits, 66 } 67 68 return &fca{ 69 c: &config.Config{ 70 JenkinsOperator: config.JenkinsOperator{ 71 JobURLTemplate: template.Must(template.New("test").Parse("{{.Status.PodName}}/{{.Status.State}}")), 72 MaxConcurrency: maxConcurrency, 73 }, 74 Presubmits: presubmitMap, 75 }, 76 } 77 78 } 79 80 func (f *fca) Config() *config.Config { 81 f.Lock() 82 defer f.Unlock() 83 return f.c 84 } 85 86 type fkc struct { 87 sync.Mutex 88 prowjobs []kube.ProwJob 89 } 90 91 func (f *fkc) CreateProwJob(pj kube.ProwJob) (kube.ProwJob, error) { 92 f.Lock() 93 defer f.Unlock() 94 f.prowjobs = append(f.prowjobs, pj) 95 return pj, nil 96 } 97 98 func (f *fkc) ListProwJobs(map[string]string) ([]kube.ProwJob, error) { 99 f.Lock() 100 defer f.Unlock() 101 return f.prowjobs, nil 102 } 103 104 func (f *fkc) ReplaceProwJob(name string, job kube.ProwJob) (kube.ProwJob, error) { 105 f.Lock() 106 defer f.Unlock() 107 for i := range f.prowjobs { 108 if f.prowjobs[i].Metadata.Name == name { 109 f.prowjobs[i] = job 110 return job, nil 111 } 112 } 113 return kube.ProwJob{}, fmt.Errorf("did not find prowjob %s", name) 114 } 115 116 type fjc struct { 117 sync.Mutex 118 built bool 119 pjs []kube.ProwJob 120 err error 121 builds map[string]JenkinsBuild 122 } 123 124 func (f *fjc) Build(pj *kube.ProwJob) error { 125 f.Lock() 126 defer f.Unlock() 127 if f.err != nil { 128 return f.err 129 } 130 f.built = true 131 f.pjs = append(f.pjs, *pj) 132 return nil 133 } 134 135 func (f *fjc) ListJenkinsBuilds(jobs map[string]struct{}) (map[string]JenkinsBuild, error) { 136 f.Lock() 137 defer f.Unlock() 138 if f.err != nil { 139 return nil, f.err 140 } 141 return f.builds, nil 142 } 143 144 func (f *fjc) Abort(job string, build *JenkinsBuild) error { 145 f.Lock() 146 defer f.Unlock() 147 return nil 148 } 149 150 type fghc struct { 151 sync.Mutex 152 changes []github.PullRequestChange 153 err error 154 } 155 156 func (f *fghc) GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error) { 157 f.Lock() 158 defer f.Unlock() 159 return f.changes, f.err 160 } 161 162 func (f *fghc) BotName() (string, error) { 163 f.Lock() 164 defer f.Unlock() 165 return "bot", nil 166 } 167 func (f *fghc) CreateStatus(org, repo, ref string, s github.Status) error { 168 f.Lock() 169 defer f.Unlock() 170 return nil 171 } 172 func (f *fghc) ListIssueComments(org, repo string, number int) ([]github.IssueComment, error) { 173 f.Lock() 174 defer f.Unlock() 175 return nil, nil 176 } 177 func (f *fghc) CreateComment(org, repo string, number int, comment string) error { 178 f.Lock() 179 defer f.Unlock() 180 return nil 181 } 182 func (f *fghc) DeleteComment(org, repo string, ID int) error { 183 f.Lock() 184 defer f.Unlock() 185 return nil 186 } 187 func (f *fghc) EditComment(org, repo string, ID int, comment string) error { 188 f.Lock() 189 defer f.Unlock() 190 return nil 191 } 192 193 func TestSyncNonPendingJobs(t *testing.T) { 194 var testcases = []struct { 195 name string 196 pj kube.ProwJob 197 pendingJobs map[string]int 198 maxConcurrency int 199 builds map[string]JenkinsBuild 200 err error 201 202 expectedState kube.ProwJobState 203 expectedBuild bool 204 expectedComplete bool 205 expectedReport bool 206 expectedEnqueued bool 207 expectedError bool 208 }{ 209 { 210 name: "complete pj", 211 pj: kube.ProwJob{ 212 Status: kube.ProwJobStatus{ 213 CompletionTime: time.Now(), 214 }, 215 }, 216 expectedComplete: true, 217 }, 218 { 219 name: "start new job", 220 pj: kube.ProwJob{ 221 Spec: kube.ProwJobSpec{ 222 Type: kube.PostsubmitJob, 223 }, 224 Status: kube.ProwJobStatus{ 225 State: kube.TriggeredState, 226 }, 227 }, 228 expectedBuild: true, 229 expectedReport: true, 230 expectedState: kube.PendingState, 231 expectedEnqueued: true, 232 }, 233 { 234 name: "start new job, error", 235 pj: kube.ProwJob{ 236 Spec: kube.ProwJobSpec{ 237 Type: kube.PresubmitJob, 238 Refs: kube.Refs{ 239 Pulls: []kube.Pull{{ 240 Number: 1, 241 SHA: "fake-sha", 242 }}, 243 }, 244 }, 245 Status: kube.ProwJobStatus{ 246 State: kube.TriggeredState, 247 }, 248 }, 249 err: errors.New("oh no"), 250 expectedReport: true, 251 expectedState: kube.ErrorState, 252 expectedComplete: true, 253 expectedError: true, 254 }, 255 { 256 name: "block running new job", 257 pj: kube.ProwJob{ 258 Spec: kube.ProwJobSpec{ 259 Type: kube.PostsubmitJob, 260 }, 261 Status: kube.ProwJobStatus{ 262 State: kube.TriggeredState, 263 }, 264 }, 265 pendingJobs: map[string]int{"motherearth": 10, "allagash": 8, "krusovice": 2}, 266 maxConcurrency: 20, 267 expectedBuild: false, 268 expectedReport: false, 269 expectedState: kube.TriggeredState, 270 expectedEnqueued: false, 271 }, 272 { 273 name: "allow running new job", 274 pj: kube.ProwJob{ 275 Spec: kube.ProwJobSpec{ 276 Type: kube.PostsubmitJob, 277 }, 278 Status: kube.ProwJobStatus{ 279 State: kube.TriggeredState, 280 }, 281 }, 282 pendingJobs: map[string]int{"motherearth": 10, "allagash": 8, "krusovice": 2}, 283 maxConcurrency: 21, 284 expectedBuild: true, 285 expectedReport: true, 286 expectedState: kube.PendingState, 287 expectedEnqueued: true, 288 }, 289 } 290 for _, tc := range testcases { 291 t.Logf("scenario %q", tc.name) 292 fjc := &fjc{ 293 err: tc.err, 294 } 295 fkc := &fkc{ 296 prowjobs: []kube.ProwJob{tc.pj}, 297 } 298 299 c := Controller{ 300 kc: fkc, 301 jc: fjc, 302 ca: newFakeConfigAgent(t, tc.maxConcurrency), 303 lock: sync.RWMutex{}, 304 pendingJobs: make(map[string]int), 305 } 306 if tc.pendingJobs != nil { 307 c.pendingJobs = tc.pendingJobs 308 } 309 310 reports := make(chan kube.ProwJob, 100) 311 if err := c.syncNonPendingJob(tc.pj, reports, tc.builds); err != nil { 312 t.Errorf("unexpected error: %v", err) 313 continue 314 } 315 close(reports) 316 317 actual := fkc.prowjobs[0] 318 if tc.expectedError && actual.Status.Description != "Error starting Jenkins job." { 319 t.Errorf("expected description %q, got %q", "Error starting Jenkins job.", actual.Status.Description) 320 continue 321 } 322 if actual.Status.State != tc.expectedState { 323 t.Errorf("expected state %q, got %q", tc.expectedState, actual.Status.State) 324 continue 325 } 326 if actual.Complete() != tc.expectedComplete { 327 t.Errorf("expected complete prowjob, got %v", actual) 328 continue 329 } 330 if tc.expectedReport && len(reports) != 1 { 331 t.Errorf("wanted one report but got %d", len(reports)) 332 continue 333 } 334 if !tc.expectedReport && len(reports) != 0 { 335 t.Errorf("did not wany any reports but got %d", len(reports)) 336 continue 337 } 338 if fjc.built != tc.expectedBuild { 339 t.Errorf("expected build: %t, got: %t", tc.expectedBuild, fjc.built) 340 continue 341 } 342 if tc.expectedEnqueued && actual.Status.Description != "Jenkins job enqueued." { 343 t.Errorf("expected enqueued prowjob, got %v", actual) 344 } 345 } 346 } 347 348 func TestSyncPendingJobs(t *testing.T) { 349 var testcases = []struct { 350 name string 351 pj kube.ProwJob 352 pendingJobs map[string]int 353 builds map[string]JenkinsBuild 354 err error 355 356 // TODO: Change to pass a ProwJobStatus 357 expectedState kube.ProwJobState 358 expectedBuild bool 359 expectedURL string 360 expectedComplete bool 361 expectedReport bool 362 expectedEnqueued bool 363 expectedError bool 364 }{ 365 { 366 name: "enqueued", 367 pj: kube.ProwJob{ 368 Metadata: kube.ObjectMeta{ 369 Name: "foofoo", 370 }, 371 Spec: kube.ProwJobSpec{ 372 Job: "test-job", 373 }, 374 Status: kube.ProwJobStatus{ 375 State: kube.PendingState, 376 Description: "Jenkins job enqueued.", 377 }, 378 }, 379 builds: map[string]JenkinsBuild{ 380 "foofoo": {enqueued: true, Number: 10}, 381 }, 382 expectedState: kube.PendingState, 383 expectedEnqueued: true, 384 }, 385 { 386 name: "finished queue", 387 pj: kube.ProwJob{ 388 Metadata: kube.ObjectMeta{ 389 Name: "boing", 390 }, 391 Spec: kube.ProwJobSpec{ 392 Job: "test-job", 393 }, 394 Status: kube.ProwJobStatus{ 395 State: kube.PendingState, 396 Description: "Jenkins job enqueued.", 397 }, 398 }, 399 builds: map[string]JenkinsBuild{ 400 "boing": {enqueued: false, Number: 10}, 401 }, 402 expectedURL: "test-job-10/pending", 403 expectedState: kube.PendingState, 404 expectedEnqueued: false, 405 expectedReport: true, 406 }, 407 { 408 name: "building", 409 pj: kube.ProwJob{ 410 Metadata: kube.ObjectMeta{ 411 Name: "firstoutthetrenches", 412 }, 413 Spec: kube.ProwJobSpec{ 414 Job: "test-job", 415 }, 416 Status: kube.ProwJobStatus{ 417 State: kube.PendingState, 418 }, 419 }, 420 builds: map[string]JenkinsBuild{ 421 "firstoutthetrenches": {enqueued: false, Number: 10}, 422 }, 423 expectedURL: "test-job-10/pending", 424 expectedState: kube.PendingState, 425 expectedReport: true, 426 }, 427 { 428 name: "missing build", 429 pj: kube.ProwJob{ 430 Metadata: kube.ObjectMeta{ 431 Name: "blabla", 432 }, 433 Spec: kube.ProwJobSpec{ 434 Type: kube.PresubmitJob, 435 Job: "test-job", 436 Refs: kube.Refs{ 437 Pulls: []kube.Pull{{ 438 Number: 1, 439 SHA: "fake-sha", 440 }}, 441 }, 442 }, 443 Status: kube.ProwJobStatus{ 444 State: kube.PendingState, 445 }, 446 }, 447 // missing build 448 builds: map[string]JenkinsBuild{ 449 "other": {enqueued: false, Number: 10}, 450 }, 451 expectedURL: "https://github.com/kubernetes/test-infra/issues", 452 expectedState: kube.ErrorState, 453 expectedError: true, 454 expectedComplete: true, 455 expectedReport: true, 456 }, 457 { 458 name: "finished, success", 459 pj: kube.ProwJob{ 460 Metadata: kube.ObjectMeta{ 461 Name: "winwin", 462 }, 463 Spec: kube.ProwJobSpec{ 464 Job: "test-job", 465 }, 466 Status: kube.ProwJobStatus{ 467 State: kube.PendingState, 468 }, 469 }, 470 builds: map[string]JenkinsBuild{ 471 "winwin": {Result: pState(Succeess), Number: 11}, 472 }, 473 expectedURL: "test-job-11/success", 474 expectedState: kube.SuccessState, 475 expectedComplete: true, 476 expectedReport: true, 477 }, 478 { 479 name: "finished, failed", 480 pj: kube.ProwJob{ 481 Metadata: kube.ObjectMeta{ 482 Name: "whatapity", 483 }, 484 Spec: kube.ProwJobSpec{ 485 Job: "test-job", 486 }, 487 Status: kube.ProwJobStatus{ 488 State: kube.PendingState, 489 }, 490 }, 491 builds: map[string]JenkinsBuild{ 492 "whatapity": {Result: pState(Failure), Number: 12}, 493 }, 494 expectedURL: "test-job-12/failure", 495 expectedState: kube.FailureState, 496 expectedComplete: true, 497 expectedReport: true, 498 }, 499 } 500 for _, tc := range testcases { 501 t.Logf("scenario %q", tc.name) 502 fjc := &fjc{ 503 err: tc.err, 504 } 505 fkc := &fkc{ 506 prowjobs: []kube.ProwJob{tc.pj}, 507 } 508 509 c := Controller{ 510 kc: fkc, 511 jc: fjc, 512 ca: newFakeConfigAgent(t, 0), 513 lock: sync.RWMutex{}, 514 pendingJobs: make(map[string]int), 515 } 516 517 reports := make(chan kube.ProwJob, 100) 518 if err := c.syncPendingJob(tc.pj, reports, tc.builds); err != nil { 519 t.Errorf("unexpected error: %v", err) 520 continue 521 } 522 close(reports) 523 524 actual := fkc.prowjobs[0] 525 if tc.expectedError && actual.Status.Description != "Error finding Jenkins job." { 526 t.Errorf("expected description %q, got %q", "Error finding Jenkins job.", actual.Status.Description) 527 continue 528 } 529 if actual.Status.State != tc.expectedState { 530 t.Errorf("expected state %q, got %q", tc.expectedState, actual.Status.State) 531 continue 532 } 533 if actual.Complete() != tc.expectedComplete { 534 t.Errorf("expected complete prowjob, got %v", actual) 535 continue 536 } 537 if tc.expectedReport && len(reports) != 1 { 538 t.Errorf("wanted one report but got %d", len(reports)) 539 continue 540 } 541 if !tc.expectedReport && len(reports) != 0 { 542 t.Errorf("did not wany any reports but got %d", len(reports)) 543 continue 544 } 545 if fjc.built != tc.expectedBuild { 546 t.Errorf("expected build: %t, got: %t", tc.expectedBuild, fjc.built) 547 continue 548 } 549 if tc.expectedEnqueued && actual.Status.Description != "Jenkins job enqueued." { 550 t.Errorf("expected enqueued prowjob, got %v", actual) 551 } 552 if tc.expectedURL != actual.Status.URL { 553 t.Errorf("expected status URL: %s, got: %s", tc.expectedURL, actual.Status.URL) 554 } 555 } 556 } 557 558 func pState(state string) *string { 559 s := state 560 return &s 561 } 562 563 // TestBatch walks through the happy path of a batch job on Jenkins. 564 func TestBatch(t *testing.T) { 565 pre := config.Presubmit{ 566 Name: "pr-some-job", 567 Agent: "jenkins", 568 Context: "Some Job Context", 569 } 570 pj := pjutil.NewProwJob(pjutil.BatchSpec(pre, kube.Refs{ 571 Org: "o", 572 Repo: "r", 573 BaseRef: "master", 574 BaseSHA: "123", 575 Pulls: []kube.Pull{ 576 { 577 Number: 1, 578 SHA: "abc", 579 }, 580 { 581 Number: 2, 582 SHA: "qwe", 583 }, 584 }, 585 })) 586 pj.Metadata.Name = "known_name" 587 fc := &fkc{ 588 prowjobs: []kube.ProwJob{pj}, 589 } 590 jc := &fjc{ 591 builds: map[string]JenkinsBuild{ 592 "known_name": { /* Running */ }, 593 }, 594 } 595 c := Controller{ 596 kc: fc, 597 jc: jc, 598 ca: newFakeConfigAgent(t, 0), 599 pendingJobs: make(map[string]int), 600 lock: sync.RWMutex{}, 601 } 602 603 if err := c.Sync(); err != nil { 604 t.Fatalf("Error on first sync: %v", err) 605 } 606 if fc.prowjobs[0].Status.State != kube.PendingState { 607 t.Fatalf("Wrong state: %v", fc.prowjobs[0].Status.State) 608 } 609 if fc.prowjobs[0].Status.Description != "Jenkins job enqueued." { 610 t.Fatalf("Expected description %q, got %q.", "Jenkins job enqueued.", fc.prowjobs[0].Status.Description) 611 } 612 jc.builds["known_name"] = JenkinsBuild{Number: 42} 613 if err := c.Sync(); err != nil { 614 t.Fatalf("Error on second sync: %v", err) 615 } 616 if fc.prowjobs[0].Status.Description != "Jenkins job running." { 617 t.Fatalf("Expected description %q, got %q.", "Jenkins job running.", fc.prowjobs[0].Status.Description) 618 } 619 if fc.prowjobs[0].Status.PodName != "pr-some-job-42" { 620 t.Fatalf("Wrong PodName: %s", fc.prowjobs[0].Status.PodName) 621 } 622 jc.builds["known_name"] = JenkinsBuild{Result: pState(Succeess)} 623 if err := c.Sync(); err != nil { 624 t.Fatalf("Error on third sync: %v", err) 625 } 626 if fc.prowjobs[0].Status.Description != "Jenkins job succeeded." { 627 t.Fatalf("Expected description %q, got %q.", "Jenkins job succeeded.", fc.prowjobs[0].Status.Description) 628 } 629 if fc.prowjobs[0].Status.State != kube.SuccessState { 630 t.Fatalf("Wrong state: %v", fc.prowjobs[0].Status.State) 631 } 632 // This is what the SQ reads. 633 if fc.prowjobs[0].Spec.Context != "Some Job Context" { 634 t.Fatalf("Wrong context: %v", fc.prowjobs[0].Spec.Context) 635 } 636 } 637 638 func TestRunAfterSuccessCanRun(t *testing.T) { 639 tests := []struct { 640 name string 641 642 parent *kube.ProwJob 643 child *kube.ProwJob 644 645 changes []github.PullRequestChange 646 err error 647 648 expected bool 649 }{ 650 { 651 name: "child does not require specific changes", 652 parent: &kube.ProwJob{ 653 Spec: kube.ProwJobSpec{ 654 Job: "test-e2e", 655 Type: kube.PresubmitJob, 656 Refs: kube.Refs{ 657 Org: "kubernetes", 658 Repo: "kubernetes", 659 Pulls: []kube.Pull{ 660 {Number: 123}, 661 }, 662 }, 663 }, 664 }, 665 child: &kube.ProwJob{ 666 Spec: kube.ProwJobSpec{ 667 Job: "push-image", 668 }, 669 }, 670 expected: true, 671 }, 672 { 673 name: "child requires specific changes that are done", 674 parent: &kube.ProwJob{ 675 Spec: kube.ProwJobSpec{ 676 Job: "test-bazel-build", 677 Type: kube.PresubmitJob, 678 Refs: kube.Refs{ 679 Org: "kubernetes", 680 Repo: "kubernetes", 681 Pulls: []kube.Pull{ 682 {Number: 123}, 683 }, 684 }, 685 }, 686 }, 687 child: &kube.ProwJob{ 688 Spec: kube.ProwJobSpec{ 689 Job: "test-kubeadm-cloud", 690 }, 691 }, 692 changes: []github.PullRequestChange{ 693 {Filename: "cmd/kubeadm/kubeadm.go"}, 694 {Filename: "vendor/BUILD"}, 695 {Filename: ".gitatrributes"}, 696 }, 697 expected: true, 698 }, 699 { 700 name: "child requires specific changes that are not done", 701 parent: &kube.ProwJob{ 702 Spec: kube.ProwJobSpec{ 703 Job: "test-bazel-build", 704 Type: kube.PresubmitJob, 705 Refs: kube.Refs{ 706 Org: "kubernetes", 707 Repo: "kubernetes", 708 Pulls: []kube.Pull{ 709 {Number: 123}, 710 }, 711 }, 712 }, 713 }, 714 child: &kube.ProwJob{ 715 Spec: kube.ProwJobSpec{ 716 Job: "test-kubeadm-cloud", 717 }, 718 }, 719 changes: []github.PullRequestChange{ 720 {Filename: "vendor/BUILD"}, 721 {Filename: ".gitatrributes"}, 722 }, 723 expected: false, 724 }, 725 } 726 727 for _, test := range tests { 728 t.Logf("scenario %q", test.name) 729 730 fakeGH := &fghc{ 731 changes: test.changes, 732 err: test.err, 733 } 734 735 got := RunAfterSuccessCanRun(test.parent, test.child, newFakeConfigAgent(t, 0), fakeGH) 736 if got != test.expected { 737 t.Errorf("expected to run: %t, got: %t", test.expected, got) 738 } 739 } 740 } 741 742 func TestMaxConcurrencyWithNewlyTriggeredJobs(t *testing.T) { 743 tests := []struct { 744 name string 745 pjs []kube.ProwJob 746 pendingJobs map[string]int 747 expectedBuilds int 748 }{ 749 { 750 name: "avoid starting a triggered job", 751 pjs: []kube.ProwJob{ 752 { 753 Spec: kube.ProwJobSpec{ 754 Job: "test-bazel-build", 755 Type: kube.PostsubmitJob, 756 MaxConcurrency: 1, 757 }, 758 Status: kube.ProwJobStatus{ 759 State: kube.TriggeredState, 760 }, 761 }, 762 { 763 Spec: kube.ProwJobSpec{ 764 Job: "test-bazel-build", 765 Type: kube.PostsubmitJob, 766 MaxConcurrency: 1, 767 }, 768 Status: kube.ProwJobStatus{ 769 State: kube.TriggeredState, 770 }, 771 }, 772 }, 773 pendingJobs: make(map[string]int), 774 expectedBuilds: 1, 775 }, 776 { 777 name: "both triggered jobs can start", 778 pjs: []kube.ProwJob{ 779 { 780 Spec: kube.ProwJobSpec{ 781 Job: "test-bazel-build", 782 Type: kube.PostsubmitJob, 783 MaxConcurrency: 2, 784 }, 785 Status: kube.ProwJobStatus{ 786 State: kube.TriggeredState, 787 }, 788 }, 789 { 790 Spec: kube.ProwJobSpec{ 791 Job: "test-bazel-build", 792 Type: kube.PostsubmitJob, 793 MaxConcurrency: 2, 794 }, 795 Status: kube.ProwJobStatus{ 796 State: kube.TriggeredState, 797 }, 798 }, 799 }, 800 pendingJobs: make(map[string]int), 801 expectedBuilds: 2, 802 }, 803 { 804 name: "no triggered job can start", 805 pjs: []kube.ProwJob{ 806 { 807 Spec: kube.ProwJobSpec{ 808 Job: "test-bazel-build", 809 Type: kube.PostsubmitJob, 810 MaxConcurrency: 5, 811 }, 812 Status: kube.ProwJobStatus{ 813 State: kube.TriggeredState, 814 }, 815 }, 816 { 817 Spec: kube.ProwJobSpec{ 818 Job: "test-bazel-build", 819 Type: kube.PostsubmitJob, 820 MaxConcurrency: 5, 821 }, 822 Status: kube.ProwJobStatus{ 823 State: kube.TriggeredState, 824 }, 825 }, 826 }, 827 pendingJobs: map[string]int{"test-bazel-build": 5}, 828 expectedBuilds: 0, 829 }, 830 } 831 832 for _, test := range tests { 833 jobs := make(chan kube.ProwJob, len(test.pjs)) 834 for _, pj := range test.pjs { 835 jobs <- pj 836 } 837 close(jobs) 838 839 fc := &fkc{ 840 prowjobs: test.pjs, 841 } 842 fjc := &fjc{} 843 c := Controller{ 844 kc: fc, 845 jc: fjc, 846 ca: newFakeConfigAgent(t, 0), 847 pendingJobs: test.pendingJobs, 848 } 849 850 reports := make(chan<- kube.ProwJob, len(test.pjs)) 851 errors := make(chan<- error, len(test.pjs)) 852 853 syncProwJobs(c.syncNonPendingJob, jobs, reports, errors, nil) 854 if len(fjc.pjs) != test.expectedBuilds { 855 t.Errorf("expected builds: %d, got: %d", test.expectedBuilds, len(fjc.pjs)) 856 } 857 } 858 } 859 860 func TestGetJenkinsJobs(t *testing.T) { 861 tests := []struct { 862 name string 863 pjs []kube.ProwJob 864 expected map[string]struct{} 865 }{ 866 { 867 name: "both complete and running", 868 pjs: []kube.ProwJob{ 869 { 870 Spec: kube.ProwJobSpec{ 871 Job: "coolio", 872 }, 873 Status: kube.ProwJobStatus{ 874 CompletionTime: time.Now(), 875 }, 876 }, 877 { 878 Spec: kube.ProwJobSpec{ 879 Job: "maradona", 880 }, 881 Status: kube.ProwJobStatus{}, 882 }, 883 }, 884 expected: map[string]struct{}{"maradona": {}}, 885 }, 886 { 887 name: "only complete", 888 pjs: []kube.ProwJob{ 889 { 890 Spec: kube.ProwJobSpec{ 891 Job: "coolio", 892 }, 893 Status: kube.ProwJobStatus{ 894 CompletionTime: time.Now(), 895 }, 896 }, 897 { 898 Spec: kube.ProwJobSpec{ 899 Job: "maradona", 900 }, 901 Status: kube.ProwJobStatus{ 902 CompletionTime: time.Now(), 903 }, 904 }, 905 }, 906 expected: map[string]struct{}{}, 907 }, 908 { 909 name: "only running", 910 pjs: []kube.ProwJob{ 911 { 912 Spec: kube.ProwJobSpec{ 913 Job: "coolio", 914 }, 915 Status: kube.ProwJobStatus{}, 916 }, 917 { 918 Spec: kube.ProwJobSpec{ 919 Job: "maradona", 920 }, 921 Status: kube.ProwJobStatus{}, 922 }, 923 }, 924 expected: map[string]struct{}{"maradona": {}, "coolio": {}}, 925 }, 926 } 927 928 for _, test := range tests { 929 t.Logf("scenario %q", test.name) 930 got := getJenkinsJobs(test.pjs) 931 if len(got) != len(test.expected) { 932 t.Errorf("unexpected job amount: %d (%v), expected: %d (%v)", 933 len(got), got, len(test.expected), test.expected) 934 } 935 for job := range test.expected { 936 if _, ok := got[job]; !ok { 937 t.Errorf("expected job %q was not found, got %v", job, got) 938 } 939 } 940 } 941 }