...

Source file src/sigs.k8s.io/kustomize/kyaml/kio/kio_test.go

Documentation: sigs.k8s.io/kustomize/kyaml/kio

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package kio_test
     5  
     6  import (
     7  	"bytes"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/require"
    15  	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
    16  	"sigs.k8s.io/kustomize/kyaml/yaml"
    17  
    18  	. "sigs.k8s.io/kustomize/kyaml/kio"
    19  )
    20  
    21  func TestPipe(t *testing.T) {
    22  	p := Pipeline{
    23  		Inputs:  []Reader{},
    24  		Filters: []Filter{},
    25  		Outputs: []Writer{},
    26  	}
    27  
    28  	err := p.Execute()
    29  	if !assert.NoError(t, err) {
    30  		assert.FailNow(t, err.Error())
    31  	}
    32  }
    33  
    34  type mockCallback struct {
    35  	mock.Mock
    36  }
    37  
    38  func (c *mockCallback) Callback(op Filter) {
    39  	c.Called(op)
    40  }
    41  
    42  func TestPipelineWithCallback(t *testing.T) {
    43  	input := ResourceNodeSlice{yaml.MakeNullNode()}
    44  	noopFilter1 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
    45  		return nodes, nil
    46  	}
    47  	noopFilter2 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
    48  		return nodes, nil
    49  	}
    50  	filters := []Filter{
    51  		FilterFunc(noopFilter1),
    52  		FilterFunc(noopFilter2),
    53  	}
    54  	p := Pipeline{
    55  		Inputs:  []Reader{input},
    56  		Filters: filters,
    57  		Outputs: []Writer{},
    58  	}
    59  
    60  	callback := mockCallback{}
    61  	// setup expectations. `Times` means the function is called no more than `times`.
    62  	callback.On("Callback", mock.Anything).Times(len(filters))
    63  
    64  	err := p.ExecuteWithCallback(callback.Callback)
    65  
    66  	if !assert.NoError(t, err) {
    67  		assert.FailNow(t, err.Error())
    68  	}
    69  
    70  	callback.AssertNumberOfCalls(t, "Callback", len(filters))
    71  
    72  	// assert filters are called in the order they are defined.
    73  	for i, filter := range filters {
    74  		assert.Equal(
    75  			t,
    76  			reflect.ValueOf(callback.Calls[i].Arguments[0]).Pointer(),
    77  			reflect.ValueOf(filter).Pointer(),
    78  		)
    79  	}
    80  }
    81  
    82  func TestEmptyInput(t *testing.T) {
    83  	actual := &bytes.Buffer{}
    84  	output := ByteWriter{
    85  		Sort:               true,
    86  		WrappingKind:       ResourceListKind,
    87  		WrappingAPIVersion: ResourceListAPIVersion,
    88  	}
    89  	output.Writer = actual
    90  
    91  	p := Pipeline{
    92  		Outputs: []Writer{output},
    93  	}
    94  
    95  	if err := p.Execute(); err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	expected := `
   100  apiVersion: config.kubernetes.io/v1
   101  kind: ResourceList
   102  items: []
   103  `
   104  
   105  	if !assert.Equal(t,
   106  		strings.TrimSpace(expected), strings.TrimSpace(actual.String())) {
   107  		t.FailNow()
   108  	}
   109  }
   110  
   111  func TestEmptyInputWithFilter(t *testing.T) {
   112  	actual := &bytes.Buffer{}
   113  	output := ByteWriter{
   114  		Sort:               true,
   115  		WrappingKind:       ResourceListKind,
   116  		WrappingAPIVersion: ResourceListAPIVersion,
   117  	}
   118  	output.Writer = actual
   119  
   120  	filters := []Filter{
   121  		FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   122  			nodes = append(nodes, yaml.NewMapRNode(&map[string]string{
   123  				"foo": "bar",
   124  			}))
   125  			return nodes, nil
   126  		}),
   127  		FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return nodes, nil }),
   128  	}
   129  
   130  	p := Pipeline{
   131  		Outputs: []Writer{output},
   132  		Filters: filters,
   133  	}
   134  
   135  	if err := p.Execute(); err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	expected := `
   140  apiVersion: config.kubernetes.io/v1
   141  kind: ResourceList
   142  items:
   143  - foo: bar
   144  `
   145  
   146  	if !assert.Equal(t,
   147  		strings.TrimSpace(expected), strings.TrimSpace(actual.String())) {
   148  		t.FailNow()
   149  	}
   150  }
   151  
   152  func TestContinueOnEmptyBehavior(t *testing.T) {
   153  	cases := map[string]struct {
   154  		continueOnEmptyResult bool
   155  		expected              string
   156  	}{
   157  		"quit on empty":     {continueOnEmptyResult: false, expected: ""},
   158  		"continue on empty": {continueOnEmptyResult: true, expected: "foo: bar"},
   159  	}
   160  	for _, tc := range cases {
   161  		actual := &bytes.Buffer{}
   162  		output := ByteWriter{Writer: actual}
   163  
   164  		generatorFunc := FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   165  			nodes = append(nodes, yaml.NewMapRNode(&map[string]string{
   166  				"foo": "bar",
   167  			}))
   168  			return nodes, nil
   169  		})
   170  		emptyFunc := FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return nodes, nil })
   171  
   172  		p := Pipeline{
   173  			Outputs:               []Writer{output},
   174  			Filters:               []Filter{emptyFunc, generatorFunc},
   175  			ContinueOnEmptyResult: tc.continueOnEmptyResult,
   176  		}
   177  
   178  		err := p.Execute()
   179  		if err != nil {
   180  			t.Fatal(err)
   181  		}
   182  
   183  		if !assert.Equal(t,
   184  			tc.expected, strings.TrimSpace(actual.String())) {
   185  			t.Fail()
   186  		}
   187  	}
   188  }
   189  
   190  func TestLegacyAnnotationReconciliation(t *testing.T) {
   191  	noopFilter1 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   192  		return nodes, nil
   193  	}
   194  	noopFilter2 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   195  		return nodes, nil
   196  	}
   197  	changeInternalAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   198  		for _, rn := range nodes {
   199  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil {
   200  				return nil, err
   201  			}
   202  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.IndexAnnotation, "new")); err != nil {
   203  				return nil, err
   204  			}
   205  		}
   206  		return nodes, nil
   207  	}
   208  	changeLegacyAnnos := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   209  		for _, rn := range nodes {
   210  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "new")); err != nil {
   211  				return nil, err
   212  			}
   213  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyIndexAnnotation, "new")); err != nil {
   214  				return nil, err
   215  			}
   216  		}
   217  		return nodes, nil
   218  	}
   219  	changeBothPathAnnosMatch := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   220  		for _, rn := range nodes {
   221  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "new")); err != nil {
   222  				return nil, err
   223  			}
   224  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "new")); err != nil {
   225  				return nil, err
   226  			}
   227  		}
   228  		return nodes, nil
   229  	}
   230  	changeBothPathAnnosMismatch := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
   231  		for _, rn := range nodes {
   232  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.LegacyPathAnnotation, "foo")); err != nil {
   233  				return nil, err
   234  			}
   235  			if err := rn.PipeE(yaml.SetAnnotation(kioutil.PathAnnotation, "bar")); err != nil {
   236  				return nil, err
   237  			}
   238  		}
   239  		return nodes, nil
   240  	}
   241  
   242  	noops := []Filter{
   243  		FilterFunc(noopFilter1),
   244  		FilterFunc(noopFilter2),
   245  	}
   246  	internal := []Filter{FilterFunc(changeInternalAnnos)}
   247  	legacy := []Filter{FilterFunc(changeLegacyAnnos)}
   248  	changeBothMatch := []Filter{FilterFunc(changeBothPathAnnosMatch), FilterFunc(noopFilter1)}
   249  	changeBothMismatch := []Filter{FilterFunc(changeBothPathAnnosMismatch), FilterFunc(noopFilter1)}
   250  
   251  	testCases := map[string]struct {
   252  		input       string
   253  		filters     []Filter
   254  		expected    string
   255  		expectedErr string
   256  	}{
   257  		// the orchestrator should copy the legacy annotations to the new
   258  		// annotations
   259  		"legacy annotations only": {
   260  			input: `apiVersion: v1
   261  kind: ConfigMap
   262  metadata:
   263    name: ports-from
   264    annotations:
   265      config.kubernetes.io/path: 'configmap.yaml'
   266      config.kubernetes.io/index: '0'
   267  data:
   268    grpcPort: 8080
   269  ---
   270  apiVersion: v1
   271  kind: ConfigMap
   272  metadata:
   273    name: ports-to
   274    annotations:
   275      config.kubernetes.io/path: "configmap.yaml"
   276      config.kubernetes.io/index: '1'
   277  data:
   278    grpcPort: 8081
   279  `,
   280  			filters: noops,
   281  			expected: `apiVersion: v1
   282  kind: ConfigMap
   283  metadata:
   284    name: ports-from
   285    annotations:
   286      config.kubernetes.io/path: 'configmap.yaml'
   287      config.kubernetes.io/index: '0'
   288  data:
   289    grpcPort: 8080
   290  ---
   291  apiVersion: v1
   292  kind: ConfigMap
   293  metadata:
   294    name: ports-to
   295    annotations:
   296      config.kubernetes.io/path: "configmap.yaml"
   297      config.kubernetes.io/index: '1'
   298  data:
   299    grpcPort: 8081
   300  `,
   301  		},
   302  		// the orchestrator should copy the new annotations to the
   303  		// legacy annotations
   304  		"new annotations only": {
   305  			input: `apiVersion: v1
   306  kind: ConfigMap
   307  metadata:
   308    name: ports-from
   309    annotations:
   310      internal.config.kubernetes.io/path: 'configmap.yaml'
   311      internal.config.kubernetes.io/index: '0'
   312  data:
   313    grpcPort: 8080
   314  ---
   315  apiVersion: v1
   316  kind: ConfigMap
   317  metadata:
   318    name: ports-to
   319    annotations:
   320      internal.config.kubernetes.io/path: "configmap.yaml"
   321      internal.config.kubernetes.io/index: '1'
   322  data:
   323    grpcPort: 8081
   324  `,
   325  			filters: noops,
   326  			expected: `apiVersion: v1
   327  kind: ConfigMap
   328  metadata:
   329    name: ports-from
   330    annotations:
   331      internal.config.kubernetes.io/path: 'configmap.yaml'
   332      internal.config.kubernetes.io/index: '0'
   333  data:
   334    grpcPort: 8080
   335  ---
   336  apiVersion: v1
   337  kind: ConfigMap
   338  metadata:
   339    name: ports-to
   340    annotations:
   341      internal.config.kubernetes.io/path: "configmap.yaml"
   342      internal.config.kubernetes.io/index: '1'
   343  data:
   344    grpcPort: 8081
   345  `,
   346  		},
   347  		// the orchestrator should detect that the legacy annotations
   348  		// have been changed by the function
   349  		"change only legacy annotations": {
   350  			input: `apiVersion: v1
   351  kind: ConfigMap
   352  metadata:
   353    name: ports-from
   354    annotations:
   355      config.kubernetes.io/path: 'configmap.yaml'
   356      config.kubernetes.io/index: '0'
   357  data:
   358    grpcPort: 8080
   359  ---
   360  apiVersion: v1
   361  kind: ConfigMap
   362  metadata:
   363    name: ports-to
   364    annotations:
   365      config.kubernetes.io/path: "configmap.yaml"
   366      config.kubernetes.io/index: '1'
   367  data:
   368    grpcPort: 8081
   369  `,
   370  			filters: legacy,
   371  			expected: `apiVersion: v1
   372  kind: ConfigMap
   373  metadata:
   374    name: ports-from
   375    annotations:
   376      config.kubernetes.io/path: 'new'
   377      config.kubernetes.io/index: 'new'
   378  data:
   379    grpcPort: 8080
   380  ---
   381  apiVersion: v1
   382  kind: ConfigMap
   383  metadata:
   384    name: ports-to
   385    annotations:
   386      config.kubernetes.io/path: "new"
   387      config.kubernetes.io/index: 'new'
   388  data:
   389    grpcPort: 8081
   390  `,
   391  		},
   392  		// the orchestrator should detect that the new internal annotations
   393  		// have been changed by the function
   394  		"change only internal annotations": {
   395  			input: `apiVersion: v1
   396  kind: ConfigMap
   397  metadata:
   398    name: ports-from
   399    annotations:
   400      internal.config.kubernetes.io/path: 'configmap.yaml'
   401      internal.config.kubernetes.io/index: '0'
   402  data:
   403    grpcPort: 8080
   404  ---
   405  apiVersion: v1
   406  kind: ConfigMap
   407  metadata:
   408    name: ports-to
   409    annotations:
   410      internal.config.kubernetes.io/path: "configmap.yaml"
   411      internal.config.kubernetes.io/index: '1'
   412  data:
   413    grpcPort: 8081
   414  `,
   415  			filters: internal,
   416  			expected: `apiVersion: v1
   417  kind: ConfigMap
   418  metadata:
   419    name: ports-from
   420    annotations:
   421      internal.config.kubernetes.io/path: 'new'
   422      internal.config.kubernetes.io/index: 'new'
   423  data:
   424    grpcPort: 8080
   425  ---
   426  apiVersion: v1
   427  kind: ConfigMap
   428  metadata:
   429    name: ports-to
   430    annotations:
   431      internal.config.kubernetes.io/path: "new"
   432      internal.config.kubernetes.io/index: 'new'
   433  data:
   434    grpcPort: 8081
   435  `,
   436  		},
   437  		// the orchestrator should detect that the legacy annotations
   438  		// have been changed by the function
   439  		"change only internal annotations while input is legacy annotations": {
   440  			input: `apiVersion: v1
   441  kind: ConfigMap
   442  metadata:
   443    name: ports-from
   444    annotations:
   445      config.kubernetes.io/path: 'configmap.yaml'
   446      config.kubernetes.io/index: '0'
   447  data:
   448    grpcPort: 8080
   449  ---
   450  apiVersion: v1
   451  kind: ConfigMap
   452  metadata:
   453    name: ports-to
   454    annotations:
   455      config.kubernetes.io/path: "configmap.yaml"
   456      config.kubernetes.io/index: '1'
   457  data:
   458    grpcPort: 8081
   459  `,
   460  			filters: internal,
   461  			expected: `apiVersion: v1
   462  kind: ConfigMap
   463  metadata:
   464    name: ports-from
   465    annotations:
   466      config.kubernetes.io/path: 'new'
   467      config.kubernetes.io/index: 'new'
   468  data:
   469    grpcPort: 8080
   470  ---
   471  apiVersion: v1
   472  kind: ConfigMap
   473  metadata:
   474    name: ports-to
   475    annotations:
   476      config.kubernetes.io/path: "new"
   477      config.kubernetes.io/index: 'new'
   478  data:
   479    grpcPort: 8081
   480  `,
   481  		},
   482  		// the orchestrator should detect that the new internal annotations
   483  		// have been changed by the function
   484  		"change only legacy annotations while input is internal annotations": {
   485  			input: `apiVersion: v1
   486  kind: ConfigMap
   487  metadata:
   488    name: ports-from
   489    annotations:
   490      internal.config.kubernetes.io/path: 'configmap.yaml'
   491      internal.config.kubernetes.io/index: '0'
   492  data:
   493    grpcPort: 8080
   494  ---
   495  apiVersion: v1
   496  kind: ConfigMap
   497  metadata:
   498    name: ports-to
   499    annotations:
   500      internal.config.kubernetes.io/path: "configmap.yaml"
   501      internal.config.kubernetes.io/index: '1'
   502  data:
   503    grpcPort: 8081
   504  `,
   505  			filters: legacy,
   506  			expected: `apiVersion: v1
   507  kind: ConfigMap
   508  metadata:
   509    name: ports-from
   510    annotations:
   511      internal.config.kubernetes.io/path: 'new'
   512      internal.config.kubernetes.io/index: 'new'
   513  data:
   514    grpcPort: 8080
   515  ---
   516  apiVersion: v1
   517  kind: ConfigMap
   518  metadata:
   519    name: ports-to
   520    annotations:
   521      internal.config.kubernetes.io/path: "new"
   522      internal.config.kubernetes.io/index: 'new'
   523  data:
   524    grpcPort: 8081
   525  `,
   526  		},
   527  		// the orchestrator should detect that the legacy annotations
   528  		// have been changed by the function
   529  		"change only legacy annotations while input has both": {
   530  			input: `apiVersion: v1
   531  kind: ConfigMap
   532  metadata:
   533    name: ports-from
   534    annotations:
   535      config.kubernetes.io/path: 'configmap.yaml'
   536      config.kubernetes.io/index: '0'
   537      internal.config.kubernetes.io/path: 'configmap.yaml'
   538      internal.config.kubernetes.io/index: '0'
   539  data:
   540    grpcPort: 8080
   541  ---
   542  apiVersion: v1
   543  kind: ConfigMap
   544  metadata:
   545    name: ports-to
   546    annotations:
   547      config.kubernetes.io/path: "configmap.yaml"
   548      config.kubernetes.io/index: '1'
   549      internal.config.kubernetes.io/path: 'configmap.yaml'
   550      internal.config.kubernetes.io/index: '1'
   551  data:
   552    grpcPort: 8081
   553  `,
   554  			filters: legacy,
   555  			expected: `apiVersion: v1
   556  kind: ConfigMap
   557  metadata:
   558    name: ports-from
   559    annotations:
   560      config.kubernetes.io/path: 'new'
   561      config.kubernetes.io/index: 'new'
   562      internal.config.kubernetes.io/path: 'new'
   563      internal.config.kubernetes.io/index: 'new'
   564  data:
   565    grpcPort: 8080
   566  ---
   567  apiVersion: v1
   568  kind: ConfigMap
   569  metadata:
   570    name: ports-to
   571    annotations:
   572      config.kubernetes.io/path: "new"
   573      config.kubernetes.io/index: 'new'
   574      internal.config.kubernetes.io/path: 'new'
   575      internal.config.kubernetes.io/index: 'new'
   576  data:
   577    grpcPort: 8081
   578  `,
   579  		},
   580  		// the orchestrator should detect that the new internal annotations
   581  		// have been changed by the function
   582  		"change only internal annotations while input has both": {
   583  			input: `apiVersion: v1
   584  kind: ConfigMap
   585  metadata:
   586    name: ports-from
   587    annotations:
   588      config.kubernetes.io/path: "configmap.yaml"
   589      config.kubernetes.io/index: '0'
   590      internal.config.kubernetes.io/path: 'configmap.yaml'
   591      internal.config.kubernetes.io/index: '0'
   592  data:
   593    grpcPort: 8080
   594  ---
   595  apiVersion: v1
   596  kind: ConfigMap
   597  metadata:
   598    name: ports-to
   599    annotations:
   600      config.kubernetes.io/path: "configmap.yaml"
   601      config.kubernetes.io/index: '1'
   602      internal.config.kubernetes.io/path: "configmap.yaml"
   603      internal.config.kubernetes.io/index: '1'
   604  data:
   605    grpcPort: 8081
   606  `,
   607  			filters: internal,
   608  			expected: `apiVersion: v1
   609  kind: ConfigMap
   610  metadata:
   611    name: ports-from
   612    annotations:
   613      config.kubernetes.io/path: "new"
   614      config.kubernetes.io/index: 'new'
   615      internal.config.kubernetes.io/path: 'new'
   616      internal.config.kubernetes.io/index: 'new'
   617  data:
   618    grpcPort: 8080
   619  ---
   620  apiVersion: v1
   621  kind: ConfigMap
   622  metadata:
   623    name: ports-to
   624    annotations:
   625      config.kubernetes.io/path: "new"
   626      config.kubernetes.io/index: 'new'
   627      internal.config.kubernetes.io/path: "new"
   628      internal.config.kubernetes.io/index: 'new'
   629  data:
   630    grpcPort: 8081
   631  `,
   632  		},
   633  		// the orchestrator should detect that the new internal annotations
   634  		// have been changed by the function
   635  		"change both to matching value while input has both": {
   636  			input: `apiVersion: v1
   637  kind: ConfigMap
   638  metadata:
   639    name: ports-from
   640    annotations:
   641      config.kubernetes.io/path: "configmap.yaml"
   642      config.kubernetes.io/index: '0'
   643      internal.config.kubernetes.io/path: 'configmap.yaml'
   644      internal.config.kubernetes.io/index: '0'
   645  data:
   646    grpcPort: 8080
   647  ---
   648  apiVersion: v1
   649  kind: ConfigMap
   650  metadata:
   651    name: ports-to
   652    annotations:
   653      config.kubernetes.io/path: "configmap.yaml"
   654      config.kubernetes.io/index: '1'
   655      internal.config.kubernetes.io/path: "configmap.yaml"
   656      internal.config.kubernetes.io/index: '1'
   657  data:
   658    grpcPort: 8081
   659  `,
   660  			filters: changeBothMatch,
   661  			expected: `apiVersion: v1
   662  kind: ConfigMap
   663  metadata:
   664    name: ports-from
   665    annotations:
   666      config.kubernetes.io/path: "new"
   667      config.kubernetes.io/index: '0'
   668      internal.config.kubernetes.io/path: 'new'
   669      internal.config.kubernetes.io/index: '0'
   670  data:
   671    grpcPort: 8080
   672  ---
   673  apiVersion: v1
   674  kind: ConfigMap
   675  metadata:
   676    name: ports-to
   677    annotations:
   678      config.kubernetes.io/path: "new"
   679      config.kubernetes.io/index: '1'
   680      internal.config.kubernetes.io/path: "new"
   681      internal.config.kubernetes.io/index: '1'
   682  data:
   683    grpcPort: 8081
   684  `,
   685  		},
   686  		// the orchestrator should detect that the new internal annotations
   687  		// have been changed by the function
   688  		"change both to matching value while input is legacy": {
   689  			input: `apiVersion: v1
   690  kind: ConfigMap
   691  metadata:
   692    name: ports-from
   693    annotations:
   694      config.kubernetes.io/path: "configmap.yaml"
   695      config.kubernetes.io/index: '0'
   696  data:
   697    grpcPort: 8080
   698  ---
   699  apiVersion: v1
   700  kind: ConfigMap
   701  metadata:
   702    name: ports-to
   703    annotations:
   704      config.kubernetes.io/path: "configmap.yaml"
   705      config.kubernetes.io/index: '1'
   706  data:
   707    grpcPort: 8081
   708  `,
   709  			filters: changeBothMatch,
   710  			expected: `apiVersion: v1
   711  kind: ConfigMap
   712  metadata:
   713    name: ports-from
   714    annotations:
   715      config.kubernetes.io/path: "new"
   716      config.kubernetes.io/index: '0'
   717  data:
   718    grpcPort: 8080
   719  ---
   720  apiVersion: v1
   721  kind: ConfigMap
   722  metadata:
   723    name: ports-to
   724    annotations:
   725      config.kubernetes.io/path: "new"
   726      config.kubernetes.io/index: '1'
   727  data:
   728    grpcPort: 8081
   729  `,
   730  		},
   731  		// the orchestrator should detect that the new internal annotations
   732  		// have been changed by the function
   733  		"change both to matching value while input is internal": {
   734  			input: `apiVersion: v1
   735  kind: ConfigMap
   736  metadata:
   737    name: ports-from
   738    annotations:
   739      internal.config.kubernetes.io/path: 'configmap.yaml'
   740      internal.config.kubernetes.io/index: '0'
   741  data:
   742    grpcPort: 8080
   743  ---
   744  apiVersion: v1
   745  kind: ConfigMap
   746  metadata:
   747    name: ports-to
   748    annotations:
   749      internal.config.kubernetes.io/path: "configmap.yaml"
   750      internal.config.kubernetes.io/index: '1'
   751  data:
   752    grpcPort: 8081
   753  `,
   754  			filters: changeBothMatch,
   755  			expected: `apiVersion: v1
   756  kind: ConfigMap
   757  metadata:
   758    name: ports-from
   759    annotations:
   760      internal.config.kubernetes.io/path: 'new'
   761      internal.config.kubernetes.io/index: '0'
   762  data:
   763    grpcPort: 8080
   764  ---
   765  apiVersion: v1
   766  kind: ConfigMap
   767  metadata:
   768    name: ports-to
   769    annotations:
   770      internal.config.kubernetes.io/path: "new"
   771      internal.config.kubernetes.io/index: '1'
   772  data:
   773    grpcPort: 8081
   774  `,
   775  		},
   776  		// the function changes both the legacy and new path annotation, and they mismatch,
   777  		// so we should get an error
   778  		"change both but mismatch while input is legacy": {
   779  			input: `apiVersion: v1
   780  kind: ConfigMap
   781  metadata:
   782    name: ports-from
   783    annotations:
   784      config.kubernetes.io/path: 'configmap.yaml'
   785      config.kubernetes.io/index: '0'
   786  data:
   787    grpcPort: 8080
   788  ---
   789  apiVersion: v1
   790  kind: ConfigMap
   791  metadata:
   792    name: ports-to
   793    annotations:
   794      config.kubernetes.io/path: "configmap.yaml"
   795      config.kubernetes.io/index: '1'
   796  data:
   797    grpcPort: 8081
   798  `,
   799  			filters:     changeBothMismatch,
   800  			expectedErr: "resource input to function has mismatched legacy and internal path annotations",
   801  		},
   802  		// the function changes both the legacy and new path annotation, and they mismatch,
   803  		// so we should get an error
   804  		"change both but mismatch while input is internal": {
   805  			input: `apiVersion: v1
   806  kind: ConfigMap
   807  metadata:
   808    name: ports-from
   809    annotations:
   810      internal.config.kubernetes.io/path: "configmap.yaml"
   811      internal.config.kubernetes.io/index: '0'
   812  data:
   813    grpcPort: 8080
   814  ---
   815  apiVersion: v1
   816  kind: ConfigMap
   817  metadata:
   818    name: ports-to
   819    annotations:
   820      internal.config.kubernetes.io/path: "configmap.yaml"
   821      internal.config.kubernetes.io/index: '1'
   822  data:
   823    grpcPort: 8081
   824  `,
   825  			filters:     changeBothMismatch,
   826  			expectedErr: "resource input to function has mismatched legacy and internal path annotations",
   827  		},
   828  		// the function changes both the legacy and new path annotation, and they mismatch,
   829  		// so we should get an error
   830  		"change both but mismatch while input has both": {
   831  			input: `apiVersion: v1
   832  kind: ConfigMap
   833  metadata:
   834    name: ports-from
   835    annotations:
   836      config.kubernetes.io/path: 'configmap.yaml'
   837      config.kubernetes.io/index: '0'
   838      config.k8s.io/id: '1'
   839      internal.config.kubernetes.io/path: "configmap.yaml"
   840      internal.config.kubernetes.io/index: '0'
   841  data:
   842    grpcPort: 8080
   843  ---
   844  apiVersion: v1
   845  kind: ConfigMap
   846  metadata:
   847    name: ports-to
   848    annotations:
   849      config.kubernetes.io/path: "configmap.yaml"
   850      config.kubernetes.io/index: '1'
   851      config.k8s.io/id: '2'
   852      internal.config.kubernetes.io/path: "configmap.yaml"
   853      internal.config.kubernetes.io/index: '1'
   854  data:
   855    grpcPort: 8081
   856  `,
   857  			filters:     changeBothMismatch,
   858  			expectedErr: "resource input to function has mismatched legacy and internal path annotations",
   859  		},
   860  	}
   861  
   862  	for tn, tc := range testCases {
   863  		t.Run(tn, func(t *testing.T) {
   864  			var out bytes.Buffer
   865  			input := ByteReadWriter{
   866  				Reader:                bytes.NewBufferString(tc.input),
   867  				Writer:                &out,
   868  				OmitReaderAnnotations: true,
   869  				KeepReaderAnnotations: true,
   870  			}
   871  			p := Pipeline{
   872  				Inputs:  []Reader{&input},
   873  				Filters: tc.filters,
   874  				Outputs: []Writer{&input},
   875  			}
   876  
   877  			err := p.Execute()
   878  			if tc.expectedErr == "" {
   879  				require.NoError(t, err)
   880  				assert.Equal(t, tc.expected, out.String())
   881  			} else {
   882  				require.Error(t, err)
   883  				assert.Equal(t, tc.expectedErr, err.Error())
   884  			}
   885  		})
   886  	}
   887  }
   888  

View as plain text