github.com/abayer/test-infra@v0.0.5/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  }
    40  
    41  func (c *fakeKubeClient) GetConfigMap(name, namespace string) (kube.ConfigMap, error) {
    42  	return c.maps[name], nil
    43  }
    44  
    45  func (c *fakeKubeClient) ReplaceConfigMap(name string, config kube.ConfigMap) (kube.ConfigMap, error) {
    46  	if config.ObjectMeta.Name != name {
    47  		return kube.ConfigMap{}, fmt.Errorf("name %s does not match configmap name %s", name, config.ObjectMeta.Name)
    48  	}
    49  	if config.Namespace == "" {
    50  		config.Namespace = defaultNamespace
    51  	}
    52  	c.maps[name] = config
    53  	c.updatedMaps = append(c.updatedMaps, name)
    54  	return c.maps[name], nil
    55  }
    56  
    57  func TestUpdateConfig(t *testing.T) {
    58  	basicPR := github.PullRequest{
    59  		Number: 1,
    60  		Base: github.PullRequestBranch{
    61  			Repo: github.Repo{
    62  				Owner: github.User{
    63  					Login: "kubernetes",
    64  				},
    65  				Name: "kubernetes",
    66  			},
    67  		},
    68  		User: github.User{
    69  			Login: "foo",
    70  		},
    71  	}
    72  
    73  	testcases := []struct {
    74  		name               string
    75  		prAction           github.PullRequestEventAction
    76  		merged             bool
    77  		mergeCommit        string
    78  		changes            []github.PullRequestChange
    79  		existConfigMaps    map[string]kube.ConfigMap
    80  		expectedConfigMaps map[string]kube.ConfigMap
    81  	}{
    82  		{
    83  			name:     "Opened PR, no update",
    84  			prAction: github.PullRequestActionOpened,
    85  			merged:   false,
    86  			changes: []github.PullRequestChange{
    87  				{
    88  					Filename:  "prow/config.yaml",
    89  					Additions: 1,
    90  				},
    91  			},
    92  			existConfigMaps: map[string]kube.ConfigMap{},
    93  		},
    94  		{
    95  			name:   "Opened PR, not merged, no update",
    96  			merged: false,
    97  			changes: []github.PullRequestChange{
    98  				{
    99  					Filename:  "prow/config.yaml",
   100  					Additions: 1,
   101  				},
   102  			},
   103  			existConfigMaps: map[string]kube.ConfigMap{},
   104  		},
   105  		{
   106  			name:     "Closed PR, no prow changes, no update",
   107  			prAction: github.PullRequestActionClosed,
   108  			merged:   false,
   109  			changes: []github.PullRequestChange{
   110  				{
   111  					Filename:  "foo.txt",
   112  					Additions: 1,
   113  				},
   114  			},
   115  			existConfigMaps: map[string]kube.ConfigMap{},
   116  		},
   117  		{
   118  			name:     "For whatever reason no merge commit SHA",
   119  			prAction: github.PullRequestActionClosed,
   120  			merged:   true,
   121  			changes: []github.PullRequestChange{
   122  				{
   123  					Filename:  "prow/config.yaml",
   124  					Additions: 1,
   125  				},
   126  			},
   127  			existConfigMaps: map[string]kube.ConfigMap{},
   128  		},
   129  		{
   130  			name:        "changed config.yaml, 1 update",
   131  			prAction:    github.PullRequestActionClosed,
   132  			merged:      true,
   133  			mergeCommit: "12345",
   134  			changes: []github.PullRequestChange{
   135  				{
   136  					Filename:  "prow/config.yaml",
   137  					Additions: 1,
   138  				},
   139  			},
   140  			existConfigMaps: map[string]kube.ConfigMap{},
   141  			expectedConfigMaps: map[string]kube.ConfigMap{
   142  				"config": {
   143  					ObjectMeta: kube.ObjectMeta{
   144  						Name:      "config",
   145  						Namespace: defaultNamespace,
   146  					},
   147  					Data: map[string]string{
   148  						"config.yaml": "new-config",
   149  					},
   150  				},
   151  			},
   152  		},
   153  		{
   154  			name:        "changed config.yaml, existed configmap, 1 update",
   155  			prAction:    github.PullRequestActionClosed,
   156  			merged:      true,
   157  			mergeCommit: "12345",
   158  			changes: []github.PullRequestChange{
   159  				{
   160  					Filename:  "prow/config.yaml",
   161  					Additions: 1,
   162  				},
   163  			},
   164  			existConfigMaps: map[string]kube.ConfigMap{
   165  				"config": {
   166  					ObjectMeta: kube.ObjectMeta{
   167  						Name:      "config",
   168  						Namespace: defaultNamespace,
   169  					},
   170  					Data: map[string]string{
   171  						"config.yaml": "old-config",
   172  					},
   173  				},
   174  			},
   175  			expectedConfigMaps: map[string]kube.ConfigMap{
   176  				"config": {
   177  					ObjectMeta: kube.ObjectMeta{
   178  						Name:      "config",
   179  						Namespace: defaultNamespace,
   180  					},
   181  					Data: map[string]string{
   182  						"config.yaml": "new-config",
   183  					},
   184  				},
   185  			},
   186  		},
   187  		{
   188  			name:        "changed plugins.yaml, 1 update with custom key",
   189  			prAction:    github.PullRequestActionClosed,
   190  			merged:      true,
   191  			mergeCommit: "12345",
   192  			changes: []github.PullRequestChange{
   193  				{
   194  					Filename:  "prow/plugins.yaml",
   195  					Additions: 1,
   196  				},
   197  			},
   198  			existConfigMaps: map[string]kube.ConfigMap{},
   199  			expectedConfigMaps: map[string]kube.ConfigMap{
   200  				"plugins": {
   201  					ObjectMeta: kube.ObjectMeta{
   202  						Name:      "plugins",
   203  						Namespace: defaultNamespace,
   204  					},
   205  					Data: map[string]string{
   206  						"test-key": "new-plugins",
   207  					},
   208  				},
   209  			},
   210  		},
   211  		{
   212  			name:        "changed resources.yaml, 1 update with custom namespace",
   213  			prAction:    github.PullRequestActionClosed,
   214  			merged:      true,
   215  			mergeCommit: "12345",
   216  			changes: []github.PullRequestChange{
   217  				{
   218  					Filename:  "boskos/resources.yaml",
   219  					Additions: 1,
   220  				},
   221  			},
   222  			existConfigMaps: map[string]kube.ConfigMap{},
   223  			expectedConfigMaps: map[string]kube.ConfigMap{
   224  				"boskos-config": {
   225  					ObjectMeta: kube.ObjectMeta{
   226  						Name:      "boskos-config",
   227  						Namespace: "boskos",
   228  					},
   229  					Data: map[string]string{
   230  						"resources.yaml": "new-boskos-config",
   231  					},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			name:        "changed config.yaml, plugins.yaml and resources.yaml, 3 update",
   237  			prAction:    github.PullRequestActionClosed,
   238  			merged:      true,
   239  			mergeCommit: "12345",
   240  			changes: []github.PullRequestChange{
   241  				{
   242  					Filename:  "prow/plugins.yaml",
   243  					Additions: 1,
   244  				},
   245  				{
   246  					Filename:  "prow/config.yaml",
   247  					Additions: 1,
   248  				},
   249  				{
   250  					Filename:  "boskos/resources.yaml",
   251  					Additions: 1,
   252  				},
   253  			},
   254  			existConfigMaps: map[string]kube.ConfigMap{},
   255  			expectedConfigMaps: map[string]kube.ConfigMap{
   256  				"config": {
   257  					ObjectMeta: kube.ObjectMeta{
   258  						Name:      "config",
   259  						Namespace: defaultNamespace,
   260  					},
   261  					Data: map[string]string{
   262  						"config.yaml": "new-config",
   263  					},
   264  				},
   265  				"plugins": {
   266  					ObjectMeta: kube.ObjectMeta{
   267  						Name:      "plugins",
   268  						Namespace: defaultNamespace,
   269  					},
   270  					Data: map[string]string{
   271  						"test-key": "new-plugins",
   272  					},
   273  				},
   274  				"boskos-config": {
   275  					ObjectMeta: kube.ObjectMeta{
   276  						Name:      "boskos-config",
   277  						Namespace: "boskos",
   278  					},
   279  					Data: map[string]string{
   280  						"resources.yaml": "new-boskos-config",
   281  					},
   282  				},
   283  			},
   284  		},
   285  		{
   286  			name:        "edited both config/foo.yaml and config/bar.yaml, 2 update",
   287  			prAction:    github.PullRequestActionClosed,
   288  			merged:      true,
   289  			mergeCommit: "12345",
   290  			changes: []github.PullRequestChange{
   291  				{
   292  					Filename:  "config/foo.yaml",
   293  					Additions: 1,
   294  				},
   295  				{
   296  					Filename:  "config/bar.yaml",
   297  					Additions: 1,
   298  				},
   299  			},
   300  			existConfigMaps: map[string]kube.ConfigMap{
   301  				"multikey-config": {
   302  					ObjectMeta: kube.ObjectMeta{
   303  						Name:      "multikey-config",
   304  						Namespace: defaultNamespace,
   305  					},
   306  					Data: map[string]string{
   307  						"foo.yaml": "old-foo-config",
   308  						"bar.yaml": "old-bar-config",
   309  					},
   310  				},
   311  			},
   312  			expectedConfigMaps: map[string]kube.ConfigMap{
   313  				"multikey-config": {
   314  					ObjectMeta: kube.ObjectMeta{
   315  						Name:      "multikey-config",
   316  						Namespace: defaultNamespace,
   317  					},
   318  					Data: map[string]string{
   319  						"foo.yaml": "new-foo-config",
   320  						"bar.yaml": "new-bar-config",
   321  					},
   322  				},
   323  			},
   324  		},
   325  		{
   326  			name:        "edited config/foo.yaml, 1 update",
   327  			prAction:    github.PullRequestActionClosed,
   328  			merged:      true,
   329  			mergeCommit: "12345",
   330  			changes: []github.PullRequestChange{
   331  				{
   332  					Filename:  "config/foo.yaml",
   333  					Status:    "modified",
   334  					Additions: 1,
   335  				},
   336  			},
   337  			existConfigMaps: map[string]kube.ConfigMap{
   338  				"unaffected-config": {
   339  					ObjectMeta: kube.ObjectMeta{
   340  						Name:      "unaffected-config",
   341  						Namespace: defaultNamespace,
   342  					},
   343  					Data: map[string]string{
   344  						"config.yaml": "old-config",
   345  					},
   346  				},
   347  				"multikey-config": {
   348  					ObjectMeta: kube.ObjectMeta{
   349  						Name:      "multikey-config",
   350  						Namespace: defaultNamespace,
   351  					},
   352  					Data: map[string]string{
   353  						"foo.yaml": "old-foo-config",
   354  						"bar.yaml": "old-bar-config",
   355  					},
   356  				},
   357  			},
   358  			expectedConfigMaps: map[string]kube.ConfigMap{
   359  				"unaffected-config": {
   360  					ObjectMeta: kube.ObjectMeta{
   361  						Name:      "unaffected-config",
   362  						Namespace: defaultNamespace,
   363  					},
   364  					Data: map[string]string{
   365  						"config.yaml": "old-config",
   366  					},
   367  				},
   368  				"multikey-config": {
   369  					ObjectMeta: kube.ObjectMeta{
   370  						Name:      "multikey-config",
   371  						Namespace: defaultNamespace,
   372  					},
   373  					Data: map[string]string{
   374  						"foo.yaml": "new-foo-config",
   375  						"bar.yaml": "old-bar-config",
   376  					},
   377  				},
   378  			},
   379  		},
   380  		{
   381  			name:        "remove config/foo.yaml, 1 update",
   382  			prAction:    github.PullRequestActionClosed,
   383  			merged:      true,
   384  			mergeCommit: "12345",
   385  			changes: []github.PullRequestChange{
   386  				{
   387  					Filename: "config/foo.yaml",
   388  					Status:   "removed",
   389  				},
   390  			},
   391  			existConfigMaps: map[string]kube.ConfigMap{
   392  				"multikey-config": {
   393  					ObjectMeta: kube.ObjectMeta{
   394  						Name:      "multikey-config",
   395  						Namespace: defaultNamespace,
   396  					},
   397  					Data: map[string]string{
   398  						"foo.yaml": "old-foo-config",
   399  						"bar.yaml": "old-bar-config",
   400  					},
   401  				},
   402  			},
   403  			expectedConfigMaps: map[string]kube.ConfigMap{
   404  				"multikey-config": {
   405  					ObjectMeta: kube.ObjectMeta{
   406  						Name:      "multikey-config",
   407  						Namespace: defaultNamespace,
   408  					},
   409  					Data: map[string]string{
   410  						"bar.yaml": "old-bar-config",
   411  					},
   412  				},
   413  			},
   414  		},
   415  		{
   416  			name:        "edited dir/subdir/fejtaverse/krzyzacy.yaml, 1 update",
   417  			prAction:    github.PullRequestActionClosed,
   418  			merged:      true,
   419  			mergeCommit: "12345",
   420  			changes: []github.PullRequestChange{
   421  				{
   422  					Filename:  "dir/subdir/fejtaverse/krzyzacy.yaml",
   423  					Status:    "modified",
   424  					Additions: 1,
   425  				},
   426  			},
   427  			existConfigMaps: map[string]kube.ConfigMap{
   428  				"glob-config": {
   429  					ObjectMeta: kube.ObjectMeta{
   430  						Name:      "glob-config",
   431  						Namespace: defaultNamespace,
   432  					},
   433  					Data: map[string]string{
   434  						"fejta.yaml":    "old-fejta-config",
   435  						"krzyzacy.yaml": "old-krzyzacy-config",
   436  					},
   437  				},
   438  			},
   439  			expectedConfigMaps: map[string]kube.ConfigMap{
   440  				"glob-config": {
   441  					ObjectMeta: kube.ObjectMeta{
   442  						Name:      "glob-config",
   443  						Namespace: defaultNamespace,
   444  					},
   445  					Data: map[string]string{
   446  						"fejta.yaml":    "old-fejta-config",
   447  						"krzyzacy.yaml": "new-krzyzacy-config",
   448  					},
   449  				},
   450  			},
   451  		},
   452  		{
   453  			name:        "add delete edit glob config, 3 update",
   454  			prAction:    github.PullRequestActionClosed,
   455  			merged:      true,
   456  			mergeCommit: "12345",
   457  			changes: []github.PullRequestChange{
   458  				{
   459  					Filename:  "dir/subdir/fejta.yaml",
   460  					Status:    "modified",
   461  					Additions: 1,
   462  				},
   463  				{
   464  					Filename:  "dir/subdir/fejtaverse/sig-foo/added.yaml",
   465  					Status:    "added",
   466  					Additions: 1,
   467  				},
   468  				{
   469  					Filename: "dir/subdir/fejtaverse/sig-bar/removed.yaml",
   470  					Status:   "removed",
   471  				},
   472  			},
   473  			existConfigMaps: map[string]kube.ConfigMap{
   474  				"glob-config": {
   475  					ObjectMeta: kube.ObjectMeta{
   476  						Name:      "glob-config",
   477  						Namespace: defaultNamespace,
   478  					},
   479  					Data: map[string]string{
   480  						"fejta.yaml":    "old-fejta-config",
   481  						"krzyzacy.yaml": "old-krzyzacy-config",
   482  						"removed.yaml":  "old-removed-config",
   483  					},
   484  				},
   485  			},
   486  			expectedConfigMaps: map[string]kube.ConfigMap{
   487  				"glob-config": {
   488  					ObjectMeta: kube.ObjectMeta{
   489  						Name:      "glob-config",
   490  						Namespace: defaultNamespace,
   491  					},
   492  					Data: map[string]string{
   493  						"fejta.yaml":    "new-fejta-config",
   494  						"krzyzacy.yaml": "old-krzyzacy-config",
   495  						"added.yaml":    "new-added-config",
   496  					},
   497  				},
   498  			},
   499  		},
   500  	}
   501  
   502  	for _, tc := range testcases {
   503  		log := logrus.WithField("plugin", pluginName)
   504  		event := github.PullRequestEvent{
   505  			Action:      tc.prAction,
   506  			Number:      basicPR.Number,
   507  			PullRequest: basicPR,
   508  		}
   509  		event.PullRequest.Merged = tc.merged
   510  		if tc.mergeCommit != "" {
   511  			event.PullRequest.MergeSHA = &tc.mergeCommit
   512  		}
   513  
   514  		fgc := &fakegithub.FakeClient{
   515  			PullRequests: map[int]*github.PullRequest{
   516  				basicPR.Number: &basicPR,
   517  			},
   518  			PullRequestChanges: map[int][]github.PullRequestChange{
   519  				basicPR.Number: tc.changes,
   520  			},
   521  			IssueComments: map[int][]github.IssueComment{},
   522  			RemoteFiles: map[string]map[string]string{
   523  				"prow/config.yaml": {
   524  					"master": "old-config",
   525  					"12345":  "new-config",
   526  				},
   527  				"prow/plugins.yaml": {
   528  					"master": "old-plugins",
   529  					"12345":  "new-plugins",
   530  				},
   531  				"boskos/resources.yaml": {
   532  					"master": "old-boskos-config",
   533  					"12345":  "new-boskos-config",
   534  				},
   535  				"config/foo.yaml": {
   536  					"master": "old-foo-config",
   537  					"12345":  "new-foo-config",
   538  				},
   539  				"config/bar.yaml": {
   540  					"master": "old-bar-config",
   541  					"12345":  "new-bar-config",
   542  				},
   543  				"dir/subdir/fejta.yaml": {
   544  					"master": "old-fejta-config",
   545  					"12345":  "new-fejta-config",
   546  				},
   547  				"dir/subdir/fejtaverse/krzyzacy.yaml": {
   548  					"master": "old-krzyzacy-config",
   549  					"12345":  "new-krzyzacy-config",
   550  				},
   551  				"dir/subdir/fejtaverse/sig-foo/added.yaml": {
   552  					"12345": "new-added-config",
   553  				},
   554  				"dir/subdir/fejtaverse/sig-bar/removed.yaml": {
   555  					"master": "old-removed-config",
   556  				},
   557  			},
   558  		}
   559  		fkc := &fakeKubeClient{
   560  			maps: tc.existConfigMaps,
   561  		}
   562  
   563  		m := map[string]plugins.ConfigMapSpec{
   564  			"prow/config.yaml": {
   565  				Name: "config",
   566  			},
   567  			"prow/plugins.yaml": {
   568  				Name: "plugins",
   569  				Key:  "test-key",
   570  			},
   571  			"boskos/resources.yaml": {
   572  				Name:      "boskos-config",
   573  				Namespace: "boskos",
   574  			},
   575  			"config/foo.yaml": {
   576  				Name: "multikey-config",
   577  			},
   578  			"config/bar.yaml": {
   579  				Name: "multikey-config",
   580  			},
   581  			"dir/subdir/**/*.yaml": {
   582  				Name: "glob-config",
   583  			},
   584  		}
   585  
   586  		if err := handle(fgc, fkc, log, event, m); err != nil {
   587  			t.Fatalf("tc: %s, err: %s", tc.name, err)
   588  		}
   589  
   590  		if tc.expectedConfigMaps != nil {
   591  			if len(fgc.IssueComments[basicPR.Number]) != 1 {
   592  				t.Fatalf("tc %s : Expect 1 comment, actually got %d", tc.name, len(fgc.IssueComments[basicPR.Number]))
   593  			}
   594  
   595  			comment := fgc.IssueComments[basicPR.Number][0].Body
   596  			if !strings.Contains(comment, "Updated the") {
   597  				t.Errorf("%s: missing Updated the from %s", tc.name, comment)
   598  			}
   599  			for configName := range tc.expectedConfigMaps {
   600  				found := false
   601  				for _, name := range fkc.updatedMaps {
   602  					if name == configName {
   603  						if !strings.Contains(comment, configName) {
   604  							t.Errorf("%s: missing %s from %s", tc.name, configName, comment)
   605  						}
   606  						found = true
   607  					}
   608  				}
   609  
   610  				if !found {
   611  					if strings.Contains(comment, configName) {
   612  						t.Errorf("%s: should not contain %s in %s", tc.name, configName, comment)
   613  					}
   614  				}
   615  			}
   616  		}
   617  
   618  		for _, name := range fkc.updatedMaps {
   619  			found := false
   620  			for expected := range tc.expectedConfigMaps {
   621  				if name == expected {
   622  					found = true
   623  				}
   624  			}
   625  
   626  			if !found {
   627  				t.Errorf("%s: should not update unexpected configmap %s", tc.name, name)
   628  			}
   629  		}
   630  
   631  		for configName := range tc.expectedConfigMaps {
   632  			if config, ok := fkc.maps[configName]; !ok {
   633  				t.Errorf("tc %s : Should have updated configmap for '%s'", tc.name, configName)
   634  			} else if expected, actual := tc.expectedConfigMaps[configName], config; !equality.Semantic.DeepEqual(expected, actual) {
   635  				t.Errorf("%s: incorrect ConfigMap state after update: %v", tc.name, diff.ObjectReflectDiff(expected, actual))
   636  			}
   637  		}
   638  	}
   639  }