...

Source file src/sigs.k8s.io/kustomize/api/resource/resource_test.go

Documentation: sigs.k8s.io/kustomize/api/resource

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package resource_test
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	"sigs.k8s.io/kustomize/api/internal/utils"
    14  	"sigs.k8s.io/kustomize/api/provider"
    15  	. "sigs.k8s.io/kustomize/api/resource"
    16  	"sigs.k8s.io/kustomize/api/types"
    17  	"sigs.k8s.io/kustomize/kyaml/resid"
    18  	kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
    19  )
    20  
    21  var factory = provider.NewDefaultDepProvider().GetResourceFactory()
    22  
    23  var testConfigMap = factory.FromMap(
    24  	map[string]interface{}{
    25  		"apiVersion": "v1",
    26  		"kind":       "ConfigMap",
    27  		"metadata": map[string]interface{}{
    28  			"name":      "winnie",
    29  			"namespace": "hundred-acre-wood",
    30  		},
    31  	})
    32  
    33  //nolint:gosec
    34  const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
    35  
    36  var testDeployment = factory.FromMap(
    37  	map[string]interface{}{
    38  		"apiVersion": "apps/v1",
    39  		"kind":       "Deployment",
    40  		"metadata": map[string]interface{}{
    41  			"name": "pooh",
    42  		},
    43  	})
    44  
    45  const deploymentAsString = `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
    46  
    47  func TestAsYAML(t *testing.T) {
    48  	expected := `apiVersion: apps/v1
    49  kind: Deployment
    50  metadata:
    51    name: pooh
    52  `
    53  	yaml, err := testDeployment.AsYAML()
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	if string(yaml) != expected {
    58  		t.Fatalf("--- expected\n%s\n--- got\n%s\n", expected, string(yaml))
    59  	}
    60  }
    61  
    62  func TestResourceString(t *testing.T) {
    63  	tests := []struct {
    64  		in *Resource
    65  		s  string
    66  	}{
    67  		{
    68  			in: testConfigMap,
    69  			s:  configMapAsString,
    70  		},
    71  		{
    72  			in: testDeployment,
    73  			s:  deploymentAsString,
    74  		},
    75  	}
    76  	for _, test := range tests {
    77  		assert.Equal(t, test.in.String(), test.s)
    78  	}
    79  }
    80  
    81  func TestResourceId(t *testing.T) {
    82  	tests := []struct {
    83  		in *Resource
    84  		id resid.ResId
    85  	}{
    86  		{
    87  			in: testConfigMap,
    88  			id: resid.NewResIdWithNamespace(
    89  				resid.NewGvk("", "v1", "ConfigMap"),
    90  				"winnie", "hundred-acre-wood"),
    91  		},
    92  		{
    93  			in: testDeployment,
    94  			id: resid.NewResId(
    95  				resid.NewGvk("apps", "v1", "Deployment"), "pooh"),
    96  		},
    97  	}
    98  	for _, test := range tests {
    99  		if test.in.OrgId() != test.id {
   100  			t.Fatalf("Expected %v, but got %v\n", test.id, test.in.OrgId())
   101  		}
   102  	}
   103  }
   104  
   105  func TestDeepCopy(t *testing.T) {
   106  	r := factory.FromMap(
   107  		map[string]interface{}{
   108  			"apiVersion": "apps/v1",
   109  			"kind":       "Deployment",
   110  			"metadata": map[string]interface{}{
   111  				"name": "pooh",
   112  			},
   113  		})
   114  	r.AppendRefBy(resid.NewResId(resid.Gvk{Group: "somegroup", Kind: "MyKind"}, "random"))
   115  
   116  	var1 := types.Var{
   117  		Name: "SERVICE_ONE",
   118  		ObjRef: types.Target{
   119  			Gvk:  resid.Gvk{Version: "v1", Kind: "Service"},
   120  			Name: "backendOne"},
   121  	}
   122  	r.AppendRefVarName(var1)
   123  
   124  	cr := r.DeepCopy()
   125  	if !reflect.DeepEqual(r, cr) {
   126  		t.Errorf("expected %v\nbut got%v", r, cr)
   127  	}
   128  }
   129  
   130  func TestApplySmPatch_1(t *testing.T) {
   131  	resource, err := factory.FromBytes([]byte(`
   132  apiVersion: apps/v1
   133  kind: Deployment
   134  metadata:
   135    annotations:
   136      baseAnno: This is a base annotation
   137    labels:
   138      app: mungebot
   139      foo: bar
   140    name: bingo
   141  spec:
   142    replicas: 1
   143    selector:
   144      matchLabels:
   145        foo: bar
   146    template:
   147      metadata:
   148        labels:
   149          app: mungebot
   150      spec:
   151        containers:
   152        - env:
   153          - name: foo
   154            value: bar
   155          image: nginx
   156          name: nginx
   157          ports:
   158          - containerPort: 80
   159  `))
   160  	require.NoError(t, err)
   161  	patch, err := factory.FromBytes([]byte(`
   162  apiVersion: apps/v1
   163  kind: Deployment
   164  metadata:
   165    name: baseprefix-mungebot
   166  spec:
   167    template:
   168      spec:
   169        containers:
   170        - image: nginx
   171          name: nginx
   172          ports:
   173          - containerPort: 777
   174  `))
   175  	require.NoError(t, err)
   176  
   177  	require.NoError(t, resource.ApplySmPatch(patch))
   178  	bytes, err := resource.AsYAML()
   179  	require.NoError(t, err)
   180  	assert.Equal(t, `apiVersion: apps/v1
   181  kind: Deployment
   182  metadata:
   183    annotations:
   184      baseAnno: This is a base annotation
   185    labels:
   186      app: mungebot
   187      foo: bar
   188    name: bingo
   189  spec:
   190    replicas: 1
   191    selector:
   192      matchLabels:
   193        foo: bar
   194    template:
   195      metadata:
   196        labels:
   197          app: mungebot
   198      spec:
   199        containers:
   200        - env:
   201          - name: foo
   202            value: bar
   203          image: nginx
   204          name: nginx
   205          ports:
   206          - containerPort: 777
   207          - containerPort: 80
   208  `, string(bytes))
   209  }
   210  
   211  func TestApplySmPatch_2(t *testing.T) {
   212  	resource, err := factory.FromBytes([]byte(`
   213  apiVersion: example.com/v1
   214  kind: Foo
   215  metadata:
   216    name: my-foo
   217  spec:
   218    bar:
   219      A: X
   220      B: Y
   221  `))
   222  	require.NoError(t, err)
   223  	patch, err := factory.FromBytes([]byte(`
   224  apiVersion: example.com/v1
   225  kind: Foo
   226  metadata:
   227    name: my-foo
   228  spec:
   229    bar:
   230      B:
   231      C: Z
   232      D: W
   233    baz:
   234      hello: world
   235  `))
   236  	require.NoError(t, err)
   237  	require.NoError(t, resource.ApplySmPatch(patch))
   238  	bytes, err := resource.AsYAML()
   239  	require.NoError(t, err)
   240  	assert.Equal(t, `apiVersion: example.com/v1
   241  kind: Foo
   242  metadata:
   243    name: my-foo
   244  spec:
   245    bar:
   246      A: X
   247      C: Z
   248      D: W
   249    baz:
   250      hello: world
   251  `, string(bytes))
   252  }
   253  
   254  func TestApplySmPatch_3(t *testing.T) {
   255  	resource, err := factory.FromBytes([]byte(`
   256  apiVersion: v1
   257  kind: Deployment
   258  metadata:
   259    name: clown
   260  spec:
   261    numReplicas: 1
   262  `))
   263  	require.NoError(t, err)
   264  	patch, err := factory.FromBytes([]byte(`
   265  apiVersion: v1
   266  kind: Deployment
   267  metadata:
   268    name: clown
   269  spec:
   270    numReplicas: 999
   271  `))
   272  	require.NoError(t, err)
   273  	require.NoError(t, resource.ApplySmPatch(patch))
   274  	bytes, err := resource.AsYAML()
   275  	require.NoError(t, err)
   276  	assert.Equal(t, `apiVersion: v1
   277  kind: Deployment
   278  metadata:
   279    name: clown
   280  spec:
   281    numReplicas: 999
   282  `, string(bytes))
   283  }
   284  
   285  // regression test for https://github.com/kubernetes-sigs/kustomize/issues/5031
   286  func TestApplySmPatch_Idempotency(t *testing.T) {
   287  	// an arbitrary number of times to apply the patch
   288  	patchApplyCount := 4
   289  	resourceYaml := `apiVersion: v1
   290  kind: Deployment
   291  metadata:
   292    creationTimestamp: null
   293    labels: null
   294    name: my-deployment
   295  `
   296  	resource, err := factory.FromBytes([]byte(resourceYaml))
   297  	require.NoError(t, err)
   298  
   299  	noOpPatch, err := factory.FromBytes([]byte(`
   300  apiVersion: v1
   301  kind: Deployment
   302  metadata:
   303    name: my-deployment
   304  `))
   305  	require.NoError(t, err)
   306  
   307  	for i := 0; i < patchApplyCount; i++ {
   308  		require.NoError(t, resource.ApplySmPatch(noOpPatch))
   309  
   310  		bytes, err := resource.AsYAML()
   311  		require.NoError(t, err)
   312  
   313  		require.Equal(
   314  			t,
   315  			resourceYaml,
   316  			string(bytes),
   317  			"resource should be unchanged after re-application of patch",
   318  		)
   319  	}
   320  }
   321  
   322  func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
   323  	cases := []struct {
   324  		name           string
   325  		skip           bool
   326  		patch          string
   327  		expectedOutput string
   328  	}{
   329  		{
   330  			name: "Order should not change when patch has foo only",
   331  			patch: `apiVersion: v1
   332  kind: Pod
   333  metadata:
   334    name: test
   335  spec:
   336    initContainers:
   337      - name: foo
   338  `,
   339  			expectedOutput: `apiVersion: v1
   340  kind: Pod
   341  metadata:
   342    name: test
   343  spec:
   344    initContainers:
   345    - name: foo
   346    - name: bar
   347  `,
   348  		},
   349  		{
   350  			name: "Order changes when patch has bar only",
   351  			patch: `apiVersion: v1
   352  kind: Pod
   353  metadata:
   354    name: test
   355  spec:
   356    initContainers:
   357      - name: bar
   358  `,
   359  			// This test records current behavior, but this behavior might be undesirable.
   360  			// If so, feel free to change the test to pass with some improved algorithm.
   361  			expectedOutput: `apiVersion: v1
   362  kind: Pod
   363  metadata:
   364    name: test
   365  spec:
   366    initContainers:
   367    - name: bar
   368    - name: foo
   369  `,
   370  		},
   371  		{
   372  			name: "Order should not change and should include a new item at the beginning when patch has a new list item",
   373  			patch: `apiVersion: v1
   374  kind: Pod
   375  metadata:
   376    name: test
   377  spec:
   378    initContainers:
   379      - name: baz
   380  `,
   381  			expectedOutput: `apiVersion: v1
   382  kind: Pod
   383  metadata:
   384    name: test
   385  spec:
   386    initContainers:
   387    - name: baz
   388    - name: foo
   389    - name: bar
   390  `,
   391  		},
   392  		{
   393  			name: "Order should not change when patch has foo and bar in same order",
   394  			patch: `apiVersion: v1
   395  kind: Pod
   396  metadata:
   397    name: test
   398  spec:
   399    initContainers:
   400      - name: foo
   401      - name: bar
   402  `,
   403  			expectedOutput: `apiVersion: v1
   404  kind: Pod
   405  metadata:
   406    name: test
   407  spec:
   408    initContainers:
   409    - name: foo
   410    - name: bar
   411  `,
   412  		},
   413  		{
   414  			name: "Order should change when patch has foo and bar in different order",
   415  			patch: `apiVersion: v1
   416  kind: Pod
   417  metadata:
   418    name: test
   419  spec:
   420    initContainers:
   421      - name: bar
   422      - name: foo
   423  `,
   424  			expectedOutput: `apiVersion: v1
   425  kind: Pod
   426  metadata:
   427    name: test
   428  spec:
   429    initContainers:
   430    - name: bar
   431    - name: foo
   432  `,
   433  		},
   434  	}
   435  	for _, tc := range cases {
   436  		t.Run(tc.name, func(t *testing.T) {
   437  			if tc.skip {
   438  				t.Skip()
   439  			}
   440  
   441  			resource, err := factory.FromBytes([]byte(`
   442  apiVersion: v1
   443  kind: Pod
   444  metadata:
   445    name: test
   446  spec:
   447    initContainers:
   448      - name: foo
   449      - name: bar
   450  `))
   451  			require.NoError(t, err)
   452  
   453  			patch, err := factory.FromBytes([]byte(tc.patch))
   454  			require.NoError(t, err)
   455  			require.NoError(t, resource.ApplySmPatch(patch))
   456  			bytes, err := resource.AsYAML()
   457  			require.NoError(t, err)
   458  			assert.Equal(t, tc.expectedOutput, string(bytes))
   459  		})
   460  	}
   461  }
   462  
   463  func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) {
   464  	cases := []struct {
   465  		name           string
   466  		skip           bool
   467  		patch          string
   468  		expectedOutput string
   469  	}{
   470  		{
   471  			name: "Order should not change when patch has foo only",
   472  			patch: `apiVersion: v1
   473  kind: Pod
   474  metadata:
   475    name: test
   476    finalizers: ["foo"]
   477  `,
   478  			expectedOutput: `apiVersion: v1
   479  kind: Pod
   480  metadata:
   481    finalizers:
   482    - foo
   483    - bar
   484    name: test
   485  `,
   486  		},
   487  		{
   488  			name: "Order should not change when patch has bar only",
   489  			skip: true, // TODO: This test should pass but fails currently. Fix the problem and unskip this test
   490  			patch: `apiVersion: v1
   491  kind: Pod
   492  metadata:
   493   name: test
   494   finalizers: ["bar"]
   495  `,
   496  			expectedOutput: `apiVersion: v1
   497  kind: Pod
   498  metadata:
   499   finalizers:
   500   - foo
   501   - bar
   502   name: test
   503  `,
   504  		},
   505  		{
   506  			name: "Order should not change and should include a new item at the beginning when patch has a new list item",
   507  			patch: `apiVersion: v1
   508  kind: Pod
   509  metadata:
   510    name: test
   511    finalizers: ["baz"]
   512  `,
   513  			expectedOutput: `apiVersion: v1
   514  kind: Pod
   515  metadata:
   516    finalizers:
   517    - baz
   518    - foo
   519    - bar
   520    name: test
   521  `,
   522  		},
   523  		{
   524  			name: "Order should not change when patch has foo and bar in same order",
   525  			patch: `apiVersion: v1
   526  kind: Pod
   527  metadata:
   528    name: test
   529    finalizers: ["foo", "bar"]
   530  `,
   531  			expectedOutput: `apiVersion: v1
   532  kind: Pod
   533  metadata:
   534    finalizers:
   535    - foo
   536    - bar
   537    name: test
   538  `,
   539  		},
   540  		{
   541  			name: "Order should change when patch has foo and bar in different order",
   542  			patch: `apiVersion: v1
   543  kind: Pod
   544  metadata:
   545    name: test
   546    finalizers: ["bar", "foo"]
   547  `,
   548  			expectedOutput: `apiVersion: v1
   549  kind: Pod
   550  metadata:
   551    finalizers:
   552    - bar
   553    - foo
   554    name: test
   555  `,
   556  		},
   557  	}
   558  	for _, tc := range cases {
   559  		t.Run(tc.name, func(t *testing.T) {
   560  			if tc.skip {
   561  				t.Skip()
   562  			}
   563  
   564  			resource, err := factory.FromBytes([]byte(`
   565  kind: Pod
   566  metadata:
   567    name: test
   568    finalizers: ["foo", "bar"]
   569  `))
   570  			require.NoError(t, err)
   571  
   572  			patch, err := factory.FromBytes([]byte(tc.patch))
   573  			require.NoError(t, err)
   574  			require.NoError(t, resource.ApplySmPatch(patch))
   575  			bytes, err := resource.AsYAML()
   576  			require.NoError(t, err)
   577  			assert.Equal(t, tc.expectedOutput, string(bytes))
   578  		})
   579  	}
   580  }
   581  
   582  func TestMergeDataMapFrom(t *testing.T) {
   583  	resource, err := factory.FromBytes([]byte(`
   584  apiVersion: v1
   585  kind: BlahBlah
   586  metadata:
   587    name: clown
   588  data:
   589    fruit: pear
   590  `))
   591  	if !assert.NoError(t, err) {
   592  		t.FailNow()
   593  	}
   594  	patch, err := factory.FromBytes([]byte(`
   595  apiVersion: v1
   596  kind: Whatever
   597  metadata:
   598    name: spaceship
   599  data:
   600    spaceship: enterprise
   601  `))
   602  	if !assert.NoError(t, err) {
   603  		t.FailNow()
   604  	}
   605  	resource.MergeDataMapFrom(patch)
   606  	bytes, err := resource.AsYAML()
   607  	require.NoError(t, err)
   608  	assert.Equal(t, `apiVersion: v1
   609  data:
   610    fruit: pear
   611    spaceship: enterprise
   612  kind: BlahBlah
   613  metadata:
   614    name: clown
   615  `, string(bytes))
   616  }
   617  
   618  func TestApplySmPatch_SwapOrder(t *testing.T) {
   619  	s1 := `
   620  apiVersion: example.com/v1
   621  kind: Foo
   622  metadata:
   623    name: my-foo
   624  spec:
   625    bar:
   626      B:
   627      C: Z
   628  `
   629  	s2 := `
   630  apiVersion: example.com/v1
   631  kind: Foo
   632  metadata:
   633    name: my-foo
   634  spec:
   635    bar:
   636      C: Z
   637      D: W
   638    baz:
   639      hello: world
   640  `
   641  	expected := `apiVersion: example.com/v1
   642  kind: Foo
   643  metadata:
   644    name: my-foo
   645  spec:
   646    bar:
   647      C: Z
   648      D: W
   649    baz:
   650      hello: world
   651  `
   652  	r1, err := factory.FromBytes([]byte(s1))
   653  	require.NoError(t, err)
   654  	r2, err := factory.FromBytes([]byte(s2))
   655  	require.NoError(t, err)
   656  	require.NoError(t, r1.ApplySmPatch(r2))
   657  	bytes, err := r1.AsYAML()
   658  	require.NoError(t, err)
   659  	assert.Equal(t, expected, string(bytes))
   660  
   661  	r1, _ = factory.FromBytes([]byte(s1))
   662  	r2, _ = factory.FromBytes([]byte(s2))
   663  	require.NoError(t, r2.ApplySmPatch(r1))
   664  	bytes, err = r2.AsYAML()
   665  	require.NoError(t, err)
   666  	assert.Equal(t, expected, string(bytes))
   667  }
   668  
   669  func TestApplySmPatch(t *testing.T) {
   670  	const (
   671  		myDeployment = "Deployment"
   672  		myCRD        = "myCRD"
   673  	)
   674  
   675  	tests := map[string]struct {
   676  		base          string
   677  		patch         []string
   678  		expected      string
   679  		errorExpected bool
   680  		errorMsg      string
   681  	}{
   682  		"withschema-label-image-container": {
   683  			base: baseResource(myDeployment),
   684  			patch: []string{
   685  				addLabelAndEnvPatch(myDeployment),
   686  				changeImagePatch(myDeployment, "nginx:latest"),
   687  				addContainerAndEnvPatch(myDeployment),
   688  			},
   689  			errorExpected: false,
   690  			expected:      expectedResultMultiPatch(myDeployment, false),
   691  		},
   692  		"withschema-image-container-label": {
   693  			base: baseResource(myDeployment),
   694  			patch: []string{
   695  				changeImagePatch(myDeployment, "nginx:latest"),
   696  				addContainerAndEnvPatch(myDeployment),
   697  				addLabelAndEnvPatch(myDeployment),
   698  			},
   699  			errorExpected: false,
   700  			expected:      expectedResultMultiPatch(myDeployment, true),
   701  		},
   702  		"withschema-container-label-image": {
   703  			base: baseResource(myDeployment),
   704  			patch: []string{
   705  				addContainerAndEnvPatch(myDeployment),
   706  				addLabelAndEnvPatch(myDeployment),
   707  				changeImagePatch(myDeployment, "nginx:latest"),
   708  			},
   709  			errorExpected: false,
   710  			expected:      expectedResultMultiPatch(myDeployment, true),
   711  		},
   712  		"noschema-label-image-container": {
   713  			base: baseResource(myCRD),
   714  			patch: []string{
   715  				addLabelAndEnvPatch(myCRD),
   716  				changeImagePatch(myCRD, "nginx:latest"),
   717  				addContainerAndEnvPatch(myCRD),
   718  			},
   719  			// Might be better if this complained about patch conflict.
   720  			// See plugin/builtin/patchstrategicmergetransformer/psmt_test.go
   721  			expected: `apiVersion: apps/v1
   722  kind: myCRD
   723  metadata:
   724    name: deploy1
   725  spec:
   726    template:
   727      metadata:
   728        labels:
   729          old-label: old-value
   730          some-label: some-value
   731      spec:
   732        containers:
   733        - env:
   734          - name: ANOTHERENV
   735            value: ANOTHERVALUE
   736          name: nginx
   737        - image: anotherimage
   738          name: anothercontainer
   739  `,
   740  		},
   741  		"noschema-image-container-label": {
   742  			base: baseResource(myCRD),
   743  			patch: []string{
   744  				changeImagePatch(myCRD, "nginx:latest"),
   745  				addContainerAndEnvPatch(myCRD),
   746  				addLabelAndEnvPatch(myCRD),
   747  			},
   748  			// Might be better if this complained about patch conflict.
   749  			expected: `apiVersion: apps/v1
   750  kind: myCRD
   751  metadata:
   752    name: deploy1
   753  spec:
   754    template:
   755      metadata:
   756        labels:
   757          old-label: old-value
   758          some-label: some-value
   759      spec:
   760        containers:
   761        - env:
   762          - name: SOMEENV
   763            value: SOMEVALUE
   764          name: nginx
   765  `,
   766  		},
   767  		"noschema-container-label-image": {
   768  			base: baseResource(myCRD),
   769  			patch: []string{
   770  				addContainerAndEnvPatch(myCRD),
   771  				addLabelAndEnvPatch(myCRD),
   772  				changeImagePatch(myCRD, "nginx:latest"),
   773  			},
   774  			// Might be better if this complained about patch conflict.
   775  			expected: `apiVersion: apps/v1
   776  kind: myCRD
   777  metadata:
   778    name: deploy1
   779  spec:
   780    template:
   781      metadata:
   782        labels:
   783          old-label: old-value
   784          some-label: some-value
   785      spec:
   786        containers:
   787        - image: nginx:latest
   788          name: nginx
   789  `,
   790  		},
   791  
   792  		"withschema-label-latest-someV-01": {
   793  			base: baseResource(myDeployment),
   794  			patch: []string{
   795  				addLabelAndEnvPatch(myDeployment),
   796  				changeImagePatch(myDeployment, "nginx:latest"),
   797  				changeImagePatch(myDeployment, "nginx:1.7.9"),
   798  			},
   799  			expected: `apiVersion: apps/v1
   800  kind: Deployment
   801  metadata:
   802    name: deploy1
   803  spec:
   804    template:
   805      metadata:
   806        labels:
   807          old-label: old-value
   808          some-label: some-value
   809      spec:
   810        containers:
   811        - env:
   812          - name: SOMEENV
   813            value: SOMEVALUE
   814          image: nginx:1.7.9
   815          name: nginx
   816  `,
   817  		},
   818  		"withschema-latest-label-someV-02": {
   819  			base: baseResource(myDeployment),
   820  			patch: []string{
   821  				changeImagePatch(myDeployment, "nginx:latest"),
   822  				addLabelAndEnvPatch(myDeployment),
   823  				changeImagePatch(myDeployment, "nginx:1.7.9"),
   824  			},
   825  			expected: `apiVersion: apps/v1
   826  kind: Deployment
   827  metadata:
   828    name: deploy1
   829  spec:
   830    template:
   831      metadata:
   832        labels:
   833          old-label: old-value
   834          some-label: some-value
   835      spec:
   836        containers:
   837        - env:
   838          - name: SOMEENV
   839            value: SOMEVALUE
   840          image: nginx:1.7.9
   841          name: nginx
   842  `,
   843  		},
   844  		"withschema-latest-label-someV-03": {
   845  			base: baseResource(myDeployment),
   846  			patch: []string{
   847  				changeImagePatch(myDeployment, "nginx:1.7.9"),
   848  				addLabelAndEnvPatch(myDeployment),
   849  				changeImagePatch(myDeployment, "nginx:latest"),
   850  			},
   851  			expected: `apiVersion: apps/v1
   852  kind: Deployment
   853  metadata:
   854    name: deploy1
   855  spec:
   856    template:
   857      metadata:
   858        labels:
   859          old-label: old-value
   860          some-label: some-value
   861      spec:
   862        containers:
   863        - env:
   864          - name: SOMEENV
   865            value: SOMEVALUE
   866          image: nginx:latest
   867          name: nginx
   868  `,
   869  		},
   870  		"withschema-latest-label-someV-04": {
   871  			base: baseResource(myDeployment),
   872  			patch: []string{
   873  				changeImagePatch(myDeployment, "nginx:1.7.9"),
   874  				changeImagePatch(myDeployment, "nginx:latest"),
   875  				addLabelAndEnvPatch(myDeployment),
   876  				changeImagePatch(myDeployment, "nginx:nginx"),
   877  			},
   878  			expected: `apiVersion: apps/v1
   879  kind: Deployment
   880  metadata:
   881    name: deploy1
   882  spec:
   883    template:
   884      metadata:
   885        labels:
   886          old-label: old-value
   887          some-label: some-value
   888      spec:
   889        containers:
   890        - env:
   891          - name: SOMEENV
   892            value: SOMEVALUE
   893          image: nginx:nginx
   894          name: nginx
   895  `,
   896  		},
   897  		"noschema-latest-label-someV-01": {
   898  			base: baseResource(myCRD),
   899  			patch: []string{
   900  				addLabelAndEnvPatch(myCRD),
   901  				changeImagePatch(myCRD, "nginx:latest"),
   902  				changeImagePatch(myCRD, "nginx:1.7.9"),
   903  			},
   904  			expected: `apiVersion: apps/v1
   905  kind: myCRD
   906  metadata:
   907    name: deploy1
   908  spec:
   909    template:
   910      metadata:
   911        labels:
   912          old-label: old-value
   913          some-label: some-value
   914      spec:
   915        containers:
   916        - image: nginx:1.7.9
   917          name: nginx
   918  `,
   919  		},
   920  		"noschema-latest-label-someV-02": {
   921  			base: baseResource(myCRD),
   922  			patch: []string{
   923  				changeImagePatch(myCRD, "nginx:latest"),
   924  				addLabelAndEnvPatch(myCRD),
   925  				changeImagePatch(myCRD, "nginx:1.7.9"),
   926  			},
   927  			expected: expectedResultJMP("nginx:1.7.9"),
   928  		},
   929  		"noschema-latest-label-someV-03": {
   930  			base: baseResource(myCRD),
   931  			patch: []string{
   932  				changeImagePatch(myCRD, "nginx:1.7.9"),
   933  				addLabelAndEnvPatch(myCRD),
   934  				changeImagePatch(myCRD, "nginx:latest"),
   935  			},
   936  			expected: `apiVersion: apps/v1
   937  kind: myCRD
   938  metadata:
   939    name: deploy1
   940  spec:
   941    template:
   942      metadata:
   943        labels:
   944          old-label: old-value
   945          some-label: some-value
   946      spec:
   947        containers:
   948        - image: nginx:latest
   949          name: nginx
   950  `,
   951  		},
   952  		"noschema-latest-label-someV-04": {
   953  			base: baseResource(myCRD),
   954  			patch: []string{
   955  				changeImagePatch(myCRD, "nginx:1.7.9"),
   956  				changeImagePatch(myCRD, "nginx:latest"),
   957  				addLabelAndEnvPatch(myCRD),
   958  				changeImagePatch(myCRD, "nginx:nginx"),
   959  			},
   960  			expected: `apiVersion: apps/v1
   961  kind: myCRD
   962  metadata:
   963    name: deploy1
   964  spec:
   965    template:
   966      metadata:
   967        labels:
   968          old-label: old-value
   969          some-label: some-value
   970      spec:
   971        containers:
   972        - image: nginx:nginx
   973          name: nginx
   974  `,
   975  		},
   976  	}
   977  
   978  	for name, test := range tests {
   979  		resource, err := factory.FromBytes([]byte(test.base))
   980  		require.NoError(t, err)
   981  		for _, p := range test.patch {
   982  			patch, err := factory.FromBytes([]byte(p))
   983  			require.NoError(t, err, name)
   984  			require.NoError(t, resource.ApplySmPatch(patch), name)
   985  		}
   986  		bytes, err := resource.AsYAML()
   987  		if test.errorExpected {
   988  			require.Error(t, err, name)
   989  		} else {
   990  			require.NoError(t, err, name)
   991  			assert.Equal(t, test.expected, string(bytes), name)
   992  		}
   993  	}
   994  }
   995  
   996  func TestResourceStorePreviousId(t *testing.T) {
   997  	tests := map[string]struct {
   998  		input    string
   999  		newName  string
  1000  		newNs    string
  1001  		expected string
  1002  	}{
  1003  		"default namespace, first previous name": {
  1004  			input: `apiVersion: apps/v1
  1005  kind: Secret
  1006  metadata:
  1007    name: oldName
  1008  `,
  1009  			newName: "newName",
  1010  			newNs:   "",
  1011  			expected: `apiVersion: apps/v1
  1012  kind: Secret
  1013  metadata:
  1014    annotations:
  1015      internal.config.kubernetes.io/previousKinds: Secret
  1016      internal.config.kubernetes.io/previousNames: oldName
  1017      internal.config.kubernetes.io/previousNamespaces: default
  1018    name: newName
  1019  `,
  1020  		},
  1021  
  1022  		"default namespace, second previous name": {
  1023  			input: `apiVersion: apps/v1
  1024  kind: Secret
  1025  metadata:
  1026    annotations:
  1027      internal.config.kubernetes.io/previousKinds: Secret
  1028      internal.config.kubernetes.io/previousNames: oldName
  1029      internal.config.kubernetes.io/previousNamespaces: default
  1030    name: oldName2
  1031  `,
  1032  			newName: "newName",
  1033  			newNs:   "",
  1034  			expected: `apiVersion: apps/v1
  1035  kind: Secret
  1036  metadata:
  1037    annotations:
  1038      internal.config.kubernetes.io/previousKinds: Secret,Secret
  1039      internal.config.kubernetes.io/previousNames: oldName,oldName2
  1040      internal.config.kubernetes.io/previousNamespaces: default,default
  1041    name: newName
  1042  `,
  1043  		},
  1044  
  1045  		"non-default namespace": {
  1046  			input: `apiVersion: apps/v1
  1047  kind: Secret
  1048  metadata:
  1049    annotations:
  1050      internal.config.kubernetes.io/previousKinds: Secret
  1051      internal.config.kubernetes.io/previousNames: oldName
  1052      internal.config.kubernetes.io/previousNamespaces: default
  1053    name: oldName2
  1054    namespace: oldNamespace
  1055  `,
  1056  			newName: "newName",
  1057  			newNs:   "newNamespace",
  1058  			expected: `apiVersion: apps/v1
  1059  kind: Secret
  1060  metadata:
  1061    annotations:
  1062      internal.config.kubernetes.io/previousKinds: Secret,Secret
  1063      internal.config.kubernetes.io/previousNames: oldName,oldName2
  1064      internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
  1065    name: newName
  1066    namespace: newNamespace
  1067  `,
  1068  		},
  1069  	}
  1070  	factory := provider.NewDefaultDepProvider().GetResourceFactory()
  1071  	for i := range tests {
  1072  		test := tests[i]
  1073  		t.Run(i, func(t *testing.T) {
  1074  			resources, err := factory.SliceFromBytes([]byte(test.input))
  1075  			if !assert.NoError(t, err) || len(resources) == 0 {
  1076  				t.FailNow()
  1077  			}
  1078  			r := resources[0]
  1079  			r.StorePreviousId()
  1080  			r.SetName(test.newName)
  1081  			if test.newNs != "" {
  1082  				r.SetNamespace(test.newNs)
  1083  			}
  1084  			bytes, err := r.AsYAML()
  1085  			if !assert.NoError(t, err) {
  1086  				t.FailNow()
  1087  			}
  1088  			assert.Equal(t, test.expected, string(bytes))
  1089  		})
  1090  	}
  1091  }
  1092  
  1093  func TestResource_PrevIds(t *testing.T) {
  1094  	tests := map[string]struct {
  1095  		input    string
  1096  		expected []resid.ResId
  1097  	}{
  1098  		"no previous IDs": {
  1099  			input: `apiVersion: apps/v1
  1100  kind: Secret
  1101  metadata:
  1102    name: name
  1103  `,
  1104  			expected: nil,
  1105  		},
  1106  
  1107  		"one previous ID": {
  1108  			input: `apiVersion: apps/v1
  1109  kind: Secret
  1110  metadata:
  1111    annotations:
  1112      internal.config.kubernetes.io/previousKinds: Secret
  1113      internal.config.kubernetes.io/previousNames: oldName
  1114      internal.config.kubernetes.io/previousNamespaces: default
  1115    name: newName
  1116  `,
  1117  			expected: []resid.ResId{
  1118  				{
  1119  					Gvk:       resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
  1120  					Name:      "oldName",
  1121  					Namespace: resid.DefaultNamespace,
  1122  				},
  1123  			},
  1124  		},
  1125  
  1126  		"two ids": {
  1127  			input: `apiVersion: apps/v1
  1128  kind: Secret
  1129  metadata:
  1130    annotations:
  1131      internal.config.kubernetes.io/previousKinds: Secret,Secret
  1132      internal.config.kubernetes.io/previousNames: oldName,oldName2
  1133      internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
  1134    name: newName
  1135    namespace: newNamespace
  1136  `,
  1137  			expected: []resid.ResId{
  1138  				{
  1139  					Gvk:       resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
  1140  					Name:      "oldName",
  1141  					Namespace: resid.DefaultNamespace,
  1142  				},
  1143  				{
  1144  					Gvk:       resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
  1145  					Name:      "oldName2",
  1146  					Namespace: "oldNamespace",
  1147  				},
  1148  			},
  1149  		},
  1150  	}
  1151  	factory := provider.NewDefaultDepProvider().GetResourceFactory()
  1152  	for i := range tests {
  1153  		test := tests[i]
  1154  		t.Run(i, func(t *testing.T) {
  1155  			resources, err := factory.SliceFromBytes([]byte(test.input))
  1156  			if !assert.NoError(t, err) || len(resources) == 0 {
  1157  				t.FailNow()
  1158  			}
  1159  			r := resources[0]
  1160  			assert.Equal(t, test.expected, r.PrevIds())
  1161  		})
  1162  	}
  1163  }
  1164  
  1165  // baseResource produces a base object which used to test
  1166  // patch transformation
  1167  // Also the structure is matching the Deployment syntax
  1168  // the kind can be replaced to allow testing using CRD
  1169  // without access to the schema
  1170  func baseResource(kind string) string {
  1171  	res := `
  1172  apiVersion: apps/v1
  1173  kind: %s
  1174  metadata:
  1175    name: deploy1
  1176  spec:
  1177    template:
  1178      metadata:
  1179        labels:
  1180          old-label: old-value
  1181      spec:
  1182        containers:
  1183        - name: nginx
  1184          image: nginx`
  1185  	return fmt.Sprintf(res, kind)
  1186  }
  1187  
  1188  // addContainerAndEnvPatch produces a patch object which adds
  1189  // an entry in the env slice of the first/nginx container
  1190  // as well as adding a label in the metadata
  1191  // Note that for SMP/WithSchema merge, the name:nginx entry
  1192  // is mandatory
  1193  func addLabelAndEnvPatch(kind string) string {
  1194  	return fmt.Sprintf(`
  1195  apiVersion: apps/v1
  1196  kind: %s
  1197  metadata:
  1198    name: deploy1
  1199  spec:
  1200    template:
  1201      metadata:
  1202        labels:
  1203          some-label: some-value
  1204      spec:
  1205        containers:
  1206         - name: nginx
  1207           env:
  1208           - name: SOMEENV
  1209             value: SOMEVALUE`, kind)
  1210  }
  1211  
  1212  // addContainerAndEnvPatch produces a patch object which adds
  1213  // an entry in the env slice of the first/nginx container
  1214  // as well as adding a second container in the container list
  1215  // Note that for SMP/WithSchema merge, the name:nginx entry
  1216  // is mandatory
  1217  func addContainerAndEnvPatch(kind string) string {
  1218  	return fmt.Sprintf(`
  1219  apiVersion: apps/v1
  1220  kind: %s
  1221  metadata:
  1222    name: deploy1
  1223  spec:
  1224    template:
  1225      spec:
  1226        containers:
  1227        - name: nginx
  1228          env:
  1229          - name: ANOTHERENV
  1230            value: ANOTHERVALUE
  1231        - name: anothercontainer
  1232          image: anotherimage`, kind)
  1233  }
  1234  
  1235  // addContainerAndEnvPatch produces a patch object which replaces
  1236  // the value of the image field in the first/nginx container
  1237  // Note that for SMP/WithSchema merge, the name:nginx entry
  1238  // is mandatory
  1239  func changeImagePatch(kind string, newImage string) string {
  1240  	return fmt.Sprintf(`
  1241  apiVersion: apps/v1
  1242  kind: %s
  1243  metadata:
  1244    name: deploy1
  1245  spec:
  1246    template:
  1247      spec:
  1248        containers:
  1249        - name: nginx
  1250          image: %s`, kind, newImage)
  1251  }
  1252  
  1253  // utility method to build the expected result of a multipatch
  1254  // the order of the patches still have influence especially
  1255  // in the insertion location within arrays.
  1256  func expectedResultMultiPatch(kind string, reversed bool) string {
  1257  	pattern := `apiVersion: apps/v1
  1258  kind: %s
  1259  metadata:
  1260    name: deploy1
  1261  spec:
  1262    template:
  1263      metadata:
  1264        labels:
  1265          old-label: old-value
  1266          some-label: some-value
  1267      spec:
  1268        containers:
  1269        - env:
  1270          %s
  1271          image: nginx:latest
  1272          name: nginx
  1273        - image: anotherimage
  1274          name: anothercontainer
  1275  `
  1276  	if reversed {
  1277  		return fmt.Sprintf(pattern, kind, `- name: SOMEENV
  1278            value: SOMEVALUE
  1279          - name: ANOTHERENV
  1280            value: ANOTHERVALUE`)
  1281  	}
  1282  	return fmt.Sprintf(pattern, kind, `- name: ANOTHERENV
  1283            value: ANOTHERVALUE
  1284          - name: SOMEENV
  1285            value: SOMEVALUE`)
  1286  }
  1287  
  1288  // utility method building the expected output of a JMP.
  1289  // imagename parameter allows to build a result consistent
  1290  // with the JMP behavior which basically overrides the
  1291  // entire "containers" list.
  1292  func expectedResultJMP(imagename string) string {
  1293  	if imagename == "" {
  1294  		return `apiVersion: apps/v1
  1295  kind: myCRD
  1296  metadata:
  1297    name: deploy1
  1298  spec:
  1299    template:
  1300      metadata:
  1301        labels:
  1302          old-label: old-value
  1303          some-label: some-value
  1304      spec:
  1305        containers:
  1306        - env:
  1307          - name: SOMEENV
  1308            value: SOMEVALUE
  1309          name: nginx
  1310  `
  1311  	}
  1312  	return fmt.Sprintf(`apiVersion: apps/v1
  1313  kind: myCRD
  1314  metadata:
  1315    name: deploy1
  1316  spec:
  1317    template:
  1318      metadata:
  1319        labels:
  1320          old-label: old-value
  1321          some-label: some-value
  1322      spec:
  1323        containers:
  1324        - image: %s
  1325          name: nginx
  1326  `, imagename)
  1327  }
  1328  
  1329  func TestSameEndingSubarray(t *testing.T) {
  1330  	testCases := map[string]struct {
  1331  		a        []string
  1332  		b        []string
  1333  		expected bool
  1334  	}{
  1335  		"both nil": {
  1336  			expected: true,
  1337  		},
  1338  		"one nil": {
  1339  			b:        []string{},
  1340  			expected: true,
  1341  		},
  1342  		"both empty": {
  1343  			a:        []string{},
  1344  			b:        []string{},
  1345  			expected: true,
  1346  		},
  1347  		"no1": {
  1348  			a:        []string{"a"},
  1349  			b:        []string{},
  1350  			expected: false,
  1351  		},
  1352  		"no2": {
  1353  			a:        []string{"b", "a"},
  1354  			b:        []string{"b"},
  1355  			expected: false,
  1356  		},
  1357  		"yes1": {
  1358  			a:        []string{"a", "b"},
  1359  			b:        []string{"b"},
  1360  			expected: true,
  1361  		},
  1362  		"yes2": {
  1363  			a:        []string{"a", "b", "c"},
  1364  			b:        []string{"b", "c"},
  1365  			expected: true,
  1366  		},
  1367  		"yes3": {
  1368  			a:        []string{"a", "b", "c", "d", "e", "f"},
  1369  			b:        []string{"f"},
  1370  			expected: true,
  1371  		},
  1372  	}
  1373  	for n := range testCases {
  1374  		tc := testCases[n]
  1375  		t.Run(n, func(t *testing.T) {
  1376  			assert.Equal(t, tc.expected, utils.SameEndingSubSlice(tc.a, tc.b))
  1377  		})
  1378  	}
  1379  }
  1380  
  1381  func TestGetGvk(t *testing.T) {
  1382  	r, err := factory.FromBytes([]byte(`
  1383  apiVersion: apps/v1
  1384  kind: Deployment
  1385  metadata:
  1386    name: clown
  1387  spec:
  1388    numReplicas: 1
  1389  `))
  1390  	require.NoError(t, err)
  1391  
  1392  	gvk := r.GetGvk()
  1393  	expected := "apps"
  1394  	actual := gvk.Group
  1395  	if expected != actual {
  1396  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1397  	}
  1398  	expected = "v1"
  1399  	actual = gvk.Version
  1400  	if expected != actual {
  1401  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1402  	}
  1403  	expected = "Deployment"
  1404  	actual = gvk.Kind
  1405  	if expected != actual {
  1406  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1407  	}
  1408  }
  1409  func TestSetGvk(t *testing.T) {
  1410  	r, err := factory.FromBytes([]byte(`
  1411  apiVersion: v1
  1412  kind: Deployment
  1413  metadata:
  1414    name: clown
  1415  spec:
  1416    numReplicas: 1
  1417  `))
  1418  	require.NoError(t, err)
  1419  	r.SetGvk(resid.GvkFromString("knd.ver.grp"))
  1420  	gvk := r.GetGvk()
  1421  	if expected, actual := "grp", gvk.Group; expected != actual {
  1422  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1423  	}
  1424  	if expected, actual := "ver", gvk.Version; expected != actual {
  1425  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1426  	}
  1427  	if expected, actual := "knd", gvk.Kind; expected != actual {
  1428  		t.Fatalf("expected '%s', got '%s'", expected, actual)
  1429  	}
  1430  }
  1431  
  1432  func TestRefBy(t *testing.T) {
  1433  	r, err := factory.FromBytes([]byte(`
  1434  apiVersion: v1
  1435  kind: Deployment
  1436  metadata:
  1437    name: clown
  1438  spec:
  1439    numReplicas: 1
  1440  `))
  1441  	require.NoError(t, err)
  1442  	r.AppendRefBy(resid.FromString("knd1.ver1.gr1/name1.ns1"))
  1443  	assert.Equal(t, `apiVersion: v1
  1444  kind: Deployment
  1445  metadata:
  1446    name: clown
  1447    annotations:
  1448      internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1'
  1449  spec:
  1450    numReplicas: 1
  1451  `, r.RNode.MustString())
  1452  	assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("knd1.ver1.gr1/name1.ns1")})
  1453  
  1454  	r.AppendRefBy(resid.FromString("knd2.ver2.gr2/name2.ns2"))
  1455  	assert.Equal(t, `apiVersion: v1
  1456  kind: Deployment
  1457  metadata:
  1458    name: clown
  1459    annotations:
  1460      internal.config.kubernetes.io/refBy: 'knd1.ver1.gr1/name1.ns1,knd2.ver2.gr2/name2.ns2'
  1461  spec:
  1462    numReplicas: 1
  1463  `, r.RNode.MustString())
  1464  	assert.Equal(t, []resid.ResId{
  1465  		resid.FromString("knd1.ver1.gr1/name1.ns1"),
  1466  		resid.FromString("knd2.ver2.gr2/name2.ns2"),
  1467  	}, r.GetRefBy())
  1468  }
  1469  
  1470  func TestOrigin(t *testing.T) {
  1471  	r, err := factory.FromBytes([]byte(`
  1472  apiVersion: v1
  1473  kind: Deployment
  1474  metadata:
  1475    name: clown
  1476  spec:
  1477    numReplicas: 1
  1478  `))
  1479  	require.NoError(t, err)
  1480  	origin := &Origin{
  1481  		Path: "deployment.yaml",
  1482  		Repo: "github.com/myrepo",
  1483  		Ref:  "master",
  1484  	}
  1485  	require.NoError(t, r.SetOrigin(origin))
  1486  	assert.Equal(t, `apiVersion: v1
  1487  kind: Deployment
  1488  metadata:
  1489    name: clown
  1490    annotations:
  1491      config.kubernetes.io/origin: |
  1492        path: deployment.yaml
  1493        repo: github.com/myrepo
  1494        ref: master
  1495  spec:
  1496    numReplicas: 1
  1497  `, r.MustString())
  1498  	or, err := r.GetOrigin()
  1499  	require.NoError(t, err)
  1500  	assert.Equal(t, origin, or)
  1501  }
  1502  
  1503  func TestTransformations(t *testing.T) {
  1504  	r, err := factory.FromBytes([]byte(`
  1505  apiVersion: v1
  1506  kind: Deployment
  1507  metadata:
  1508    name: clown
  1509  spec:
  1510    numReplicas: 1
  1511  `))
  1512  	require.NoError(t, err)
  1513  	origin1 := &Origin{
  1514  		Repo:         "github.com/myrepo",
  1515  		Ref:          "master",
  1516  		ConfiguredIn: "config.yaml",
  1517  		ConfiguredBy: kyaml.ResourceIdentifier{
  1518  			TypeMeta: kyaml.TypeMeta{
  1519  				APIVersion: "builtin",
  1520  				Kind:       "Generator",
  1521  			},
  1522  			NameMeta: kyaml.NameMeta{
  1523  				Name:      "my-name",
  1524  				Namespace: "my-namespace",
  1525  			},
  1526  		},
  1527  	}
  1528  	origin2 := &Origin{
  1529  		ConfiguredIn: "../base/config.yaml",
  1530  		ConfiguredBy: kyaml.ResourceIdentifier{
  1531  			TypeMeta: kyaml.TypeMeta{
  1532  				APIVersion: "builtin",
  1533  				Kind:       "Generator",
  1534  			},
  1535  			NameMeta: kyaml.NameMeta{
  1536  				Name:      "my-name",
  1537  				Namespace: "my-namespace",
  1538  			},
  1539  		},
  1540  	}
  1541  	require.NoError(t, r.AddTransformation(origin1))
  1542  	assert.Equal(t, `apiVersion: v1
  1543  kind: Deployment
  1544  metadata:
  1545    name: clown
  1546    annotations:
  1547      alpha.config.kubernetes.io/transformations: |
  1548        - repo: github.com/myrepo
  1549          ref: master
  1550          configuredIn: config.yaml
  1551          configuredBy:
  1552            apiVersion: builtin
  1553            kind: Generator
  1554            name: my-name
  1555            namespace: my-namespace
  1556  spec:
  1557    numReplicas: 1
  1558  `, r.MustString())
  1559  	require.NoError(t, r.AddTransformation(origin2))
  1560  	assert.Equal(t, `apiVersion: v1
  1561  kind: Deployment
  1562  metadata:
  1563    name: clown
  1564    annotations:
  1565      alpha.config.kubernetes.io/transformations: |
  1566        - repo: github.com/myrepo
  1567          ref: master
  1568          configuredIn: config.yaml
  1569          configuredBy:
  1570            apiVersion: builtin
  1571            kind: Generator
  1572            name: my-name
  1573            namespace: my-namespace
  1574        - configuredIn: ../base/config.yaml
  1575          configuredBy:
  1576            apiVersion: builtin
  1577            kind: Generator
  1578            name: my-name
  1579            namespace: my-namespace
  1580  spec:
  1581    numReplicas: 1
  1582  `, r.MustString())
  1583  	transformations, err := r.GetTransformations()
  1584  	require.NoError(t, err)
  1585  	assert.Equal(t, Transformations{origin1, origin2}, transformations)
  1586  	require.NoError(t, r.ClearTransformations())
  1587  	assert.Equal(t, `apiVersion: v1
  1588  kind: Deployment
  1589  metadata:
  1590    name: clown
  1591  spec:
  1592    numReplicas: 1
  1593  `, r.MustString())
  1594  }
  1595  

View as plain text