github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/updateconfig/updateconfig_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 updateconfig 18 19 import ( 20 "fmt" 21 "strings" 22 "testing" 23 24 "github.com/sirupsen/logrus" 25 "k8s.io/apimachinery/pkg/api/equality" 26 "k8s.io/apimachinery/pkg/util/diff" 27 28 "k8s.io/test-infra/prow/github" 29 "k8s.io/test-infra/prow/github/fakegithub" 30 "k8s.io/test-infra/prow/kube" 31 "k8s.io/test-infra/prow/plugins" 32 ) 33 34 const defaultNamespace = "default" 35 36 type fakeKubeClient struct { 37 maps map[string]kube.ConfigMap 38 updatedMaps []string 39 createdMaps []string 40 } 41 42 func (c *fakeKubeClient) GetConfigMap(name, namespace string) (kube.ConfigMap, error) { 43 data, exists := c.maps[name] 44 var err error 45 if !exists { 46 err = kube.NotFoundError{} 47 } 48 return data, err 49 } 50 51 func (c *fakeKubeClient) ReplaceConfigMap(name string, config kube.ConfigMap) (kube.ConfigMap, error) { 52 if config.ObjectMeta.Name != name { 53 return kube.ConfigMap{}, fmt.Errorf("name %s does not match configmap name %s", name, config.ObjectMeta.Name) 54 } 55 if config.Namespace == "" { 56 config.Namespace = defaultNamespace 57 } 58 if _, exists := c.maps[name]; !exists { 59 return kube.ConfigMap{}, fmt.Errorf("called update on non-existent configmap %s", name) 60 } 61 c.maps[name] = config 62 c.updatedMaps = append(c.updatedMaps, name) 63 return c.maps[name], nil 64 } 65 66 func (c *fakeKubeClient) CreateConfigMap(content kube.ConfigMap) (kube.ConfigMap, error) { 67 if content.Namespace == "" { 68 content.Namespace = defaultNamespace 69 } 70 c.maps[content.Name] = content 71 c.createdMaps = append(c.createdMaps, content.Name) 72 return c.maps[content.Name], nil 73 } 74 75 func TestUpdateConfig(t *testing.T) { 76 basicPR := github.PullRequest{ 77 Number: 1, 78 Base: github.PullRequestBranch{ 79 Repo: github.Repo{ 80 Owner: github.User{ 81 Login: "kubernetes", 82 }, 83 Name: "kubernetes", 84 }, 85 }, 86 User: github.User{ 87 Login: "foo", 88 }, 89 } 90 91 testcases := []struct { 92 name string 93 prAction github.PullRequestEventAction 94 merged bool 95 mergeCommit string 96 changes []github.PullRequestChange 97 existConfigMaps map[string]kube.ConfigMap 98 expectedConfigMaps map[string]kube.ConfigMap 99 }{ 100 { 101 name: "Opened PR, no update", 102 prAction: github.PullRequestActionOpened, 103 merged: false, 104 changes: []github.PullRequestChange{ 105 { 106 Filename: "prow/config.yaml", 107 Additions: 1, 108 }, 109 }, 110 existConfigMaps: map[string]kube.ConfigMap{}, 111 }, 112 { 113 name: "Opened PR, not merged, no update", 114 merged: false, 115 changes: []github.PullRequestChange{ 116 { 117 Filename: "prow/config.yaml", 118 Additions: 1, 119 }, 120 }, 121 existConfigMaps: map[string]kube.ConfigMap{}, 122 }, 123 { 124 name: "Closed PR, no prow changes, no update", 125 prAction: github.PullRequestActionClosed, 126 merged: false, 127 changes: []github.PullRequestChange{ 128 { 129 Filename: "foo.txt", 130 Additions: 1, 131 }, 132 }, 133 existConfigMaps: map[string]kube.ConfigMap{}, 134 }, 135 { 136 name: "For whatever reason no merge commit SHA", 137 prAction: github.PullRequestActionClosed, 138 merged: true, 139 changes: []github.PullRequestChange{ 140 { 141 Filename: "prow/config.yaml", 142 Additions: 1, 143 }, 144 }, 145 existConfigMaps: map[string]kube.ConfigMap{}, 146 }, 147 { 148 name: "changed config.yaml, 1 update", 149 prAction: github.PullRequestActionClosed, 150 merged: true, 151 mergeCommit: "12345", 152 changes: []github.PullRequestChange{ 153 { 154 Filename: "prow/config.yaml", 155 Additions: 1, 156 }, 157 }, 158 existConfigMaps: map[string]kube.ConfigMap{ 159 "config": { 160 ObjectMeta: kube.ObjectMeta{ 161 Name: "config", 162 Namespace: defaultNamespace, 163 }, 164 Data: map[string]string{ 165 "config.yaml": "old-config", 166 }, 167 }, 168 }, 169 expectedConfigMaps: map[string]kube.ConfigMap{ 170 "config": { 171 ObjectMeta: kube.ObjectMeta{ 172 Name: "config", 173 Namespace: defaultNamespace, 174 }, 175 Data: map[string]string{ 176 "config.yaml": "new-config", 177 }, 178 }, 179 }, 180 }, 181 { 182 name: "changed config.yaml, existed configmap, 1 update", 183 prAction: github.PullRequestActionClosed, 184 merged: true, 185 mergeCommit: "12345", 186 changes: []github.PullRequestChange{ 187 { 188 Filename: "prow/config.yaml", 189 Additions: 1, 190 }, 191 }, 192 existConfigMaps: map[string]kube.ConfigMap{ 193 "config": { 194 ObjectMeta: kube.ObjectMeta{ 195 Name: "config", 196 Namespace: defaultNamespace, 197 }, 198 Data: map[string]string{ 199 "config.yaml": "old-config", 200 }, 201 }, 202 }, 203 expectedConfigMaps: map[string]kube.ConfigMap{ 204 "config": { 205 ObjectMeta: kube.ObjectMeta{ 206 Name: "config", 207 Namespace: defaultNamespace, 208 }, 209 Data: map[string]string{ 210 "config.yaml": "new-config", 211 }, 212 }, 213 }, 214 }, 215 { 216 name: "changed plugins.yaml, 1 update with custom key", 217 prAction: github.PullRequestActionClosed, 218 merged: true, 219 mergeCommit: "12345", 220 changes: []github.PullRequestChange{ 221 { 222 Filename: "prow/plugins.yaml", 223 Additions: 1, 224 }, 225 }, 226 existConfigMaps: map[string]kube.ConfigMap{ 227 "plugins": { 228 ObjectMeta: kube.ObjectMeta{ 229 Name: "plugins", 230 Namespace: defaultNamespace, 231 }, 232 Data: map[string]string{ 233 "test-key": "old-plugins", 234 }, 235 }, 236 }, 237 expectedConfigMaps: map[string]kube.ConfigMap{ 238 "plugins": { 239 ObjectMeta: kube.ObjectMeta{ 240 Name: "plugins", 241 Namespace: defaultNamespace, 242 }, 243 Data: map[string]string{ 244 "test-key": "new-plugins", 245 }, 246 }, 247 }, 248 }, 249 { 250 name: "changed resources.yaml, 1 update with custom namespace", 251 prAction: github.PullRequestActionClosed, 252 merged: true, 253 mergeCommit: "12345", 254 changes: []github.PullRequestChange{ 255 { 256 Filename: "boskos/resources.yaml", 257 Additions: 1, 258 }, 259 }, 260 existConfigMaps: map[string]kube.ConfigMap{ 261 "boskos-config": { 262 ObjectMeta: kube.ObjectMeta{ 263 Name: "boskos-config", 264 Namespace: "boskos", 265 }, 266 Data: map[string]string{ 267 "resources.yaml": "old-boskos-config", 268 }, 269 }, 270 }, 271 expectedConfigMaps: map[string]kube.ConfigMap{ 272 "boskos-config": { 273 ObjectMeta: kube.ObjectMeta{ 274 Name: "boskos-config", 275 Namespace: "boskos", 276 }, 277 Data: map[string]string{ 278 "resources.yaml": "new-boskos-config", 279 }, 280 }, 281 }, 282 }, 283 { 284 name: "changed config.yaml, plugins.yaml and resources.yaml, 3 update", 285 prAction: github.PullRequestActionClosed, 286 merged: true, 287 mergeCommit: "12345", 288 changes: []github.PullRequestChange{ 289 { 290 Filename: "prow/plugins.yaml", 291 Additions: 1, 292 }, 293 { 294 Filename: "prow/config.yaml", 295 Additions: 1, 296 }, 297 { 298 Filename: "boskos/resources.yaml", 299 Additions: 1, 300 }, 301 }, 302 existConfigMaps: map[string]kube.ConfigMap{ 303 "config": { 304 ObjectMeta: kube.ObjectMeta{ 305 Name: "config", 306 Namespace: defaultNamespace, 307 }, 308 Data: map[string]string{ 309 "config.yaml": "old-config", 310 }, 311 }, 312 "plugins": { 313 ObjectMeta: kube.ObjectMeta{ 314 Name: "plugins", 315 Namespace: defaultNamespace, 316 }, 317 Data: map[string]string{ 318 "test-key": "old-plugins", 319 }, 320 }, 321 "boskos-config": { 322 ObjectMeta: kube.ObjectMeta{ 323 Name: "boskos-config", 324 Namespace: "boskos", 325 }, 326 Data: map[string]string{ 327 "resources.yaml": "old-boskos-config", 328 }, 329 }, 330 }, 331 expectedConfigMaps: map[string]kube.ConfigMap{ 332 "config": { 333 ObjectMeta: kube.ObjectMeta{ 334 Name: "config", 335 Namespace: defaultNamespace, 336 }, 337 Data: map[string]string{ 338 "config.yaml": "new-config", 339 }, 340 }, 341 "plugins": { 342 ObjectMeta: kube.ObjectMeta{ 343 Name: "plugins", 344 Namespace: defaultNamespace, 345 }, 346 Data: map[string]string{ 347 "test-key": "new-plugins", 348 }, 349 }, 350 "boskos-config": { 351 ObjectMeta: kube.ObjectMeta{ 352 Name: "boskos-config", 353 Namespace: "boskos", 354 }, 355 Data: map[string]string{ 356 "resources.yaml": "new-boskos-config", 357 }, 358 }, 359 }, 360 }, 361 { 362 name: "edited both config/foo.yaml and config/bar.yaml, 2 update", 363 prAction: github.PullRequestActionClosed, 364 merged: true, 365 mergeCommit: "12345", 366 changes: []github.PullRequestChange{ 367 { 368 Filename: "config/foo.yaml", 369 Additions: 1, 370 }, 371 { 372 Filename: "config/bar.yaml", 373 Additions: 1, 374 }, 375 }, 376 existConfigMaps: map[string]kube.ConfigMap{ 377 "multikey-config": { 378 ObjectMeta: kube.ObjectMeta{ 379 Name: "multikey-config", 380 Namespace: defaultNamespace, 381 }, 382 Data: map[string]string{ 383 "foo.yaml": "old-foo-config", 384 "bar.yaml": "old-bar-config", 385 }, 386 }, 387 }, 388 expectedConfigMaps: map[string]kube.ConfigMap{ 389 "multikey-config": { 390 ObjectMeta: kube.ObjectMeta{ 391 Name: "multikey-config", 392 Namespace: defaultNamespace, 393 }, 394 Data: map[string]string{ 395 "foo.yaml": "new-foo-config", 396 "bar.yaml": "new-bar-config", 397 }, 398 }, 399 }, 400 }, 401 { 402 name: "edited config/foo.yaml, 1 update", 403 prAction: github.PullRequestActionClosed, 404 merged: true, 405 mergeCommit: "12345", 406 changes: []github.PullRequestChange{ 407 { 408 Filename: "config/foo.yaml", 409 Status: "modified", 410 Additions: 1, 411 }, 412 }, 413 existConfigMaps: map[string]kube.ConfigMap{ 414 "unaffected-config": { 415 ObjectMeta: kube.ObjectMeta{ 416 Name: "unaffected-config", 417 Namespace: defaultNamespace, 418 }, 419 Data: map[string]string{ 420 "config.yaml": "old-config", 421 }, 422 }, 423 "multikey-config": { 424 ObjectMeta: kube.ObjectMeta{ 425 Name: "multikey-config", 426 Namespace: defaultNamespace, 427 }, 428 Data: map[string]string{ 429 "foo.yaml": "old-foo-config", 430 "bar.yaml": "old-bar-config", 431 }, 432 }, 433 }, 434 expectedConfigMaps: map[string]kube.ConfigMap{ 435 "unaffected-config": { 436 ObjectMeta: kube.ObjectMeta{ 437 Name: "unaffected-config", 438 Namespace: defaultNamespace, 439 }, 440 Data: map[string]string{ 441 "config.yaml": "old-config", 442 }, 443 }, 444 "multikey-config": { 445 ObjectMeta: kube.ObjectMeta{ 446 Name: "multikey-config", 447 Namespace: defaultNamespace, 448 }, 449 Data: map[string]string{ 450 "foo.yaml": "new-foo-config", 451 "bar.yaml": "old-bar-config", 452 }, 453 }, 454 }, 455 }, 456 { 457 name: "remove config/foo.yaml, 1 update", 458 prAction: github.PullRequestActionClosed, 459 merged: true, 460 mergeCommit: "12345", 461 changes: []github.PullRequestChange{ 462 { 463 Filename: "config/foo.yaml", 464 Status: "removed", 465 }, 466 }, 467 existConfigMaps: map[string]kube.ConfigMap{ 468 "multikey-config": { 469 ObjectMeta: kube.ObjectMeta{ 470 Name: "multikey-config", 471 Namespace: defaultNamespace, 472 }, 473 Data: map[string]string{ 474 "foo.yaml": "old-foo-config", 475 "bar.yaml": "old-bar-config", 476 }, 477 }, 478 }, 479 expectedConfigMaps: map[string]kube.ConfigMap{ 480 "multikey-config": { 481 ObjectMeta: kube.ObjectMeta{ 482 Name: "multikey-config", 483 Namespace: defaultNamespace, 484 }, 485 Data: map[string]string{ 486 "bar.yaml": "old-bar-config", 487 }, 488 }, 489 }, 490 }, 491 { 492 name: "edited dir/subdir/fejtaverse/krzyzacy.yaml, 1 update", 493 prAction: github.PullRequestActionClosed, 494 merged: true, 495 mergeCommit: "12345", 496 changes: []github.PullRequestChange{ 497 { 498 Filename: "dir/subdir/fejtaverse/krzyzacy.yaml", 499 Status: "modified", 500 Additions: 1, 501 }, 502 }, 503 existConfigMaps: map[string]kube.ConfigMap{ 504 "glob-config": { 505 ObjectMeta: kube.ObjectMeta{ 506 Name: "glob-config", 507 Namespace: defaultNamespace, 508 }, 509 Data: map[string]string{ 510 "fejta.yaml": "old-fejta-config", 511 "krzyzacy.yaml": "old-krzyzacy-config", 512 }, 513 }, 514 }, 515 expectedConfigMaps: map[string]kube.ConfigMap{ 516 "glob-config": { 517 ObjectMeta: kube.ObjectMeta{ 518 Name: "glob-config", 519 Namespace: defaultNamespace, 520 }, 521 Data: map[string]string{ 522 "fejta.yaml": "old-fejta-config", 523 "krzyzacy.yaml": "new-krzyzacy-config", 524 }, 525 }, 526 }, 527 }, 528 { 529 name: "renamed dir/subdir/fejtaverse/krzyzacy.yaml, 1 update", 530 prAction: github.PullRequestActionClosed, 531 merged: true, 532 mergeCommit: "54321", 533 changes: []github.PullRequestChange{ 534 { 535 Filename: "dir/subdir/fejtaverse/fejtabot.yaml", 536 PreviousFilename: "dir/subdir/fejtaverse/krzyzacy.yaml", 537 Status: "renamed", 538 Additions: 1, 539 }, 540 }, 541 existConfigMaps: map[string]kube.ConfigMap{ 542 "glob-config": { 543 ObjectMeta: kube.ObjectMeta{ 544 Name: "glob-config", 545 Namespace: defaultNamespace, 546 }, 547 Data: map[string]string{ 548 "krzyzacy.yaml": "old-krzyzacy-config", 549 }, 550 }, 551 }, 552 expectedConfigMaps: map[string]kube.ConfigMap{ 553 "glob-config": { 554 ObjectMeta: kube.ObjectMeta{ 555 Name: "glob-config", 556 Namespace: defaultNamespace, 557 }, 558 Data: map[string]string{ 559 "fejtabot.yaml": "new-fejtabot-config", 560 }, 561 }, 562 }, 563 }, 564 { 565 name: "add delete edit glob config, 3 update", 566 prAction: github.PullRequestActionClosed, 567 merged: true, 568 mergeCommit: "12345", 569 changes: []github.PullRequestChange{ 570 { 571 Filename: "dir/subdir/fejta.yaml", 572 Status: "modified", 573 Additions: 1, 574 }, 575 { 576 Filename: "dir/subdir/fejtaverse/sig-foo/added.yaml", 577 Status: "added", 578 Additions: 1, 579 }, 580 { 581 Filename: "dir/subdir/fejtaverse/sig-bar/removed.yaml", 582 Status: "removed", 583 }, 584 }, 585 existConfigMaps: map[string]kube.ConfigMap{ 586 "glob-config": { 587 ObjectMeta: kube.ObjectMeta{ 588 Name: "glob-config", 589 Namespace: defaultNamespace, 590 }, 591 Data: map[string]string{ 592 "fejta.yaml": "old-fejta-config", 593 "krzyzacy.yaml": "old-krzyzacy-config", 594 "removed.yaml": "old-removed-config", 595 }, 596 }, 597 }, 598 expectedConfigMaps: map[string]kube.ConfigMap{ 599 "glob-config": { 600 ObjectMeta: kube.ObjectMeta{ 601 Name: "glob-config", 602 Namespace: defaultNamespace, 603 }, 604 Data: map[string]string{ 605 "fejta.yaml": "new-fejta-config", 606 "krzyzacy.yaml": "old-krzyzacy-config", 607 "added.yaml": "new-added-config", 608 }, 609 }, 610 }, 611 }, 612 { 613 name: "config changes without a backing configmap causes creation", 614 prAction: github.PullRequestActionClosed, 615 merged: true, 616 mergeCommit: "12345", 617 changes: []github.PullRequestChange{ 618 { 619 Filename: "prow/config.yaml", 620 Status: "modified", 621 Additions: 1, 622 }, 623 }, 624 existConfigMaps: map[string]kube.ConfigMap{}, 625 expectedConfigMaps: map[string]kube.ConfigMap{ 626 "config": { 627 ObjectMeta: kube.ObjectMeta{ 628 Name: "config", 629 Namespace: defaultNamespace, 630 }, 631 Data: map[string]string{ 632 "config.yaml": "new-config", 633 }, 634 }, 635 }, 636 }, 637 } 638 639 for _, tc := range testcases { 640 log := logrus.WithField("plugin", pluginName) 641 event := github.PullRequestEvent{ 642 Action: tc.prAction, 643 Number: basicPR.Number, 644 PullRequest: basicPR, 645 } 646 event.PullRequest.Merged = tc.merged 647 if tc.mergeCommit != "" { 648 event.PullRequest.MergeSHA = &tc.mergeCommit 649 } 650 651 fgc := &fakegithub.FakeClient{ 652 PullRequests: map[int]*github.PullRequest{ 653 basicPR.Number: &basicPR, 654 }, 655 PullRequestChanges: map[int][]github.PullRequestChange{ 656 basicPR.Number: tc.changes, 657 }, 658 IssueComments: map[int][]github.IssueComment{}, 659 RemoteFiles: map[string]map[string]string{ 660 "prow/config.yaml": { 661 "master": "old-config", 662 "12345": "new-config", 663 }, 664 "prow/plugins.yaml": { 665 "master": "old-plugins", 666 "12345": "new-plugins", 667 }, 668 "boskos/resources.yaml": { 669 "master": "old-boskos-config", 670 "12345": "new-boskos-config", 671 }, 672 "config/foo.yaml": { 673 "master": "old-foo-config", 674 "12345": "new-foo-config", 675 }, 676 "config/bar.yaml": { 677 "master": "old-bar-config", 678 "12345": "new-bar-config", 679 }, 680 "dir/subdir/fejta.yaml": { 681 "master": "old-fejta-config", 682 "12345": "new-fejta-config", 683 }, 684 "dir/subdir/fejtaverse/krzyzacy.yaml": { 685 "master": "old-krzyzacy-config", 686 "12345": "new-krzyzacy-config", 687 }, 688 "dir/subdir/fejtaverse/fejtabot.yaml": { 689 "54321": "new-fejtabot-config", 690 }, 691 "dir/subdir/fejtaverse/sig-foo/added.yaml": { 692 "12345": "new-added-config", 693 }, 694 "dir/subdir/fejtaverse/sig-bar/removed.yaml": { 695 "master": "old-removed-config", 696 }, 697 }, 698 } 699 fkc := &fakeKubeClient{ 700 maps: tc.existConfigMaps, 701 } 702 703 m := map[string]plugins.ConfigMapSpec{ 704 "prow/config.yaml": { 705 Name: "config", 706 }, 707 "prow/plugins.yaml": { 708 Name: "plugins", 709 Key: "test-key", 710 }, 711 "boskos/resources.yaml": { 712 Name: "boskos-config", 713 Namespace: "boskos", 714 }, 715 "config/foo.yaml": { 716 Name: "multikey-config", 717 }, 718 "config/bar.yaml": { 719 Name: "multikey-config", 720 }, 721 "dir/subdir/**/*.yaml": { 722 Name: "glob-config", 723 }, 724 } 725 726 if err := handle(fgc, fkc, log, event, m); err != nil { 727 t.Errorf("tc: %s, err: %s", tc.name, err) 728 } 729 730 if tc.expectedConfigMaps != nil { 731 if len(fgc.IssueComments[basicPR.Number]) != 1 { 732 t.Errorf("tc %s : Expect 1 comment, actually got %d", tc.name, len(fgc.IssueComments[basicPR.Number])) 733 } else { 734 comment := fgc.IssueComments[basicPR.Number][0].Body 735 if !strings.Contains(comment, "Updated the") { 736 t.Errorf("%s: missing Updated the from %s", tc.name, comment) 737 } 738 for configName := range tc.expectedConfigMaps { 739 found := false 740 for _, collection := range [][]string{fkc.updatedMaps, fkc.createdMaps} { 741 for _, name := range collection { 742 if name == configName { 743 if !strings.Contains(comment, configName) { 744 t.Errorf("%s: missing %s from %s", tc.name, configName, comment) 745 } 746 found = true 747 } 748 } 749 750 } 751 if !found { 752 if strings.Contains(comment, configName) { 753 t.Errorf("%s: should not contain %s in %s", tc.name, configName, comment) 754 } 755 } 756 } 757 } 758 } 759 760 actions := map[string][]string{ 761 "update": fkc.updatedMaps, 762 "create": fkc.createdMaps, 763 } 764 for action, names := range actions { 765 for _, name := range names { 766 found := false 767 for expected := range tc.expectedConfigMaps { 768 if name == expected { 769 found = true 770 } 771 } 772 773 if !found { 774 t.Errorf("%s: should not %s unexpected configmap %s", tc.name, action, name) 775 } 776 } 777 } 778 779 for configName := range tc.expectedConfigMaps { 780 if config, ok := fkc.maps[configName]; !ok { 781 t.Errorf("tc %s : Should have updated or created configmap for '%s'", tc.name, configName) 782 } else if expected, actual := tc.expectedConfigMaps[configName], config; !equality.Semantic.DeepEqual(expected, actual) { 783 t.Errorf("%s: incorrect ConfigMap state after update: %v", tc.name, diff.ObjectReflectDiff(expected, actual)) 784 } 785 } 786 } 787 }