...

Source file src/sigs.k8s.io/kustomize/api/internal/accumulator/namereferencetransformer_test.go

Documentation: sigs.k8s.io/kustomize/api/internal/accumulator

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package accumulator
     5  
     6  import (
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
    12  	"sigs.k8s.io/kustomize/api/provider"
    13  	"sigs.k8s.io/kustomize/api/resmap"
    14  	resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
    15  	"sigs.k8s.io/kustomize/kyaml/resid"
    16  )
    17  
    18  const notEqualErrFmt = "expected (self) doesn't match actual (other): %v"
    19  
    20  func TestNameReferenceHappyRun(t *testing.T) {
    21  	m := resmaptest_test.NewRmBuilderDefault(t).AddWithName(
    22  		"cm1",
    23  		map[string]interface{}{
    24  			"apiVersion": "v1",
    25  			"kind":       "ConfigMap",
    26  			"metadata": map[string]interface{}{
    27  				"name": "someprefix-cm1-somehash",
    28  			},
    29  		}).AddWithName(
    30  		"cm2",
    31  		map[string]interface{}{
    32  			"apiVersion": "v1",
    33  			"kind":       "ConfigMap",
    34  			"metadata": map[string]interface{}{
    35  				"name": "someprefix-cm2-somehash",
    36  			},
    37  		}).AddWithName(
    38  		"secret1",
    39  		map[string]interface{}{
    40  			"apiVersion": "v1",
    41  			"kind":       "Secret",
    42  			"metadata": map[string]interface{}{
    43  				"name": "someprefix-secret1-somehash",
    44  			},
    45  		}).AddWithName(
    46  		"claim1",
    47  		map[string]interface{}{
    48  			"apiVersion": "v1",
    49  			"kind":       "PersistentVolumeClaim",
    50  			"metadata": map[string]interface{}{
    51  				"name": "someprefix-claim1",
    52  			},
    53  		}).Add(
    54  		map[string]interface{}{
    55  			"group":      "networking.k8s.io",
    56  			"apiVersion": "v1beta1",
    57  			"kind":       "Ingress",
    58  			"metadata": map[string]interface{}{
    59  				"name": "ingress1",
    60  				"annotations": map[string]interface{}{
    61  					"ingress.kubernetes.io/auth-secret":           "secret1",
    62  					"nginx.ingress.kubernetes.io/auth-secret":     "secret1",
    63  					"nginx.ingress.kubernetes.io/auth-tls-secret": "secret1",
    64  				},
    65  			},
    66  			"spec": map[string]interface{}{
    67  				"backend": map[string]interface{}{
    68  					"serviceName": "testsvc",
    69  					"servicePort": "80",
    70  				},
    71  			},
    72  		},
    73  	).Add(
    74  		map[string]interface{}{
    75  			"group":      "apps",
    76  			"apiVersion": "v1",
    77  			"kind":       "Deployment",
    78  			"metadata": map[string]interface{}{
    79  				"name": "deploy1",
    80  			},
    81  			"spec": map[string]interface{}{
    82  				"template": map[string]interface{}{
    83  					"spec": map[string]interface{}{
    84  						"containers": []interface{}{
    85  							map[string]interface{}{
    86  								"name":  "nginx",
    87  								"image": "nginx:1.7.9",
    88  								"env": []interface{}{
    89  									map[string]interface{}{
    90  										"name": "CM_FOO",
    91  										"valueFrom": map[string]interface{}{
    92  											"configMapKeyRef": map[string]interface{}{
    93  												"name": "cm1",
    94  												"key":  "somekey",
    95  											},
    96  										},
    97  									},
    98  									map[string]interface{}{
    99  										"name": "SECRET_FOO",
   100  										"valueFrom": map[string]interface{}{
   101  											"secretKeyRef": map[string]interface{}{
   102  												"name": "secret1",
   103  												"key":  "somekey",
   104  											},
   105  										},
   106  									},
   107  								},
   108  								"envFrom": []interface{}{
   109  									map[string]interface{}{
   110  										"configMapRef": map[string]interface{}{
   111  											"name": "cm1",
   112  											"key":  "somekey",
   113  										},
   114  									},
   115  									map[string]interface{}{
   116  										"secretRef": map[string]interface{}{
   117  											"name": "secret1",
   118  											"key":  "somekey",
   119  										},
   120  									},
   121  								},
   122  							},
   123  						},
   124  						"imagePullSecrets": []interface{}{
   125  							map[string]interface{}{
   126  								"name": "secret1",
   127  							},
   128  						},
   129  						"volumes": map[string]interface{}{
   130  							"configMap": map[string]interface{}{
   131  								"name": "cm1",
   132  							},
   133  							"projected": map[string]interface{}{
   134  								"sources": map[string]interface{}{
   135  									"configMap": map[string]interface{}{
   136  										"name": "cm2",
   137  									},
   138  									"secret": map[string]interface{}{
   139  										"name": "secret1",
   140  									},
   141  								},
   142  							},
   143  							"secret": map[string]interface{}{
   144  								"secretName": "secret1",
   145  							},
   146  							"persistentVolumeClaim": map[string]interface{}{
   147  								"claimName": "claim1",
   148  							},
   149  						},
   150  					},
   151  				},
   152  			},
   153  		}).Add(
   154  		map[string]interface{}{
   155  			"group":      "apps",
   156  			"apiVersion": "v1",
   157  			"kind":       "StatefulSet",
   158  			"metadata": map[string]interface{}{
   159  				"name": "statefulset1",
   160  			},
   161  			"spec": map[string]interface{}{
   162  				"template": map[string]interface{}{
   163  					"spec": map[string]interface{}{
   164  						"containers": []interface{}{
   165  							map[string]interface{}{
   166  								"name":  "nginx",
   167  								"image": "nginx:1.7.9",
   168  							},
   169  						},
   170  						"volumes": map[string]interface{}{
   171  							"projected": map[string]interface{}{
   172  								"sources": map[string]interface{}{
   173  									"configMap": map[string]interface{}{
   174  										"name": "cm2",
   175  									},
   176  									"secret": map[string]interface{}{
   177  										"name": "secret1",
   178  									},
   179  								},
   180  							},
   181  						},
   182  					},
   183  				},
   184  			},
   185  		}).AddWithName("sa",
   186  		map[string]interface{}{
   187  			"apiVersion": "v1",
   188  			"kind":       "ServiceAccount",
   189  			"metadata": map[string]interface{}{
   190  				"name":      "someprefix-sa",
   191  				"namespace": "test",
   192  			},
   193  		}).Add(
   194  		map[string]interface{}{
   195  			"apiVersion": "rbac.authorization.k8s.io/v1",
   196  			"kind":       "ClusterRoleBinding",
   197  			"metadata": map[string]interface{}{
   198  				"name": "crb",
   199  			},
   200  			"subjects": []interface{}{
   201  				map[string]interface{}{
   202  					"kind":      "ServiceAccount",
   203  					"name":      "sa",
   204  					"namespace": "test",
   205  				},
   206  			},
   207  		}).Add(
   208  		map[string]interface{}{
   209  			"apiVersion": "rbac.authorization.k8s.io/v1",
   210  			"kind":       "ClusterRole",
   211  			"metadata": map[string]interface{}{
   212  				"name": "cr",
   213  			},
   214  			"rules": []interface{}{
   215  				map[string]interface{}{
   216  					"resources": []interface{}{
   217  						"secrets",
   218  					},
   219  					"resourceNames": []interface{}{
   220  						"secret1",
   221  						"secret1",
   222  						"secret2",
   223  						"cm1",
   224  					},
   225  				},
   226  			},
   227  		}).Add(
   228  		map[string]interface{}{
   229  			"apiVersion": "batch/v1beta1",
   230  			"kind":       "CronJob",
   231  			"metadata": map[string]interface{}{
   232  				"name": "cronjob1",
   233  			},
   234  			"spec": map[string]interface{}{
   235  				"schedule": "0 14 * * *",
   236  				"jobTemplate": map[string]interface{}{
   237  					"spec": map[string]interface{}{
   238  						"template": map[string]interface{}{
   239  							"spec": map[string]interface{}{
   240  								"containers": []interface{}{
   241  									map[string]interface{}{
   242  										"name":  "main",
   243  										"image": "myimage",
   244  									},
   245  								},
   246  								"volumes": map[string]interface{}{
   247  									"projected": map[string]interface{}{
   248  										"sources": map[string]interface{}{
   249  											"configMap": map[string]interface{}{
   250  												"name": "cm2",
   251  											},
   252  											"secret": map[string]interface{}{
   253  												"name": "secret1",
   254  											},
   255  										},
   256  									},
   257  								},
   258  							},
   259  						},
   260  					},
   261  				},
   262  			},
   263  		}).ResMap()
   264  
   265  	expected := resmaptest_test.NewSeededRmBuilderDefault(
   266  		t, m.ShallowCopy()).ReplaceResource(
   267  		map[string]interface{}{
   268  			"group":      "apps",
   269  			"apiVersion": "v1",
   270  			"kind":       "Deployment",
   271  			"metadata": map[string]interface{}{
   272  				"name": "deploy1",
   273  			},
   274  			"spec": map[string]interface{}{
   275  				"template": map[string]interface{}{
   276  					"spec": map[string]interface{}{
   277  						"containers": []interface{}{
   278  							map[string]interface{}{
   279  								"name":  "nginx",
   280  								"image": "nginx:1.7.9",
   281  								"env": []interface{}{
   282  									map[string]interface{}{
   283  										"name": "CM_FOO",
   284  										"valueFrom": map[string]interface{}{
   285  											"configMapKeyRef": map[string]interface{}{
   286  												"name": "someprefix-cm1-somehash",
   287  												"key":  "somekey",
   288  											},
   289  										},
   290  									},
   291  									map[string]interface{}{
   292  										"name": "SECRET_FOO",
   293  										"valueFrom": map[string]interface{}{
   294  											"secretKeyRef": map[string]interface{}{
   295  												"name": "someprefix-secret1-somehash",
   296  												"key":  "somekey",
   297  											},
   298  										},
   299  									},
   300  								},
   301  								"envFrom": []interface{}{
   302  									map[string]interface{}{
   303  										"configMapRef": map[string]interface{}{
   304  											"name": "someprefix-cm1-somehash",
   305  											"key":  "somekey",
   306  										},
   307  									},
   308  									map[string]interface{}{
   309  										"secretRef": map[string]interface{}{
   310  											"name": "someprefix-secret1-somehash",
   311  											"key":  "somekey",
   312  										},
   313  									},
   314  								},
   315  							},
   316  						},
   317  						"imagePullSecrets": []interface{}{
   318  							map[string]interface{}{
   319  								"name": "someprefix-secret1-somehash",
   320  							},
   321  						},
   322  						"volumes": map[string]interface{}{
   323  							"configMap": map[string]interface{}{
   324  								"name": "someprefix-cm1-somehash",
   325  							},
   326  							"projected": map[string]interface{}{
   327  								"sources": map[string]interface{}{
   328  									"configMap": map[string]interface{}{
   329  										"name": "someprefix-cm2-somehash",
   330  									},
   331  									"secret": map[string]interface{}{
   332  										"name": "someprefix-secret1-somehash",
   333  									},
   334  								},
   335  							},
   336  							"secret": map[string]interface{}{
   337  								"secretName": "someprefix-secret1-somehash",
   338  							},
   339  							"persistentVolumeClaim": map[string]interface{}{
   340  								"claimName": "someprefix-claim1",
   341  							},
   342  						},
   343  					},
   344  				},
   345  			},
   346  		}).ReplaceResource(
   347  		map[string]interface{}{
   348  			"group":      "apps",
   349  			"apiVersion": "v1",
   350  			"kind":       "StatefulSet",
   351  			"metadata": map[string]interface{}{
   352  				"name": "statefulset1",
   353  			},
   354  			"spec": map[string]interface{}{
   355  				"template": map[string]interface{}{
   356  					"spec": map[string]interface{}{
   357  						"containers": []interface{}{
   358  							map[string]interface{}{
   359  								"name":  "nginx",
   360  								"image": "nginx:1.7.9",
   361  							},
   362  						},
   363  						"volumes": map[string]interface{}{
   364  							"projected": map[string]interface{}{
   365  								"sources": map[string]interface{}{
   366  									"configMap": map[string]interface{}{
   367  										"name": "someprefix-cm2-somehash",
   368  									},
   369  									"secret": map[string]interface{}{
   370  										"name": "someprefix-secret1-somehash",
   371  									},
   372  								},
   373  							},
   374  						},
   375  					},
   376  				},
   377  			},
   378  		}).ReplaceResource(
   379  		map[string]interface{}{
   380  			"group":      "networking.k8s.io",
   381  			"apiVersion": "v1beta1",
   382  			"kind":       "Ingress",
   383  			"metadata": map[string]interface{}{
   384  				"name": "ingress1",
   385  				"annotations": map[string]interface{}{
   386  					"ingress.kubernetes.io/auth-secret":           "someprefix-secret1-somehash",
   387  					"nginx.ingress.kubernetes.io/auth-secret":     "someprefix-secret1-somehash",
   388  					"nginx.ingress.kubernetes.io/auth-tls-secret": "someprefix-secret1-somehash",
   389  				},
   390  			},
   391  			"spec": map[string]interface{}{
   392  				"backend": map[string]interface{}{
   393  					"serviceName": "testsvc",
   394  					"servicePort": "80",
   395  				},
   396  			},
   397  		}).ReplaceResource(
   398  		map[string]interface{}{
   399  			"apiVersion": "rbac.authorization.k8s.io/v1",
   400  			"kind":       "ClusterRoleBinding",
   401  			"metadata": map[string]interface{}{
   402  				"name": "crb",
   403  			},
   404  			"subjects": []interface{}{
   405  				map[string]interface{}{
   406  					"kind":      "ServiceAccount",
   407  					"name":      "someprefix-sa",
   408  					"namespace": "test",
   409  				},
   410  			},
   411  		}).ReplaceResource(
   412  		map[string]interface{}{
   413  			"apiVersion": "rbac.authorization.k8s.io/v1",
   414  			"kind":       "ClusterRole",
   415  			"metadata": map[string]interface{}{
   416  				"name": "cr",
   417  			},
   418  			"rules": []interface{}{
   419  				map[string]interface{}{
   420  					"resources": []interface{}{
   421  						"secrets",
   422  					},
   423  					"resourceNames": []interface{}{
   424  						"someprefix-secret1-somehash",
   425  						"someprefix-secret1-somehash",
   426  						"secret2",
   427  						"someprefix-cm1-somehash",
   428  					},
   429  				},
   430  			},
   431  		}).ReplaceResource(
   432  		map[string]interface{}{
   433  			"apiVersion": "batch/v1beta1",
   434  			"kind":       "CronJob",
   435  			"metadata": map[string]interface{}{
   436  				"name": "cronjob1",
   437  			},
   438  			"spec": map[string]interface{}{
   439  				"schedule": "0 14 * * *",
   440  				"jobTemplate": map[string]interface{}{
   441  					"spec": map[string]interface{}{
   442  						"template": map[string]interface{}{
   443  							"spec": map[string]interface{}{
   444  								"containers": []interface{}{
   445  									map[string]interface{}{
   446  										"name":  "main",
   447  										"image": "myimage",
   448  									},
   449  								},
   450  								"volumes": map[string]interface{}{
   451  									"projected": map[string]interface{}{
   452  										"sources": map[string]interface{}{
   453  											"configMap": map[string]interface{}{
   454  												"name": "someprefix-cm2-somehash",
   455  											},
   456  											"secret": map[string]interface{}{
   457  												"name": "someprefix-secret1-somehash",
   458  											},
   459  										},
   460  									},
   461  								},
   462  							},
   463  						},
   464  					},
   465  				},
   466  			},
   467  		}).ResMap()
   468  
   469  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
   470  	err := nrt.Transform(m)
   471  	if err != nil {
   472  		t.Fatalf("unexpected error: %v", err)
   473  	}
   474  
   475  	if err = expected.ErrorIfNotEqualLists(m); err != nil {
   476  		t.Fatalf(notEqualErrFmt, err)
   477  	}
   478  }
   479  
   480  func TestNameReferenceUnhappyRun(t *testing.T) {
   481  	tests := []struct {
   482  		resMap      resmap.ResMap
   483  		expectedErr string
   484  	}{
   485  		{
   486  			resMap: resmaptest_test.NewRmBuilderDefault(t).Add(
   487  				map[string]interface{}{
   488  					"apiVersion": "rbac.authorization.k8s.io/v1",
   489  					"kind":       "ClusterRole",
   490  					"metadata": map[string]interface{}{
   491  						"name": "cr",
   492  					},
   493  					"rules": []interface{}{
   494  						map[string]interface{}{
   495  							"resources": []interface{}{
   496  								"secrets",
   497  							},
   498  							"resourceNames": []interface{}{
   499  								[]interface{}{},
   500  							},
   501  						},
   502  					},
   503  				}).ResMap(),
   504  			expectedErr: "is expected to be"},
   505  		{
   506  			resMap: resmaptest_test.NewRmBuilderDefault(t).Add(
   507  				map[string]interface{}{
   508  					"apiVersion": "rbac.authorization.k8s.io/v1",
   509  					"kind":       "ClusterRole",
   510  					"metadata": map[string]interface{}{
   511  						"name": "cr",
   512  					},
   513  					"rules": []interface{}{
   514  						map[string]interface{}{
   515  							"resources": []interface{}{
   516  								"secrets",
   517  							},
   518  							"resourceNames": map[string]interface{}{
   519  								"foo": "bar",
   520  							},
   521  						},
   522  					},
   523  				}).ResMap(),
   524  			expectedErr: `updating name reference in 'rules/resourceNames' field of 'ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]': ` +
   525  				`considering field 'rules/resourceNames' of object ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]: visit traversal on ` +
   526  				`path: [resourceNames]: path config error; no 'name' field in node`,
   527  		},
   528  		{
   529  			// When targeting a map reference, we need to update both name and namespace, so multiple
   530  			// possible referral targets with different namespaces should not be considered identical.
   531  			// This test covers a bug where the difference in namespace was ignored and one candidate was chosen at random.
   532  			resMap: resmaptest_test.NewRmBuilderDefault(t).AddWithNsAndName(
   533  				"", orgname,
   534  				map[string]interface{}{
   535  					"apiVersion": "v1",
   536  					"kind":       "ServiceAccount",
   537  					"metadata": map[string]interface{}{
   538  						"name":      orgname,
   539  						"namespace": ns1,
   540  					},
   541  				},
   542  			).AddWithNsAndName(
   543  				"", orgname,
   544  				map[string]interface{}{
   545  					"apiVersion": "v1",
   546  					"kind":       "ServiceAccount",
   547  					"metadata": map[string]interface{}{
   548  						"name":      orgname,
   549  						"namespace": ns2,
   550  					},
   551  				},
   552  			).Add(
   553  				map[string]interface{}{
   554  					"apiVersion": "rbac.authorization.k8s.io/v1",
   555  					"kind":       "ClusterRoleBinding",
   556  					"metadata": map[string]interface{}{
   557  						"name": orgname,
   558  					},
   559  					"roleRef": map[string]interface{}{
   560  						"apiGroup": "rbac.authorization.k8s.io",
   561  						"kind":     "ClusterRole",
   562  						"name":     orgname,
   563  					},
   564  					"subjects": []interface{}{
   565  						map[string]interface{}{
   566  							"kind": "ServiceAccount",
   567  							"name": orgname,
   568  						},
   569  					},
   570  				},
   571  			).ResMap(),
   572  			expectedErr: "found multiple possible referrals: ServiceAccount.v1.[noGrp]/uniquename.ns1, ServiceAccount.v1.[noGrp]/uniquename.ns2",
   573  		},
   574  	}
   575  
   576  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
   577  	for _, test := range tests {
   578  		err := nrt.Transform(test.resMap)
   579  		if err == nil {
   580  			t.Fatalf("expected error to happen")
   581  		}
   582  
   583  		if !strings.Contains(err.Error(), test.expectedErr) {
   584  			t.Fatalf("Incorrect error.\nExpected:\n %s\nGot:\n%v",
   585  				test.expectedErr, err)
   586  		}
   587  	}
   588  }
   589  
   590  func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
   591  	rf := provider.NewDefaultDepProvider().GetResourceFactory()
   592  
   593  	v1 := rf.FromMapWithName(
   594  		"volume1",
   595  		map[string]interface{}{
   596  			"apiVersion": "v1",
   597  			"kind":       "PersistentVolume",
   598  			"metadata": map[string]interface{}{
   599  				"name": "someprefix-volume1",
   600  			},
   601  		})
   602  	c1 := rf.FromMapWithName(
   603  		"claim1",
   604  		map[string]interface{}{
   605  			"apiVersion": "v1",
   606  			"kind":       "PersistentVolumeClaim",
   607  			"metadata": map[string]interface{}{
   608  				"name":      "someprefix-claim1",
   609  				"namespace": "some-namespace",
   610  			},
   611  			"spec": map[string]interface{}{
   612  				"volumeName": "volume1",
   613  			},
   614  		})
   615  
   616  	v2 := v1.DeepCopy()
   617  	c2 := rf.FromMapWithName(
   618  		"claim1",
   619  		map[string]interface{}{
   620  			"apiVersion": "v1",
   621  			"kind":       "PersistentVolumeClaim",
   622  			"metadata": map[string]interface{}{
   623  				"name":      "someprefix-claim1",
   624  				"namespace": "some-namespace",
   625  			},
   626  			"spec": map[string]interface{}{
   627  				"volumeName": "someprefix-volume1",
   628  			},
   629  		})
   630  
   631  	m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(v1).AddR(c1).ResMap()
   632  
   633  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
   634  	if err := nrt.Transform(m1); err != nil {
   635  		t.Fatalf("unexpected error: %v", err)
   636  	}
   637  
   638  	m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(v2).AddR(c2).ResMap()
   639  	v2.AppendRefBy(c2.CurId())
   640  
   641  	if err := m1.ErrorIfNotEqualLists(m2); err != nil {
   642  		t.Fatalf(notEqualErrFmt, err)
   643  	}
   644  }
   645  
   646  // utility map to create a deployment object
   647  // with (metadatanamespace, metadataname) as key
   648  // and pointing to "refname" secret and configmap
   649  func deploymentMap(metadatanamespace string, metadataname string,
   650  	configmapref string, secretref string) map[string]interface{} {
   651  	deployment := map[string]interface{}{
   652  		"group":      "apps",
   653  		"apiVersion": "v1",
   654  		"kind":       "Deployment",
   655  		"metadata": map[string]interface{}{
   656  			"name": metadataname,
   657  		},
   658  		"spec": map[string]interface{}{
   659  			"template": map[string]interface{}{
   660  				"spec": map[string]interface{}{
   661  					"containers": []interface{}{
   662  						map[string]interface{}{
   663  							"name":  "nginx",
   664  							"image": "nginx:1.7.9",
   665  							"env": []interface{}{
   666  								map[string]interface{}{
   667  									"name": "CM_FOO",
   668  									"valueFrom": map[string]interface{}{
   669  										"configMapKeyRef": map[string]interface{}{
   670  											"name": configmapref,
   671  											"key":  "somekey",
   672  										},
   673  									},
   674  								},
   675  								map[string]interface{}{
   676  									"name": "SECRET_FOO",
   677  									"valueFrom": map[string]interface{}{
   678  										"secretKeyRef": map[string]interface{}{
   679  											"name": secretref,
   680  											"key":  "somekey",
   681  										},
   682  									},
   683  								},
   684  							},
   685  						},
   686  					},
   687  				},
   688  			},
   689  		},
   690  	}
   691  
   692  	if metadatanamespace != "" {
   693  		metadata := deployment["metadata"].(map[string]interface{})
   694  		metadata["namespace"] = metadatanamespace
   695  	}
   696  	return deployment
   697  }
   698  
   699  const (
   700  	defaultNs = "default"
   701  	ns1       = "ns1"
   702  	ns2       = "ns2"
   703  	ns3       = "ns3"
   704  	ns4       = "ns4"
   705  
   706  	orgname      = "uniquename"
   707  	prefixedname = "prefix-uniquename"
   708  	suffixedname = "uniquename-suffix"
   709  	modifiedname = "modifiedname"
   710  )
   711  
   712  // TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding
   713  // object with the same original names (uniquename) in different namespaces
   714  // and with different current Id.
   715  func TestNameReferenceNamespace(t *testing.T) {
   716  	m := resmaptest_test.NewRmBuilderDefault(t).
   717  		// Add ConfigMap with the same org name in noNs, "ns1" and "ns2" namespaces
   718  		AddWithName(orgname, map[string]interface{}{
   719  			"apiVersion": "v1",
   720  			"kind":       "ConfigMap",
   721  			"metadata": map[string]interface{}{
   722  				"name": modifiedname,
   723  			}}).
   724  		AddWithNsAndName(ns1, orgname, map[string]interface{}{
   725  			"apiVersion": "v1",
   726  			"kind":       "ConfigMap",
   727  			"metadata": map[string]interface{}{
   728  				"name":      prefixedname,
   729  				"namespace": ns1,
   730  			}}).
   731  		AddWithNsAndName(ns2, orgname, map[string]interface{}{
   732  			"apiVersion": "v1",
   733  			"kind":       "ConfigMap",
   734  			"metadata": map[string]interface{}{
   735  				"name":      suffixedname,
   736  				"namespace": ns2,
   737  			}}).
   738  		// Add Secret with the same org name in noNs, "ns1" and "ns2" namespaces
   739  		AddWithNsAndName(defaultNs, orgname, map[string]interface{}{
   740  			"apiVersion": "v1",
   741  			"kind":       "Secret",
   742  			"metadata": map[string]interface{}{
   743  				"name":      modifiedname,
   744  				"namespace": defaultNs,
   745  			}}).
   746  		AddWithNsAndName(ns1, orgname, map[string]interface{}{
   747  			"apiVersion": "v1",
   748  			"kind":       "Secret",
   749  			"metadata": map[string]interface{}{
   750  				"name":      prefixedname,
   751  				"namespace": ns1,
   752  			}}).
   753  		AddWithNsAndName(ns2, orgname, map[string]interface{}{
   754  			"apiVersion": "v1",
   755  			"kind":       "Secret",
   756  			"metadata": map[string]interface{}{
   757  				"name":      suffixedname,
   758  				"namespace": ns2,
   759  			}}).
   760  		// Add Deployment with the same org name in noNs, "ns1" and "ns2" namespaces
   761  		AddWithNsAndName(defaultNs, orgname, deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)).
   762  		AddWithNsAndName(ns1, orgname, deploymentMap(ns1, prefixedname, orgname, orgname)).
   763  		AddWithNsAndName(ns2, orgname, deploymentMap(ns2, suffixedname, orgname, orgname)).ResMap()
   764  
   765  	expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
   766  		ReplaceResource(deploymentMap(defaultNs, modifiedname, modifiedname, modifiedname)).
   767  		ReplaceResource(deploymentMap(ns1, prefixedname, prefixedname, prefixedname)).
   768  		ReplaceResource(deploymentMap(ns2, suffixedname, suffixedname, suffixedname)).ResMap()
   769  
   770  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
   771  	err := nrt.Transform(m)
   772  	if err != nil {
   773  		t.Fatalf("unexpected error: %v", err)
   774  	}
   775  
   776  	m.RemoveBuildAnnotations()
   777  	if err = expected.ErrorIfNotEqualLists(m); err != nil {
   778  		t.Fatalf(notEqualErrFmt, err)
   779  	}
   780  }
   781  
   782  // TestNameReferenceNamespace creates serviceAccount and clusterRoleBinding
   783  // object with the same original names (uniquename) in different namespaces
   784  // and with different current Id.
   785  func TestNameReferenceClusterWide(t *testing.T) {
   786  	m := resmaptest_test.NewRmBuilderDefault(t).
   787  		// Add ServiceAccount with the same org name in noNs, "ns1" and "ns2" namespaces
   788  		AddWithName(orgname, map[string]interface{}{
   789  			"apiVersion": "v1",
   790  			"kind":       "ServiceAccount",
   791  			"metadata": map[string]interface{}{
   792  				"name": modifiedname,
   793  			}}).
   794  		AddWithNsAndName(ns1, orgname, map[string]interface{}{
   795  			"apiVersion": "v1",
   796  			"kind":       "ServiceAccount",
   797  			"metadata": map[string]interface{}{
   798  				"name":      prefixedname,
   799  				"namespace": ns1,
   800  			}}).
   801  		AddWithNsAndName(ns2, orgname, map[string]interface{}{
   802  			"apiVersion": "v1",
   803  			"kind":       "ServiceAccount",
   804  			"metadata": map[string]interface{}{
   805  				"name":      suffixedname,
   806  				"namespace": ns2,
   807  			}}).
   808  		// Add a PersistentVolume to have a clusterwide resource
   809  		AddWithName(orgname, map[string]interface{}{
   810  			"apiVersion": "v1",
   811  			"kind":       "PersistentVolume",
   812  			"metadata": map[string]interface{}{
   813  				"name": modifiedname,
   814  			}}).
   815  		AddWithName(orgname, map[string]interface{}{
   816  			"apiVersion": "rbac.authorization.k8s.io/v1",
   817  			"kind":       "ClusterRole",
   818  			"metadata": map[string]interface{}{
   819  				"name": modifiedname,
   820  			},
   821  			"rules": []interface{}{
   822  				map[string]interface{}{
   823  					"resources": []interface{}{
   824  						"persistentvolumes",
   825  					},
   826  					"resourceNames": []interface{}{
   827  						orgname,
   828  					},
   829  				},
   830  			}}).
   831  		AddWithName(orgname, map[string]interface{}{
   832  			"apiVersion": "rbac.authorization.k8s.io/v1",
   833  			"kind":       "ClusterRoleBinding",
   834  			"metadata": map[string]interface{}{
   835  				"name": modifiedname,
   836  			},
   837  			"roleRef": map[string]interface{}{
   838  				"apiGroup": "rbac.authorization.k8s.io",
   839  				"kind":     "ClusterRole",
   840  				"name":     orgname,
   841  			},
   842  			"subjects": []interface{}{
   843  				map[string]interface{}{
   844  					"kind":      "ServiceAccount",
   845  					"name":      orgname,
   846  					"namespace": defaultNs,
   847  				},
   848  				map[string]interface{}{
   849  					"kind":      "ServiceAccount",
   850  					"name":      orgname,
   851  					"namespace": ns1,
   852  				},
   853  				map[string]interface{}{
   854  					"kind":      "ServiceAccount",
   855  					"name":      orgname,
   856  					"namespace": ns2,
   857  				},
   858  				map[string]interface{}{
   859  					"kind":      "ServiceAccount",
   860  					"name":      orgname,
   861  					"namespace": "random",
   862  				},
   863  			}}).ResMap()
   864  
   865  	expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
   866  		ReplaceResource(
   867  			map[string]interface{}{
   868  				"apiVersion": "rbac.authorization.k8s.io/v1",
   869  				"kind":       "ClusterRole",
   870  				"metadata": map[string]interface{}{
   871  					"name": modifiedname,
   872  				},
   873  				// Behavior of the transformer is still imperfect
   874  				// It should use the (resources,apigroup,resourceNames) as
   875  				// combination to select the candidates.
   876  				"rules": []interface{}{
   877  					map[string]interface{}{
   878  						"resources": []interface{}{
   879  							"persistentvolumes",
   880  						},
   881  						"resourceNames": []interface{}{
   882  							modifiedname,
   883  						},
   884  					},
   885  				}}).
   886  		ReplaceResource(
   887  			map[string]interface{}{
   888  				"apiVersion": "rbac.authorization.k8s.io/v1",
   889  				"kind":       "ClusterRoleBinding",
   890  				"metadata": map[string]interface{}{
   891  					"name": modifiedname,
   892  				},
   893  				"roleRef": map[string]interface{}{
   894  					"apiGroup": "rbac.authorization.k8s.io",
   895  					"kind":     "ClusterRole",
   896  					"name":     modifiedname,
   897  				},
   898  				// The following tests required a change in
   899  				// getNameFunc implementation in order to leverage
   900  				// the namespace field.
   901  				"subjects": []interface{}{
   902  					map[string]interface{}{
   903  						"kind":      "ServiceAccount",
   904  						"name":      modifiedname,
   905  						"namespace": defaultNs,
   906  					},
   907  					map[string]interface{}{
   908  						"kind":      "ServiceAccount",
   909  						"name":      prefixedname,
   910  						"namespace": ns1,
   911  					},
   912  					map[string]interface{}{
   913  						"kind":      "ServiceAccount",
   914  						"name":      suffixedname,
   915  						"namespace": ns2,
   916  					},
   917  					map[string]interface{}{
   918  						"kind":      "ServiceAccount",
   919  						"name":      orgname,
   920  						"namespace": "random",
   921  					},
   922  				},
   923  			}).ResMap()
   924  
   925  	clusterRoleId := resid.NewResId(
   926  		resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"), modifiedname)
   927  	clusterRoleBindingId := resid.NewResId(
   928  		resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), modifiedname)
   929  	clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
   930  	clusterRole.AppendRefBy(clusterRoleBindingId)
   931  
   932  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
   933  	err := nrt.Transform(m)
   934  	if err != nil {
   935  		t.Fatalf("unexpected error: %v", err)
   936  	}
   937  
   938  	expected.RemoveBuildAnnotations()
   939  	m.RemoveBuildAnnotations()
   940  
   941  	if err = expected.ErrorIfNotEqualLists(m); err != nil {
   942  		t.Fatalf(notEqualErrFmt, err)
   943  	}
   944  }
   945  
   946  // TestNameReferenceNamespaceTransformation creates serviceAccount and clusterRoleBinding
   947  // object with the same original names (uniquename) in different namespaces
   948  // and with different current Id.
   949  func TestNameReferenceNamespaceTransformation(t *testing.T) {
   950  	m := resmaptest_test.NewRmBuilderDefault(t).
   951  		AddWithNsAndName(ns4, orgname, map[string]interface{}{
   952  			"apiVersion": "v1",
   953  			"kind":       "Secret",
   954  			"metadata": map[string]interface{}{
   955  				"name":      orgname,
   956  				"namespace": ns4,
   957  			}}).
   958  		// Add ServiceAccount with the same org name in "ns1" namespaces
   959  		AddWithNsAndName(ns1, orgname, map[string]interface{}{
   960  			"apiVersion": "v1",
   961  			"kind":       "ServiceAccount",
   962  			"metadata": map[string]interface{}{
   963  				"name":      prefixedname,
   964  				"namespace": ns1,
   965  			}}).
   966  		// Simulate NamespaceTransformer effect (ns3 transformed in ns2)
   967  		AddWithNsAndName(ns3, orgname, map[string]interface{}{
   968  			"apiVersion": "v1",
   969  			"kind":       "ServiceAccount",
   970  			"metadata": map[string]interface{}{
   971  				"name":      suffixedname,
   972  				"namespace": ns2,
   973  			}}).
   974  		AddWithName(orgname, map[string]interface{}{
   975  			"apiVersion": "rbac.authorization.k8s.io/v1",
   976  			"kind":       "ClusterRole",
   977  			"metadata": map[string]interface{}{
   978  				"name": modifiedname,
   979  			}}).
   980  		AddWithName(orgname, map[string]interface{}{
   981  			"apiVersion": "rbac.authorization.k8s.io/v1",
   982  			"kind":       "ClusterRoleBinding",
   983  			"metadata": map[string]interface{}{
   984  				"name": modifiedname,
   985  			},
   986  			"roleRef": map[string]interface{}{
   987  				"apiGroup": "rbac.authorization.k8s.io",
   988  				"kind":     "ClusterRole",
   989  				"name":     orgname,
   990  			},
   991  			"subjects": []interface{}{
   992  				map[string]interface{}{
   993  					"kind":      "ServiceAccount",
   994  					"name":      orgname,
   995  					"namespace": ns1,
   996  				},
   997  				map[string]interface{}{
   998  					"kind":      "ServiceAccount",
   999  					"name":      orgname,
  1000  					"namespace": ns3,
  1001  				},
  1002  				map[string]interface{}{
  1003  					"kind":      "ServiceAccount",
  1004  					"name":      orgname,
  1005  					"namespace": "random",
  1006  				},
  1007  				map[string]interface{}{
  1008  					"kind":      "ServiceAccount",
  1009  					"name":      orgname,
  1010  					"namespace": ns4,
  1011  				},
  1012  			}}).ResMap()
  1013  
  1014  	expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
  1015  		ReplaceResource(
  1016  			map[string]interface{}{
  1017  				"apiVersion": "rbac.authorization.k8s.io/v1",
  1018  				"kind":       "ClusterRoleBinding",
  1019  				"metadata": map[string]interface{}{
  1020  					"name": modifiedname,
  1021  				},
  1022  				"roleRef": map[string]interface{}{
  1023  					"apiGroup": "rbac.authorization.k8s.io",
  1024  					"kind":     "ClusterRole",
  1025  					"name":     modifiedname,
  1026  				},
  1027  				// The following tests required a change in
  1028  				// getNameFunc implementation in order to leverage
  1029  				// the namespace field.
  1030  				"subjects": []interface{}{
  1031  					map[string]interface{}{
  1032  						"kind":      "ServiceAccount",
  1033  						"name":      prefixedname,
  1034  						"namespace": ns1,
  1035  					},
  1036  					map[string]interface{}{
  1037  						"kind":      "ServiceAccount",
  1038  						"name":      suffixedname,
  1039  						"namespace": ns2,
  1040  					},
  1041  					map[string]interface{}{
  1042  						"kind":      "ServiceAccount",
  1043  						"name":      orgname,
  1044  						"namespace": "random",
  1045  					},
  1046  					map[string]interface{}{
  1047  						"kind":      "ServiceAccount",
  1048  						"name":      orgname,
  1049  						"namespace": ns4,
  1050  					},
  1051  				},
  1052  			}).ResMap()
  1053  
  1054  	clusterRoleId := resid.NewResId(
  1055  		resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"),
  1056  		modifiedname)
  1057  	clusterRoleBindingId := resid.NewResId(
  1058  		resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
  1059  		modifiedname)
  1060  	clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
  1061  	clusterRole.AppendRefBy(clusterRoleBindingId)
  1062  
  1063  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
  1064  	err := nrt.Transform(m)
  1065  	if err != nil {
  1066  		t.Fatalf("unexpected error: %v", err)
  1067  	}
  1068  
  1069  	m.RemoveBuildAnnotations()
  1070  	if err = expected.ErrorIfNotEqualLists(m); err != nil {
  1071  		t.Fatalf(notEqualErrFmt, err)
  1072  	}
  1073  }
  1074  
  1075  // TestNameReferenceNamespace creates configmap, secret, deployment
  1076  // It validates the change done is IsSameFuzzyNamespace which
  1077  // uses the IsNsEquals method instead of the simple == operator.
  1078  func TestNameReferenceCandidateSelection(t *testing.T) {
  1079  	m := resmaptest_test.NewRmBuilderDefault(t).
  1080  		AddWithName("cm1", map[string]interface{}{
  1081  			"apiVersion": "v1",
  1082  			"kind":       "ConfigMap",
  1083  			"metadata": map[string]interface{}{
  1084  				"name": "p1-cm1-hash",
  1085  			}}).
  1086  		AddWithNsAndName("default", "secret1", map[string]interface{}{
  1087  			"apiVersion": "v1",
  1088  			"kind":       "Secret",
  1089  			"metadata": map[string]interface{}{
  1090  				"name":      "p1-secret1-hash",
  1091  				"namespace": "default",
  1092  			}}).
  1093  		AddWithName("deploy1", deploymentMap("", "p1-deploy1", "cm1", "secret1")).
  1094  		ResMap()
  1095  
  1096  	expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
  1097  		ReplaceResource(deploymentMap("", "p1-deploy1", "p1-cm1-hash", "p1-secret1-hash")).
  1098  		ResMap()
  1099  
  1100  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
  1101  	err := nrt.Transform(m)
  1102  	if err != nil {
  1103  		t.Fatalf("unexpected error: %v", err)
  1104  	}
  1105  
  1106  	m.RemoveBuildAnnotations()
  1107  	if err = expected.ErrorIfNotEqualLists(m); err != nil {
  1108  		t.Fatalf(notEqualErrFmt, err)
  1109  	}
  1110  }
  1111  
  1112  func TestNameReferenceCandidateDisambiguationByNamespace(t *testing.T) {
  1113  	// The ClusterRole refers to both configmaps, since it is not namespace-specific.
  1114  	// Since both names are updated consistently, the transformer should be able to
  1115  	// silently update the ClusterRole as well.
  1116  	// This test guards against a regression where allNamesAndNamespacesAreTheSame would be
  1117  	// used to detect referral candidate identity instead of allNamesAreTheSame.
  1118  	m := resmaptest_test.NewRmBuilderDefault(t).AddWithName(orgname,
  1119  		map[string]interface{}{
  1120  			"apiVersion": "v1",
  1121  			"kind":       "ConfigMap",
  1122  			"metadata": map[string]interface{}{
  1123  				"name":      suffixedname,
  1124  				"namespace": ns1,
  1125  			},
  1126  		},
  1127  	).AddWithName(orgname,
  1128  		map[string]interface{}{
  1129  			"apiVersion": "v1",
  1130  			"kind":       "ConfigMap",
  1131  			"metadata": map[string]interface{}{
  1132  				"name":      suffixedname,
  1133  				"namespace": ns2,
  1134  			},
  1135  		},
  1136  	).Add(
  1137  		map[string]interface{}{
  1138  			"apiVersion": "rbac.authorization.k8s.io/v1",
  1139  			"kind":       "ClusterRole",
  1140  			"metadata": map[string]interface{}{
  1141  				"name": orgname,
  1142  			},
  1143  			"rules": []interface{}{
  1144  				map[string]interface{}{
  1145  					"resources": []interface{}{
  1146  						"configmaps",
  1147  					},
  1148  					"resourceNames": []interface{}{
  1149  						orgname,
  1150  					},
  1151  				},
  1152  			},
  1153  		},
  1154  	).ResMap()
  1155  
  1156  	expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
  1157  		ReplaceResource(map[string]interface{}{
  1158  			"apiVersion": "rbac.authorization.k8s.io/v1",
  1159  			"kind":       "ClusterRole",
  1160  			"metadata": map[string]interface{}{
  1161  				"name": orgname,
  1162  			},
  1163  			"rules": []interface{}{
  1164  				map[string]interface{}{
  1165  					"resources": []interface{}{
  1166  						"configmaps",
  1167  					},
  1168  					"resourceNames": []interface{}{
  1169  						suffixedname,
  1170  					},
  1171  				},
  1172  			},
  1173  		},
  1174  		).ResMap()
  1175  
  1176  	nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
  1177  	require.NoError(t, nrt.Transform(m))
  1178  
  1179  	m.RemoveBuildAnnotations()
  1180  	if err := expected.ErrorIfNotEqualLists(m); err != nil {
  1181  		t.Fatalf(notEqualErrFmt, err)
  1182  	}
  1183  }
  1184  

View as plain text