github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/pkg/pjutil/pjutil_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 pjutil 18 19 import ( 20 "errors" 21 "fmt" 22 "reflect" 23 "testing" 24 "text/template" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/sirupsen/logrus" 29 corev1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/api/equality" 31 "k8s.io/apimachinery/pkg/api/resource" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/util/diff" 34 "k8s.io/apimachinery/pkg/util/sets" 35 36 prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 37 "sigs.k8s.io/prow/pkg/config" 38 "sigs.k8s.io/prow/pkg/github" 39 "sigs.k8s.io/prow/pkg/kube" 40 ) 41 42 func TestPostsubmitSpec(t *testing.T) { 43 tests := []struct { 44 name string 45 p config.Postsubmit 46 refs prowapi.Refs 47 expected prowapi.ProwJobSpec 48 }{ 49 { 50 name: "can override fields", 51 p: config.Postsubmit{ 52 JobBase: config.JobBase{ 53 UtilityConfig: config.UtilityConfig{ 54 PathAlias: "foo", 55 CloneURI: "bar", 56 SkipSubmodules: true, 57 CloneDepth: 7, 58 SkipFetchHead: true, 59 }, 60 }, 61 }, 62 expected: prowapi.ProwJobSpec{ 63 Type: prowapi.PostsubmitJob, 64 Refs: &prowapi.Refs{ 65 PathAlias: "foo", 66 CloneURI: "bar", 67 SkipSubmodules: true, 68 CloneDepth: 7, 69 SkipFetchHead: true, 70 }, 71 Report: true, 72 }, 73 }, 74 { 75 name: "controller can default fields", 76 refs: prowapi.Refs{ 77 PathAlias: "fancy", 78 CloneURI: "cats", 79 SkipSubmodules: true, 80 CloneDepth: 8, 81 SkipFetchHead: true, 82 }, 83 expected: prowapi.ProwJobSpec{ 84 Type: prowapi.PostsubmitJob, 85 Refs: &prowapi.Refs{ 86 PathAlias: "fancy", 87 CloneURI: "cats", 88 SkipSubmodules: true, 89 CloneDepth: 8, 90 SkipFetchHead: true, 91 }, 92 Report: true, 93 }, 94 }, 95 { 96 name: "job overrides take precedence over controller defaults", 97 p: config.Postsubmit{ 98 JobBase: config.JobBase{ 99 UtilityConfig: config.UtilityConfig{ 100 PathAlias: "foo", 101 CloneDepth: 3, 102 CloneURI: "bar", 103 }, 104 }, 105 }, 106 refs: prowapi.Refs{ 107 PathAlias: "fancy", 108 CloneDepth: 1, 109 CloneURI: "cats", 110 }, 111 expected: prowapi.ProwJobSpec{ 112 Type: prowapi.PostsubmitJob, 113 Refs: &prowapi.Refs{ 114 PathAlias: "foo", 115 CloneDepth: 3, 116 CloneURI: "bar", 117 }, 118 Report: true, 119 }, 120 }, 121 } 122 123 for _, tc := range tests { 124 t.Run(tc.name, func(t *testing.T) { 125 actual := PostsubmitSpec(tc.p, tc.refs) 126 if diff := cmp.Diff(actual, tc.expected); diff != "" { 127 t.Errorf("PostsubmitSpec() got unexpected diff (-have, +want):\n%s", diff) 128 } 129 }) 130 } 131 } 132 133 func TestPresubmitSpec(t *testing.T) { 134 tests := []struct { 135 name string 136 p config.Presubmit 137 refs prowapi.Refs 138 expected prowapi.ProwJobSpec 139 }{ 140 { 141 name: "can override path alias and cloneuri", 142 p: config.Presubmit{ 143 JobBase: config.JobBase{ 144 UtilityConfig: config.UtilityConfig{ 145 PathAlias: "foo", 146 CloneURI: "bar", 147 }, 148 }, 149 }, 150 expected: prowapi.ProwJobSpec{ 151 Type: prowapi.PresubmitJob, 152 Refs: &prowapi.Refs{ 153 PathAlias: "foo", 154 CloneURI: "bar", 155 }, 156 Report: true, 157 }, 158 }, 159 { 160 name: "controller can default path alias and cloneuri", 161 refs: prowapi.Refs{ 162 PathAlias: "fancy", 163 CloneURI: "cats", 164 }, 165 expected: prowapi.ProwJobSpec{ 166 Type: prowapi.PresubmitJob, 167 Refs: &prowapi.Refs{ 168 PathAlias: "fancy", 169 CloneURI: "cats", 170 }, 171 Report: true, 172 }, 173 }, 174 { 175 name: "job overrides take precedence over controller defaults", 176 p: config.Presubmit{ 177 JobBase: config.JobBase{ 178 UtilityConfig: config.UtilityConfig{ 179 PathAlias: "foo", 180 CloneURI: "bar", 181 }, 182 }, 183 }, 184 refs: prowapi.Refs{ 185 PathAlias: "fancy", 186 CloneURI: "cats", 187 }, 188 expected: prowapi.ProwJobSpec{ 189 Type: prowapi.PresubmitJob, 190 Refs: &prowapi.Refs{ 191 PathAlias: "foo", 192 CloneURI: "bar", 193 }, 194 Report: true, 195 }, 196 }, 197 } 198 199 for _, tc := range tests { 200 t.Run(tc.name, func(t *testing.T) { 201 actual := PresubmitSpec(tc.p, tc.refs) 202 if diff := cmp.Diff(actual, tc.expected); diff != "" { 203 t.Errorf("PresubmitSpec() got unexpected diff (-have, +want):\n%s", diff) 204 } 205 }) 206 } 207 } 208 209 func TestBatchSpec(t *testing.T) { 210 tests := []struct { 211 name string 212 p config.Presubmit 213 refs prowapi.Refs 214 expected prowapi.ProwJobSpec 215 }{ 216 { 217 name: "can override path alias and cloneuri", 218 p: config.Presubmit{ 219 JobBase: config.JobBase{ 220 UtilityConfig: config.UtilityConfig{ 221 PathAlias: "foo", 222 CloneURI: "bar", 223 }, 224 }, 225 }, 226 expected: prowapi.ProwJobSpec{ 227 Type: prowapi.BatchJob, 228 Refs: &prowapi.Refs{ 229 PathAlias: "foo", 230 CloneURI: "bar", 231 }, 232 }, 233 }, 234 { 235 name: "controller can default path alias and cloneuri", 236 refs: prowapi.Refs{ 237 PathAlias: "fancy", 238 CloneURI: "cats", 239 }, 240 expected: prowapi.ProwJobSpec{ 241 Type: prowapi.BatchJob, 242 Refs: &prowapi.Refs{ 243 PathAlias: "fancy", 244 CloneURI: "cats", 245 }, 246 }, 247 }, 248 { 249 name: "job overrides take precedence over controller defaults", 250 p: config.Presubmit{ 251 JobBase: config.JobBase{ 252 UtilityConfig: config.UtilityConfig{ 253 PathAlias: "foo", 254 CloneURI: "bar", 255 }, 256 }, 257 }, 258 refs: prowapi.Refs{ 259 PathAlias: "fancy", 260 CloneURI: "cats", 261 }, 262 expected: prowapi.ProwJobSpec{ 263 Type: prowapi.BatchJob, 264 Refs: &prowapi.Refs{ 265 PathAlias: "foo", 266 CloneURI: "bar", 267 }, 268 }, 269 }, 270 } 271 272 for _, tc := range tests { 273 t.Run(tc.name, func(t *testing.T) { 274 actual := BatchSpec(tc.p, tc.refs) 275 if diff := cmp.Diff(actual, tc.expected); diff != "" { 276 t.Errorf("BatchSpec() got unexpected diff (-have, +want):\n%s", diff) 277 } 278 }) 279 } 280 } 281 282 func TestCompletePrimaryRefs(t *testing.T) { 283 cases := []struct { 284 name string 285 refs prowapi.Refs 286 jobBase config.JobBase 287 expected prowapi.Refs 288 }{ 289 { 290 name: "basically works", 291 }, 292 { 293 name: "use values from refs", 294 refs: prowapi.Refs{ 295 PathAlias: "this", 296 CloneURI: "that", 297 SkipSubmodules: true, 298 CloneDepth: 1, 299 SkipFetchHead: true, 300 }, 301 expected: prowapi.Refs{ 302 PathAlias: "this", 303 CloneURI: "that", 304 SkipSubmodules: true, 305 CloneDepth: 1, 306 SkipFetchHead: true, 307 }, 308 }, 309 { 310 name: "use values from job base", 311 jobBase: config.JobBase{ 312 UtilityConfig: config.UtilityConfig{ 313 PathAlias: "more", 314 CloneURI: "fun", 315 SkipSubmodules: true, 316 CloneDepth: 2, 317 SkipFetchHead: true, 318 DecorationConfig: &prowapi.DecorationConfig{ 319 BloblessFetch: boolPtr(true), 320 }, 321 }, 322 }, 323 expected: prowapi.Refs{ 324 PathAlias: "more", 325 CloneURI: "fun", 326 SkipSubmodules: true, 327 CloneDepth: 2, 328 SkipFetchHead: true, 329 BloblessFetch: boolPtr(true), 330 }, 331 }, 332 { 333 name: "prefer job base values", 334 refs: prowapi.Refs{ 335 PathAlias: "this", 336 CloneURI: "that", 337 CloneDepth: 1, 338 }, 339 jobBase: config.JobBase{ 340 UtilityConfig: config.UtilityConfig{ 341 PathAlias: "more", 342 CloneURI: "fun", 343 CloneDepth: 2, 344 }, 345 }, 346 expected: prowapi.Refs{ 347 PathAlias: "more", 348 CloneURI: "fun", 349 CloneDepth: 2, 350 }, 351 }, 352 } 353 354 for _, tc := range cases { 355 t.Run(tc.name, func(t *testing.T) { 356 actual := CompletePrimaryRefs(tc.refs, tc.jobBase) 357 if diff := cmp.Diff(actual, &tc.expected); diff != "" { 358 t.Errorf("CompletePrimaryRefs() got unexpected diff (-have, +want):\n%s", diff) 359 } 360 }) 361 } 362 } 363 364 func TestPartitionActive(t *testing.T) { 365 tests := []struct { 366 pjs []prowapi.ProwJob 367 368 pending sets.Set[string] 369 triggered sets.Set[string] 370 aborted sets.Set[string] 371 }{ 372 { 373 pjs: []prowapi.ProwJob{ 374 { 375 ObjectMeta: metav1.ObjectMeta{ 376 Name: "foo", 377 }, 378 Status: prowapi.ProwJobStatus{ 379 State: prowapi.TriggeredState, 380 }, 381 }, 382 { 383 ObjectMeta: metav1.ObjectMeta{ 384 Name: "bar", 385 }, 386 Status: prowapi.ProwJobStatus{ 387 State: prowapi.PendingState, 388 }, 389 }, 390 { 391 ObjectMeta: metav1.ObjectMeta{ 392 Name: "baz", 393 }, 394 Status: prowapi.ProwJobStatus{ 395 State: prowapi.SuccessState, 396 }, 397 }, 398 { 399 ObjectMeta: metav1.ObjectMeta{ 400 Name: "error", 401 }, 402 Status: prowapi.ProwJobStatus{ 403 State: prowapi.ErrorState, 404 }, 405 }, 406 { 407 ObjectMeta: metav1.ObjectMeta{ 408 Name: "bak", 409 }, 410 Status: prowapi.ProwJobStatus{ 411 State: prowapi.PendingState, 412 }, 413 }, 414 { 415 ObjectMeta: metav1.ObjectMeta{ 416 Name: "aborted", 417 }, 418 Status: prowapi.ProwJobStatus{ 419 State: prowapi.AbortedState, 420 }, 421 }, 422 { 423 ObjectMeta: metav1.ObjectMeta{ 424 Name: "aborted-and-completed", 425 }, 426 Status: prowapi.ProwJobStatus{ 427 State: prowapi.AbortedState, 428 CompletionTime: &[]metav1.Time{metav1.Now()}[0], 429 }, 430 }, 431 }, 432 pending: sets.New[string]("bar", "bak"), 433 triggered: sets.New[string]("foo"), 434 aborted: sets.New[string]("aborted"), 435 }, 436 } 437 438 for i, test := range tests { 439 t.Logf("test run #%d", i) 440 pendingCh, triggeredCh, abortedCh := PartitionActive(test.pjs) 441 for job := range pendingCh { 442 if !test.pending.Has(job.Name) { 443 t.Errorf("didn't find pending job %#v", job) 444 } 445 } 446 for job := range triggeredCh { 447 if !test.triggered.Has(job.Name) { 448 t.Errorf("didn't find triggered job %#v", job) 449 } 450 } 451 for job := range abortedCh { 452 if !test.aborted.Has(job.Name) { 453 t.Errorf("didn't find aborted job %#v", job) 454 } 455 } 456 } 457 } 458 459 func TestGetLatestProwJobs(t *testing.T) { 460 tests := []struct { 461 name string 462 463 pjs []prowapi.ProwJob 464 jobType string 465 466 expected map[string]struct{} 467 }{ 468 { 469 pjs: []prowapi.ProwJob{ 470 { 471 ObjectMeta: metav1.ObjectMeta{ 472 Name: "831c7df0-baa4-11e7-a1a4-0a58ac10134a", 473 }, 474 Spec: prowapi.ProwJobSpec{ 475 Type: prowapi.PresubmitJob, 476 Agent: prowapi.JenkinsAgent, 477 Job: "test_pull_request_origin_extended_networking_minimal", 478 Refs: &prowapi.Refs{ 479 Org: "openshift", 480 Repo: "origin", 481 BaseRef: "master", 482 BaseSHA: "e92d5c525795eafb82cf16e3ab151b567b47e333", 483 Pulls: []prowapi.Pull{ 484 { 485 Number: 17061, 486 Author: "enj", 487 SHA: "f94a3a51f59a693642e39084f03efa83af9442d3", 488 HeadRef: "fixes-123", 489 }, 490 }, 491 }, 492 Report: true, 493 Context: "ci/openshift-jenkins/extended_networking_minimal", 494 RerunCommand: "/test extended_networking_minimal", 495 }, 496 Status: prowapi.ProwJobStatus{ 497 StartTime: metav1.Date(2017, time.October, 26, 23, 22, 19, 0, time.UTC), 498 State: prowapi.FailureState, 499 Description: "Jenkins job failed.", 500 URL: "https://openshift-gce-devel.appspot.com/build/origin-ci-test/pr-logs/pull/17061/test_pull_request_origin_extended_networking_minimal/9756/", 501 PodName: "test_pull_request_origin_extended_networking_minimal-9756", 502 BuildID: "9756", 503 }, 504 }, 505 { 506 ObjectMeta: metav1.ObjectMeta{ 507 Name: "0079d4d3-ba25-11e7-ae3f-0a58ac10123b", 508 }, 509 Spec: prowapi.ProwJobSpec{ 510 Type: prowapi.PresubmitJob, 511 Agent: prowapi.JenkinsAgent, 512 Job: "test_pull_request_origin_extended_networking_minimal", 513 Refs: &prowapi.Refs{ 514 Org: "openshift", 515 Repo: "origin", 516 BaseRef: "master", 517 BaseSHA: "e92d5c525795eafb82cf16e3ab151b567b47e333", 518 Pulls: []prowapi.Pull{ 519 { 520 Number: 17061, 521 Author: "enj", 522 SHA: "f94a3a51f59a693642e39084f03efa83af9442d3", 523 HeadRef: "fixes-123", 524 }, 525 }, 526 }, 527 Report: true, 528 Context: "ci/openshift-jenkins/extended_networking_minimal", 529 RerunCommand: "/test extended_networking_minimal", 530 }, 531 Status: prowapi.ProwJobStatus{ 532 StartTime: metav1.Date(2017, time.October, 26, 22, 22, 19, 0, time.UTC), 533 State: prowapi.FailureState, 534 Description: "Jenkins job failed.", 535 URL: "https://openshift-gce-devel.appspot.com/build/origin-ci-test/pr-logs/pull/17061/test_pull_request_origin_extended_networking_minimal/9755/", 536 PodName: "test_pull_request_origin_extended_networking_minimal-9755", 537 BuildID: "9755", 538 }, 539 }, 540 }, 541 jobType: "presubmit", 542 expected: map[string]struct{}{"831c7df0-baa4-11e7-a1a4-0a58ac10134a": {}}, 543 }, 544 } 545 546 for _, test := range tests { 547 got := GetLatestProwJobs(test.pjs, prowapi.ProwJobType(test.jobType)) 548 if len(got) != len(test.expected) { 549 t.Errorf("expected jobs:\n%+v\ngot jobs:\n%+v", test.expected, got) 550 continue 551 } 552 for name := range test.expected { 553 if _, ok := got[name]; ok { 554 t.Errorf("expected job: %s", name) 555 } 556 } 557 } 558 } 559 560 func TestNewProwJob(t *testing.T) { 561 var testCases = []struct { 562 name string 563 spec prowapi.ProwJobSpec 564 labels map[string]string 565 expectedLabels map[string]string 566 annotations map[string]string 567 expectedAnnotations map[string]string 568 }{ 569 { 570 name: "periodic job, no extra labels", 571 spec: prowapi.ProwJobSpec{ 572 Job: "job", 573 Context: "job-context", 574 Type: prowapi.PeriodicJob, 575 }, 576 labels: map[string]string{}, 577 expectedLabels: map[string]string{ 578 kube.CreatedByProw: "true", 579 kube.ProwJobAnnotation: "job", 580 kube.ContextAnnotation: "job-context", 581 kube.ProwJobTypeLabel: "periodic", 582 }, 583 expectedAnnotations: map[string]string{ 584 kube.ProwJobAnnotation: "job", 585 kube.ContextAnnotation: "job-context", 586 }, 587 }, 588 { 589 name: "periodic job, extra labels", 590 spec: prowapi.ProwJobSpec{ 591 Job: "job", 592 Context: "job-context", 593 Type: prowapi.PeriodicJob, 594 }, 595 labels: map[string]string{ 596 "extra": "stuff", 597 }, 598 expectedLabels: map[string]string{ 599 kube.CreatedByProw: "true", 600 kube.ProwJobAnnotation: "job", 601 kube.ContextAnnotation: "job-context", 602 kube.ProwJobTypeLabel: "periodic", 603 "extra": "stuff", 604 }, 605 expectedAnnotations: map[string]string{ 606 kube.ProwJobAnnotation: "job", 607 kube.ContextAnnotation: "job-context", 608 }, 609 }, 610 { 611 name: "periodic job with extra refs", 612 spec: prowapi.ProwJobSpec{ 613 Job: "job", 614 Context: "job-context", 615 Type: prowapi.PeriodicJob, 616 ExtraRefs: []prowapi.Refs{{ 617 Org: "org", 618 Repo: "repo", 619 BaseRef: "main", 620 }}, 621 }, 622 labels: map[string]string{}, 623 expectedLabels: map[string]string{ 624 kube.CreatedByProw: "true", 625 kube.ProwJobAnnotation: "job", 626 kube.ContextAnnotation: "job-context", 627 kube.ProwJobTypeLabel: "periodic", 628 kube.OrgLabel: "org", 629 kube.RepoLabel: "repo", 630 kube.BaseRefLabel: "main", 631 }, 632 expectedAnnotations: map[string]string{ 633 kube.ProwJobAnnotation: "job", 634 kube.ContextAnnotation: "job-context", 635 }, 636 }, 637 { 638 name: "presubmit job", 639 spec: prowapi.ProwJobSpec{ 640 Job: "job", 641 Context: "job-context", 642 Type: prowapi.PresubmitJob, 643 Refs: &prowapi.Refs{ 644 Org: "org", 645 Repo: "repo", 646 BaseRef: "main", 647 Pulls: []prowapi.Pull{ 648 {Number: 1}, 649 }, 650 }, 651 }, 652 labels: map[string]string{}, 653 expectedLabels: map[string]string{ 654 kube.CreatedByProw: "true", 655 kube.ProwJobAnnotation: "job", 656 kube.ContextAnnotation: "job-context", 657 kube.ProwJobTypeLabel: "presubmit", 658 kube.OrgLabel: "org", 659 kube.RepoLabel: "repo", 660 kube.BaseRefLabel: "main", 661 kube.PullLabel: "1", 662 }, 663 expectedAnnotations: map[string]string{ 664 kube.ProwJobAnnotation: "job", 665 kube.ContextAnnotation: "job-context", 666 }, 667 }, 668 { 669 name: "non-github presubmit job", 670 spec: prowapi.ProwJobSpec{ 671 Job: "job", 672 Context: "job-context", 673 Type: prowapi.PresubmitJob, 674 Refs: &prowapi.Refs{ 675 Org: "https://some-gerrit-instance.foo.com", 676 Repo: "some/invalid/repo", 677 BaseRef: "main", 678 Pulls: []prowapi.Pull{ 679 {Number: 1}, 680 }, 681 }, 682 }, 683 labels: map[string]string{}, 684 expectedLabels: map[string]string{ 685 kube.CreatedByProw: "true", 686 kube.ProwJobAnnotation: "job", 687 kube.ContextAnnotation: "job-context", 688 kube.ProwJobTypeLabel: "presubmit", 689 kube.OrgLabel: "some-gerrit-instance.foo.com", 690 kube.RepoLabel: "repo", 691 kube.BaseRefLabel: "main", 692 kube.PullLabel: "1", 693 }, 694 expectedAnnotations: map[string]string{ 695 kube.ProwJobAnnotation: "job", 696 kube.ContextAnnotation: "job-context", 697 }, 698 }, { 699 name: "job with name too long to fit in a label", 700 spec: prowapi.ProwJobSpec{ 701 Job: "job-created-by-someone-who-loves-very-very-very-long-names-so-long-that-it-does-not-fit-into-the-Kubernetes-label-so-it-needs-to-be-truncated-to-63-characters", 702 Context: "job-context", 703 Type: prowapi.PresubmitJob, 704 Refs: &prowapi.Refs{ 705 Org: "org", 706 Repo: "repo", 707 BaseRef: "main", 708 Pulls: []prowapi.Pull{ 709 {Number: 1}, 710 }, 711 }, 712 }, 713 labels: map[string]string{}, 714 expectedLabels: map[string]string{ 715 kube.CreatedByProw: "true", 716 kube.ProwJobAnnotation: "job-created-by-someone-who-loves-very-very-very-long-names-so-l", 717 kube.ContextAnnotation: "job-context", 718 kube.ProwJobTypeLabel: "presubmit", 719 kube.OrgLabel: "org", 720 kube.RepoLabel: "repo", 721 kube.BaseRefLabel: "main", 722 kube.PullLabel: "1", 723 }, 724 expectedAnnotations: map[string]string{ 725 kube.ContextAnnotation: "job-context", 726 kube.ProwJobAnnotation: "job-created-by-someone-who-loves-very-very-very-long-names-so-long-that-it-does-not-fit-into-the-Kubernetes-label-so-it-needs-to-be-truncated-to-63-characters", 727 }, 728 }, 729 { 730 name: "job with context too long to fit in a label", 731 spec: prowapi.ProwJobSpec{ 732 Job: "job", 733 Context: "context-created-by-someone-who-loves-very-very-very-long-names-so-long-that-it-does-not-fit-into-the-Kubernetes-label-so-it-needs-to-be-truncated-to-63-characters", 734 Type: prowapi.PresubmitJob, 735 Refs: &prowapi.Refs{ 736 Org: "org", 737 Repo: "repo", 738 BaseRef: "main", 739 Pulls: []prowapi.Pull{ 740 {Number: 1}, 741 }, 742 }, 743 }, 744 labels: map[string]string{}, 745 expectedLabels: map[string]string{ 746 kube.CreatedByProw: "true", 747 kube.ProwJobAnnotation: "job", 748 kube.ContextAnnotation: "context-created-by-someone-who-loves-very-very-very-long-names", 749 kube.ProwJobTypeLabel: "presubmit", 750 kube.OrgLabel: "org", 751 kube.RepoLabel: "repo", 752 kube.BaseRefLabel: "main", 753 kube.PullLabel: "1", 754 }, 755 expectedAnnotations: map[string]string{ 756 kube.ProwJobAnnotation: "job", 757 kube.ContextAnnotation: "context-created-by-someone-who-loves-very-very-very-long-names-so-long-that-it-does-not-fit-into-the-Kubernetes-label-so-it-needs-to-be-truncated-to-63-characters", 758 }, 759 }, 760 { 761 name: "periodic job, extra labels, extra annotations", 762 spec: prowapi.ProwJobSpec{ 763 Job: "job", 764 Context: "job-context", 765 Type: prowapi.PeriodicJob, 766 }, 767 labels: map[string]string{ 768 "extra": "stuff", 769 }, 770 annotations: map[string]string{ 771 "extraannotation": "foo", 772 }, 773 expectedLabels: map[string]string{ 774 kube.CreatedByProw: "true", 775 kube.ProwJobAnnotation: "job", 776 kube.ContextAnnotation: "job-context", 777 kube.ProwJobTypeLabel: "periodic", 778 "extra": "stuff", 779 }, 780 expectedAnnotations: map[string]string{ 781 kube.ProwJobAnnotation: "job", 782 kube.ContextAnnotation: "job-context", 783 "extraannotation": "foo", 784 }, 785 }, 786 { 787 name: "job with podspec", 788 spec: prowapi.ProwJobSpec{ 789 Job: "job", 790 Context: "job-context", 791 Type: prowapi.PeriodicJob, 792 PodSpec: &corev1.PodSpec{}, // Needed to catch race 793 }, 794 expectedLabels: map[string]string{ 795 kube.CreatedByProw: "true", 796 kube.ProwJobAnnotation: "job", 797 kube.ContextAnnotation: "job-context", 798 kube.ProwJobTypeLabel: "periodic", 799 }, 800 expectedAnnotations: map[string]string{ 801 kube.ProwJobAnnotation: "job", 802 kube.ContextAnnotation: "job-context", 803 }, 804 }, 805 } 806 for _, testCase := range testCases { 807 pj := NewProwJob(testCase.spec, testCase.labels, testCase.annotations) 808 if actual, expected := pj.Spec, testCase.spec; !equality.Semantic.DeepEqual(actual, expected) { 809 t.Errorf("%s: incorrect ProwJobSpec created: %s", testCase.name, diff.ObjectReflectDiff(actual, expected)) 810 } 811 if actual, expected := pj.Labels, testCase.expectedLabels; !reflect.DeepEqual(actual, expected) { 812 t.Errorf("%s: incorrect ProwJob labels created: %s", testCase.name, diff.ObjectReflectDiff(actual, expected)) 813 } 814 if actual, expected := pj.Annotations, testCase.expectedAnnotations; !reflect.DeepEqual(actual, expected) { 815 t.Errorf("%s: incorrect ProwJob annotations created: %s", testCase.name, diff.ObjectReflectDiff(actual, expected)) 816 } 817 if pj.Spec.PodSpec != nil { 818 futzWithPodSpec := func(spec *corev1.PodSpec, val string) { 819 if spec == nil { 820 return 821 } 822 if spec.NodeSelector == nil { 823 spec.NodeSelector = map[string]string{} 824 } 825 spec.NodeSelector["foo"] = val 826 for i := range spec.Containers { 827 c := &spec.Containers[i] 828 if c.Resources.Limits == nil { 829 c.Resources.Limits = corev1.ResourceList{} 830 } 831 if c.Resources.Requests == nil { 832 c.Resources.Requests = corev1.ResourceList{} 833 } 834 c.Resources.Limits[corev1.ResourceCPU] = resource.MustParse(val) 835 c.Resources.Requests[corev1.ResourceCPU] = resource.MustParse(val) 836 } 837 } 838 go futzWithPodSpec(pj.Spec.PodSpec, "12M") 839 futzWithPodSpec(testCase.spec.PodSpec, "34M") 840 } 841 } 842 } 843 844 func TestNewProwJobWithAnnotations(t *testing.T) { 845 var testCases = []struct { 846 name string 847 spec prowapi.ProwJobSpec 848 annotations map[string]string 849 expectedAnnotations map[string]string 850 }{ 851 { 852 name: "job without annotation", 853 spec: prowapi.ProwJobSpec{ 854 Job: "job", 855 Context: "job-context", 856 Type: prowapi.PeriodicJob, 857 }, 858 annotations: nil, 859 expectedAnnotations: map[string]string{ 860 kube.ProwJobAnnotation: "job", 861 kube.ContextAnnotation: "job-context", 862 }, 863 }, 864 { 865 name: "job with annotation", 866 spec: prowapi.ProwJobSpec{ 867 Job: "job", 868 Context: "job-context", 869 Type: prowapi.PeriodicJob, 870 }, 871 annotations: map[string]string{ 872 "annotation": "foo", 873 }, 874 expectedAnnotations: map[string]string{ 875 "annotation": "foo", 876 kube.ProwJobAnnotation: "job", 877 kube.ContextAnnotation: "job-context", 878 }, 879 }, 880 } 881 882 for _, testCase := range testCases { 883 pj := NewProwJob(testCase.spec, nil, testCase.annotations) 884 if actual, expected := pj.Spec, testCase.spec; !equality.Semantic.DeepEqual(actual, expected) { 885 t.Errorf("%s: incorrect ProwJobSpec created: %s", testCase.name, diff.ObjectReflectDiff(actual, expected)) 886 } 887 if actual, expected := pj.Annotations, testCase.expectedAnnotations; !reflect.DeepEqual(actual, expected) { 888 t.Errorf("%s: incorrect ProwJob labels created: %s", testCase.name, diff.ObjectReflectDiff(actual, expected)) 889 } 890 } 891 } 892 893 func TestNewProwJobWithModifiers(t *testing.T) { 894 stime, _ := time.Parse(time.DateOnly, "2024-02-27") 895 fakeStartTime := metav1.NewTime(stime) 896 fakeName := "foo" 897 898 for _, testCase := range []struct { 899 name string 900 spec *prowapi.ProwJobSpec 901 labels map[string]string 902 annotations map[string]string 903 options []Modifier 904 wantProwJob prowapi.ProwJob 905 }{ 906 { 907 name: "no options", 908 spec: &prowapi.ProwJobSpec{ 909 Job: "job", 910 Context: "job-context", 911 Type: prowapi.PeriodicJob, 912 }, 913 labels: map[string]string{"extra-label": "foo"}, 914 annotations: map[string]string{"extra-annotation": "bar"}, 915 wantProwJob: prowapi.ProwJob{ 916 TypeMeta: metav1.TypeMeta{ 917 APIVersion: "prow.k8s.io/v1", 918 Kind: "ProwJob", 919 }, 920 ObjectMeta: metav1.ObjectMeta{ 921 Name: fakeName, 922 Labels: map[string]string{ 923 kube.CreatedByProw: "true", 924 kube.ProwJobAnnotation: "job", 925 kube.ContextAnnotation: "job-context", 926 kube.ProwJobTypeLabel: string(prowapi.PeriodicJob), 927 "extra-label": "foo", 928 }, 929 Annotations: map[string]string{ 930 kube.ProwJobAnnotation: "job", 931 kube.ContextAnnotation: "job-context", 932 "extra-annotation": "bar", 933 }, 934 }, 935 Spec: prowapi.ProwJobSpec{ 936 Job: "job", 937 Context: "job-context", 938 Type: prowapi.PeriodicJob, 939 }, 940 Status: prowapi.ProwJobStatus{ 941 StartTime: fakeStartTime, 942 State: prowapi.TriggeredState, 943 }, 944 }, 945 }, 946 { 947 name: "require scheduling", 948 spec: &prowapi.ProwJobSpec{ 949 Job: "job", 950 Context: "job-context", 951 Type: prowapi.PeriodicJob, 952 }, 953 labels: map[string]string{"extra-label": "foo"}, 954 annotations: map[string]string{"extra-annotation": "bar"}, 955 options: []Modifier{RequireScheduling(true)}, 956 wantProwJob: prowapi.ProwJob{ 957 TypeMeta: metav1.TypeMeta{ 958 APIVersion: "prow.k8s.io/v1", 959 Kind: "ProwJob", 960 }, 961 ObjectMeta: metav1.ObjectMeta{ 962 Name: fakeName, 963 Labels: map[string]string{ 964 kube.CreatedByProw: "true", 965 kube.ProwJobAnnotation: "job", 966 kube.ContextAnnotation: "job-context", 967 kube.ProwJobTypeLabel: string(prowapi.PeriodicJob), 968 "extra-label": "foo", 969 }, 970 Annotations: map[string]string{ 971 kube.ProwJobAnnotation: "job", 972 kube.ContextAnnotation: "job-context", 973 "extra-annotation": "bar", 974 }, 975 }, 976 Spec: prowapi.ProwJobSpec{ 977 Job: "job", 978 Context: "job-context", 979 Type: prowapi.PeriodicJob, 980 }, 981 Status: prowapi.ProwJobStatus{ 982 StartTime: fakeStartTime, 983 State: prowapi.SchedulingState, 984 }, 985 }, 986 }, 987 } { 988 t.Run(testCase.name, func(t *testing.T) { 989 pj := NewProwJob(*testCase.spec, testCase.labels, testCase.annotations, testCase.options...) 990 pj.Name = fakeName 991 pj.Status.StartTime = fakeStartTime 992 993 if diff := cmp.Diff(&testCase.wantProwJob, &pj); diff != "" { 994 t.Errorf("prowjob doesn't match: %s", diff) 995 } 996 }) 997 } 998 } 999 1000 func TestNewPresubmitWithModifiers(t *testing.T) { 1001 stime, _ := time.Parse(time.DateOnly, "2024-02-27") 1002 fakeStartTime := metav1.NewTime(stime) 1003 fakeName := "foo" 1004 pr := github.PullRequest{ 1005 Number: 42, 1006 Title: "pr-title", 1007 HTMLURL: "https://github.example.com/kubernetes/test-infra/pull/42", 1008 Head: github.PullRequestBranch{ 1009 SHA: "123456", 1010 Ref: "head-ref", 1011 }, 1012 Base: github.PullRequestBranch{ 1013 Ref: "base-ref", 1014 Repo: github.Repo{ 1015 Name: "repo-name", 1016 HTMLURL: "https://github.example.com/kubernetes/test-infra", 1017 Owner: github.User{ 1018 Login: "kubernetes", 1019 }, 1020 }, 1021 }, 1022 User: github.User{ 1023 Login: "user-login", 1024 HTMLURL: "https://github.example.com/user-login", 1025 }, 1026 } 1027 1028 for _, testCase := range []struct { 1029 name string 1030 spec *prowapi.ProwJobSpec 1031 labels map[string]string 1032 pr *github.PullRequest 1033 baseSHA string 1034 job *config.Presubmit 1035 eventGUID string 1036 options []Modifier 1037 wantProwJob prowapi.ProwJob 1038 }{ 1039 { 1040 name: "no options", 1041 labels: map[string]string{"extra-label": "foo"}, 1042 pr: &pr, 1043 baseSHA: "987654", 1044 job: &config.Presubmit{ 1045 JobBase: config.JobBase{Name: "job"}, 1046 Reporter: config.Reporter{Context: "job-context"}, 1047 }, 1048 eventGUID: "0001", 1049 wantProwJob: prowapi.ProwJob{ 1050 TypeMeta: metav1.TypeMeta{ 1051 APIVersion: "prow.k8s.io/v1", 1052 Kind: "ProwJob", 1053 }, 1054 ObjectMeta: metav1.ObjectMeta{ 1055 Name: fakeName, 1056 Labels: map[string]string{ 1057 kube.CreatedByProw: "true", 1058 kube.ProwJobAnnotation: "job", 1059 kube.ContextAnnotation: "job-context", 1060 kube.ProwJobTypeLabel: string(prowapi.PresubmitJob), 1061 github.EventGUID: "0001", 1062 kube.IsOptionalLabel: "false", 1063 kube.OrgLabel: "kubernetes", 1064 kube.RepoLabel: "repo-name", 1065 kube.BaseRefLabel: "base-ref", 1066 kube.PullLabel: "42", 1067 "extra-label": "foo", 1068 }, 1069 Annotations: map[string]string{ 1070 kube.ProwJobAnnotation: "job", 1071 kube.ContextAnnotation: "job-context", 1072 }, 1073 }, 1074 Spec: prowapi.ProwJobSpec{ 1075 Job: "job", 1076 Context: "job-context", 1077 Type: prowapi.PresubmitJob, 1078 Report: true, 1079 Refs: &prowapi.Refs{ 1080 Org: "kubernetes", 1081 Repo: "repo-name", 1082 BaseSHA: "987654", 1083 RepoLink: "https://github.example.com/kubernetes/test-infra", 1084 BaseRef: "base-ref", 1085 BaseLink: "https://github.example.com/kubernetes/test-infra/commit/987654", 1086 Pulls: []prowapi.Pull{{ 1087 Number: 42, 1088 Author: "user-login", 1089 SHA: "123456", 1090 Title: "pr-title", 1091 HeadRef: "head-ref", 1092 Link: "https://github.example.com/kubernetes/test-infra/pull/42", 1093 CommitLink: "https://github.example.com/kubernetes/test-infra/pull/42/commits/123456", 1094 AuthorLink: "https://github.example.com/user-login", 1095 }}, 1096 }, 1097 }, 1098 Status: prowapi.ProwJobStatus{ 1099 StartTime: fakeStartTime, 1100 State: prowapi.TriggeredState, 1101 }, 1102 }, 1103 }, 1104 { 1105 name: "require scheduling", 1106 labels: map[string]string{"extra-label": "foo"}, 1107 pr: &pr, 1108 baseSHA: "987654", 1109 job: &config.Presubmit{ 1110 JobBase: config.JobBase{Name: "job"}, 1111 Reporter: config.Reporter{Context: "job-context"}, 1112 }, 1113 eventGUID: "0001", 1114 options: []Modifier{RequireScheduling(true)}, 1115 wantProwJob: prowapi.ProwJob{ 1116 TypeMeta: metav1.TypeMeta{ 1117 APIVersion: "prow.k8s.io/v1", 1118 Kind: "ProwJob", 1119 }, 1120 ObjectMeta: metav1.ObjectMeta{ 1121 Name: fakeName, 1122 Labels: map[string]string{ 1123 kube.CreatedByProw: "true", 1124 kube.ProwJobAnnotation: "job", 1125 kube.ContextAnnotation: "job-context", 1126 kube.ProwJobTypeLabel: string(prowapi.PresubmitJob), 1127 github.EventGUID: "0001", 1128 kube.IsOptionalLabel: "false", 1129 kube.OrgLabel: "kubernetes", 1130 kube.RepoLabel: "repo-name", 1131 kube.BaseRefLabel: "base-ref", 1132 kube.PullLabel: "42", 1133 "extra-label": "foo", 1134 }, 1135 Annotations: map[string]string{ 1136 kube.ProwJobAnnotation: "job", 1137 kube.ContextAnnotation: "job-context", 1138 }, 1139 }, 1140 Spec: prowapi.ProwJobSpec{ 1141 Job: "job", 1142 Context: "job-context", 1143 Type: prowapi.PresubmitJob, 1144 Report: true, 1145 Refs: &prowapi.Refs{ 1146 Org: "kubernetes", 1147 Repo: "repo-name", 1148 BaseSHA: "987654", 1149 RepoLink: "https://github.example.com/kubernetes/test-infra", 1150 BaseRef: "base-ref", 1151 BaseLink: "https://github.example.com/kubernetes/test-infra/commit/987654", 1152 Pulls: []prowapi.Pull{{ 1153 Number: 42, 1154 Author: "user-login", 1155 SHA: "123456", 1156 Title: "pr-title", 1157 HeadRef: "head-ref", 1158 Link: "https://github.example.com/kubernetes/test-infra/pull/42", 1159 CommitLink: "https://github.example.com/kubernetes/test-infra/pull/42/commits/123456", 1160 AuthorLink: "https://github.example.com/user-login", 1161 }}, 1162 }, 1163 }, 1164 Status: prowapi.ProwJobStatus{ 1165 StartTime: fakeStartTime, 1166 State: prowapi.SchedulingState, 1167 }, 1168 }, 1169 }, 1170 } { 1171 t.Run(testCase.name, func(t *testing.T) { 1172 pj := NewPresubmit(*testCase.pr, testCase.baseSHA, *testCase.job, testCase.eventGUID, testCase.labels, testCase.options...) 1173 pj.Name = fakeName 1174 pj.Status.StartTime = fakeStartTime 1175 1176 if diff := cmp.Diff(&testCase.wantProwJob, &pj); diff != "" { 1177 t.Errorf("prowjob doesn't match: %s", diff) 1178 } 1179 }) 1180 } 1181 } 1182 1183 func TestJobURL(t *testing.T) { 1184 var testCases = []struct { 1185 name string 1186 plank config.Plank 1187 pj prowapi.ProwJob 1188 expected string 1189 expectedErr string 1190 }{ 1191 { 1192 name: "non-decorated job uses template", 1193 plank: config.Plank{ 1194 Controller: config.Controller{ 1195 JobURLTemplate: template.Must(template.New("test").Parse("{{.Spec.Type}}")), 1196 }, 1197 }, 1198 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{Type: prowapi.PeriodicJob}}, 1199 expected: "periodic", 1200 }, 1201 { 1202 name: "non-decorated job with broken template gives empty string", 1203 plank: config.Plank{ 1204 Controller: config.Controller{ 1205 JobURLTemplate: template.Must(template.New("test").Parse("{{.Garbage}}")), 1206 }, 1207 }, 1208 pj: prowapi.ProwJob{}, 1209 expected: "", 1210 }, 1211 { 1212 name: "decorated job without prefix uses template", 1213 plank: config.Plank{ 1214 Controller: config.Controller{ 1215 JobURLTemplate: template.Must(template.New("test").Parse("{{.Spec.Type}}")), 1216 }, 1217 }, 1218 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{Type: prowapi.PeriodicJob}}, 1219 expected: "periodic", 1220 }, 1221 { 1222 name: "decorated job with prefix uses gcsupload", 1223 plank: config.Plank{ 1224 JobURLPrefixConfig: map[string]string{"*": "https://gubernator.com/build"}, 1225 JobURLPrefixDisableAppendStorageProvider: true, 1226 }, 1227 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{ 1228 Type: prowapi.PresubmitJob, 1229 Refs: &prowapi.Refs{ 1230 Org: "org", 1231 Repo: "repo", 1232 Pulls: []prowapi.Pull{{Number: 1}}, 1233 }, 1234 DecorationConfig: &prowapi.DecorationConfig{GCSConfiguration: &prowapi.GCSConfiguration{ 1235 Bucket: "bucket", 1236 PathStrategy: prowapi.PathStrategyExplicit, 1237 }}, 1238 }}, 1239 expected: "https://gubernator.com/build/bucket/pr-logs/pull/org_repo/1", 1240 }, 1241 { 1242 name: "decorated job with prefix uses gcsupload and new bucket format with gcs (new job url format)", 1243 plank: config.Plank{ 1244 JobURLPrefixConfig: map[string]string{"*": "https://prow.k8s.io/view/"}, 1245 }, 1246 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{ 1247 Type: prowapi.PresubmitJob, 1248 Refs: &prowapi.Refs{ 1249 Org: "org", 1250 Repo: "repo", 1251 Pulls: []prowapi.Pull{{Number: 1}}, 1252 }, 1253 DecorationConfig: &prowapi.DecorationConfig{GCSConfiguration: &prowapi.GCSConfiguration{ 1254 Bucket: "gs://bucket", 1255 PathStrategy: prowapi.PathStrategyExplicit, 1256 }}, 1257 }}, 1258 expected: "https://prow.k8s.io/view/gs/bucket/pr-logs/pull/org_repo/1", 1259 }, 1260 { 1261 name: "decorated job with prefix uses gcsupload and new bucket format with s3", 1262 plank: config.Plank{ 1263 JobURLPrefixConfig: map[string]string{"*": "https://prow.k8s.io/view/"}, 1264 }, 1265 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{ 1266 Type: prowapi.PresubmitJob, 1267 Refs: &prowapi.Refs{ 1268 Org: "org", 1269 Repo: "repo", 1270 Pulls: []prowapi.Pull{{Number: 1}}, 1271 }, 1272 DecorationConfig: &prowapi.DecorationConfig{GCSConfiguration: &prowapi.GCSConfiguration{ 1273 Bucket: "s3://bucket", 1274 PathStrategy: prowapi.PathStrategyExplicit, 1275 }}, 1276 }}, 1277 expected: "https://prow.k8s.io/view/s3/bucket/pr-logs/pull/org_repo/1", 1278 }, 1279 { 1280 name: "decorated job with prefix uses gcsupload with valid bucket with multiple separators", 1281 plank: config.Plank{ 1282 JobURLPrefixConfig: map[string]string{"*": "https://prow.k8s.io/view/"}, 1283 }, 1284 pj: prowapi.ProwJob{Spec: prowapi.ProwJobSpec{ 1285 Type: prowapi.PresubmitJob, 1286 Refs: &prowapi.Refs{ 1287 Org: "org", 1288 Repo: "repo", 1289 Pulls: []prowapi.Pull{{Number: 1}}, 1290 }, 1291 DecorationConfig: &prowapi.DecorationConfig{GCSConfiguration: &prowapi.GCSConfiguration{ 1292 Bucket: "gs://my-floppy-backup/a://doom2.wad.006", 1293 PathStrategy: prowapi.PathStrategyExplicit, 1294 }}, 1295 }}, 1296 expected: "https://prow.k8s.io/view/gs/my-floppy-backup/a:/doom2.wad.006/pr-logs/pull/org_repo/1", 1297 }, 1298 } 1299 1300 logger := logrus.New() 1301 for _, testCase := range testCases { 1302 t.Run(testCase.name, func(t *testing.T) { 1303 actual, actualErr := JobURL(testCase.plank, testCase.pj, logger.WithField("name", testCase.name)) 1304 var actualErrStr string 1305 if actualErr != nil { 1306 actualErrStr = actualErr.Error() 1307 } 1308 1309 if actualErrStr != testCase.expectedErr { 1310 t.Errorf("%s: expectedErr = %v, but got %v", testCase.name, testCase.expectedErr, actualErrStr) 1311 } 1312 if actual != testCase.expected { 1313 t.Errorf("%s: expected URL to be %q but got %q", testCase.name, testCase.expected, actual) 1314 } 1315 }) 1316 } 1317 } 1318 1319 func TestSetReportDefault(t *testing.T) { 1320 tests := []struct { 1321 name string 1322 spec *prowapi.ProwJobSpec 1323 wantSpec *prowapi.ProwJobSpec 1324 }{ 1325 { 1326 name: "base-case", 1327 spec: &prowapi.ProwJobSpec{ 1328 ReporterConfig: &prowapi.ReporterConfig{ 1329 Slack: &prowapi.SlackReporterConfig{ 1330 JobStatesToReport: []prowapi.ProwJobState{}, 1331 }, 1332 }, 1333 }, 1334 wantSpec: &prowapi.ProwJobSpec{ 1335 ReporterConfig: &prowapi.ReporterConfig{ 1336 Slack: &prowapi.SlackReporterConfig{ 1337 JobStatesToReport: []prowapi.ProwJobState{}, 1338 Report: boolPtr(false), 1339 }, 1340 }, 1341 }, 1342 }, 1343 { 1344 name: "to-report", 1345 spec: &prowapi.ProwJobSpec{ 1346 ReporterConfig: &prowapi.ReporterConfig{ 1347 Slack: &prowapi.SlackReporterConfig{ 1348 JobStatesToReport: []prowapi.ProwJobState{prowapi.AbortedState}, 1349 }, 1350 }, 1351 }, 1352 wantSpec: &prowapi.ProwJobSpec{ 1353 ReporterConfig: &prowapi.ReporterConfig{ 1354 Slack: &prowapi.SlackReporterConfig{ 1355 JobStatesToReport: []prowapi.ProwJobState{prowapi.AbortedState}, 1356 Report: boolPtr(true), 1357 }, 1358 }, 1359 }, 1360 }, 1361 { 1362 name: "no-slack-report", 1363 spec: &prowapi.ProwJobSpec{ 1364 ReporterConfig: &prowapi.ReporterConfig{}, 1365 }, 1366 wantSpec: &prowapi.ProwJobSpec{ 1367 ReporterConfig: &prowapi.ReporterConfig{}, 1368 }, 1369 }, 1370 } 1371 1372 for _, tc := range tests { 1373 t.Run(tc.name, func(t *testing.T) { 1374 setReportDefault(tc.spec) 1375 if diff := cmp.Diff(tc.wantSpec, tc.spec); diff != "" { 1376 t.Fatalf("Spec mismatch. Want(-), got(+):\n%s", diff) 1377 } 1378 }) 1379 } 1380 } 1381 1382 func TestCreateRefs(t *testing.T) { 1383 pr := github.PullRequest{ 1384 Number: 42, 1385 Title: "hello world", 1386 HTMLURL: "https://github.example.com/kubernetes/Hello-World/pull/42", 1387 Head: github.PullRequestBranch{ 1388 SHA: "123456", 1389 Ref: "my-great-change", 1390 }, 1391 Base: github.PullRequestBranch{ 1392 Ref: "master", 1393 Repo: github.Repo{ 1394 Name: "Hello-World", 1395 HTMLURL: "https://github.example.com/kubernetes/Hello-World", 1396 Owner: github.User{ 1397 Login: "kubernetes", 1398 }, 1399 }, 1400 }, 1401 User: github.User{ 1402 Login: "ibzib", 1403 HTMLURL: "https://github.example.com/ibzib", 1404 }, 1405 } 1406 expected := prowapi.Refs{ 1407 Org: "kubernetes", 1408 Repo: "Hello-World", 1409 RepoLink: "https://github.example.com/kubernetes/Hello-World", 1410 BaseRef: "master", 1411 BaseSHA: "abcdef", 1412 BaseLink: "https://github.example.com/kubernetes/Hello-World/commit/abcdef", 1413 Pulls: []prowapi.Pull{ 1414 { 1415 Number: 42, 1416 Author: "ibzib", 1417 SHA: "123456", 1418 HeadRef: "my-great-change", 1419 Title: "hello world", 1420 Link: "https://github.example.com/kubernetes/Hello-World/pull/42", 1421 AuthorLink: "https://github.example.com/ibzib", 1422 CommitLink: "https://github.example.com/kubernetes/Hello-World/pull/42/commits/123456", 1423 }, 1424 }, 1425 } 1426 if actual := createRefs(pr, "abcdef"); !reflect.DeepEqual(expected, actual) { 1427 t.Errorf("diff between expected and actual refs:%s", diff.ObjectReflectDiff(expected, actual)) 1428 } 1429 } 1430 1431 func TestSpecFromJobBase(t *testing.T) { 1432 permittedGroups := []int{1234, 5678} 1433 permittedUsers := []string{"authorized_user", "another_authorized_user"} 1434 permittedOrgs := []string{"kubernetes", "kubernetes-sigs"} 1435 rerunAuthConfig := prowapi.RerunAuthConfig{ 1436 AllowAnyone: false, 1437 GitHubTeamIDs: permittedGroups, 1438 GitHubUsers: permittedUsers, 1439 GitHubOrgs: permittedOrgs, 1440 } 1441 testCases := []struct { 1442 name string 1443 jobBase config.JobBase 1444 verify func(prowapi.ProwJobSpec) error 1445 }{ 1446 { 1447 name: "Verify reporter config gets copied", 1448 jobBase: config.JobBase{ 1449 ReporterConfig: &prowapi.ReporterConfig{ 1450 Slack: &prowapi.SlackReporterConfig{ 1451 Channel: "my-channel", 1452 }, 1453 }, 1454 }, 1455 verify: func(pj prowapi.ProwJobSpec) error { 1456 if pj.ReporterConfig == nil { 1457 return errors.New("Expected ReporterConfig to be non-nil") 1458 } 1459 if pj.ReporterConfig.Slack == nil { 1460 return errors.New("Expected ReporterConfig.Slack to be non-nil") 1461 } 1462 if pj.ReporterConfig.Slack.Channel != "my-channel" { 1463 return fmt.Errorf("Expected pj.ReporterConfig.Slack.Channel to be \"my-channel\", was %q", 1464 pj.ReporterConfig.Slack.Channel) 1465 } 1466 return nil 1467 }, 1468 }, 1469 { 1470 name: "Verify rerun permissions gets copied", 1471 jobBase: config.JobBase{ 1472 RerunAuthConfig: &rerunAuthConfig, 1473 }, 1474 verify: func(pj prowapi.ProwJobSpec) error { 1475 if pj.RerunAuthConfig.AllowAnyone { 1476 return errors.New("Expected RerunAuthConfig.AllowAnyone to be false") 1477 } 1478 if pj.RerunAuthConfig.GitHubTeamIDs == nil { 1479 return errors.New("Expected RerunAuthConfig.GitHubTeamIDs to be non-nil") 1480 } 1481 if pj.RerunAuthConfig.GitHubUsers == nil { 1482 return errors.New("Expected RerunAuthConfig.GitHubUsers to be non-nil") 1483 } 1484 if pj.RerunAuthConfig.GitHubOrgs == nil { 1485 return errors.New("Expected RerunAuthConfig.GitHubOrgs to be non-nil") 1486 } 1487 return nil 1488 }, 1489 }, 1490 { 1491 name: "Verify hidden property gets copied", 1492 jobBase: config.JobBase{ 1493 Hidden: true, 1494 }, 1495 verify: func(pj prowapi.ProwJobSpec) error { 1496 if !pj.Hidden { 1497 return errors.New("hidden property didnt get copied") 1498 } 1499 return nil 1500 }, 1501 }, 1502 { 1503 name: "Verify DecorateExtraRefs default true", 1504 jobBase: config.JobBase{ 1505 UtilityConfig: config.UtilityConfig{ 1506 DecorationConfig: &prowapi.DecorationConfig{ 1507 BloblessFetch: boolPtr(true), 1508 }, 1509 ExtraRefs: []prowapi.Refs{ 1510 { 1511 Org: "true-org", 1512 BloblessFetch: boolPtr(true), 1513 }, 1514 { 1515 Org: "false-org", 1516 BloblessFetch: boolPtr(false), 1517 }, 1518 { 1519 Org: "default-org", 1520 BloblessFetch: nil, 1521 }, 1522 }, 1523 }, 1524 }, 1525 verify: func(pj prowapi.ProwJobSpec) error { 1526 for _, r := range pj.ExtraRefs { 1527 got := r.BloblessFetch 1528 want := boolPtr(r.Org != "false-org") 1529 if diff := cmp.Diff(want, got); diff != "" { 1530 return fmt.Errorf("ExtraRefs BloblessFetch for %s differs (-want +got)\n%s", r.Org, diff) 1531 } 1532 } 1533 return nil 1534 }, 1535 }, 1536 { 1537 name: "Verify DecorateExtraRefs default false", 1538 jobBase: config.JobBase{ 1539 UtilityConfig: config.UtilityConfig{ 1540 DecorationConfig: &prowapi.DecorationConfig{ 1541 BloblessFetch: boolPtr(false), 1542 }, 1543 ExtraRefs: []prowapi.Refs{ 1544 { 1545 Org: "true-org", 1546 BloblessFetch: boolPtr(true), 1547 }, 1548 { 1549 Org: "false-org", 1550 BloblessFetch: boolPtr(false), 1551 }, 1552 { 1553 Org: "default-org", 1554 BloblessFetch: nil, 1555 }, 1556 }, 1557 }, 1558 }, 1559 verify: func(pj prowapi.ProwJobSpec) error { 1560 for _, r := range pj.ExtraRefs { 1561 got := r.BloblessFetch 1562 want := boolPtr(r.Org == "true-org") 1563 if diff := cmp.Diff(want, got); diff != "" { 1564 return fmt.Errorf("ExtraRefs BloblessFetch for %s differs (-want +got)\n%s", r.Org, diff) 1565 } 1566 } 1567 return nil 1568 }, 1569 }, 1570 { 1571 name: "Verify DecorateExtraRefs no decoration preserves existing", 1572 jobBase: config.JobBase{ 1573 UtilityConfig: config.UtilityConfig{ 1574 ExtraRefs: []prowapi.Refs{ 1575 { 1576 Org: "true-org", 1577 BloblessFetch: boolPtr(true), 1578 }, 1579 { 1580 Org: "false-org", 1581 BloblessFetch: boolPtr(false), 1582 }, 1583 { 1584 Org: "default-org", 1585 BloblessFetch: nil, 1586 }, 1587 }, 1588 }, 1589 }, 1590 verify: func(pj prowapi.ProwJobSpec) error { 1591 for _, r := range pj.ExtraRefs { 1592 got := r.BloblessFetch 1593 var want *bool 1594 switch r.Org { 1595 case "true-org": 1596 want = boolPtr(true) 1597 case "false-org": 1598 want = boolPtr(false) 1599 case "default-org": 1600 // Keep the unset default value. 1601 want = nil 1602 } 1603 if diff := cmp.Diff(want, got); diff != "" { 1604 return fmt.Errorf("ExtraRefs BloblessFetch for %s differs (-want +got)\n%s", r.Org, diff) 1605 } 1606 } 1607 return nil 1608 }, 1609 }, 1610 } 1611 1612 for _, tc := range testCases { 1613 t.Run(tc.name, func(t *testing.T) { 1614 pj := specFromJobBase(tc.jobBase) 1615 if err := tc.verify(pj); err != nil { 1616 t.Fatalf("Verification failed: %v", err) 1617 } 1618 }) 1619 } 1620 } 1621 1622 func TestPeriodicSpec(t *testing.T) { 1623 testCases := []struct { 1624 name string 1625 config config.Periodic 1626 verify func(prowapi.ProwJobSpec) error 1627 }{ 1628 { 1629 name: "Report gets set to true", 1630 config: config.Periodic{}, 1631 verify: func(p prowapi.ProwJobSpec) error { 1632 if !p.Report { 1633 return errors.New("report is not true") 1634 } 1635 return nil 1636 }, 1637 }, 1638 } 1639 1640 for _, tc := range testCases { 1641 if err := tc.verify(PeriodicSpec(tc.config)); err != nil { 1642 t.Error(err) 1643 } 1644 } 1645 }