...

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

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

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package kioutil_test
     5  
     6  import (
     7  	"bytes"
     8  	"math/rand"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	"sigs.k8s.io/kustomize/kyaml/kio"
    16  	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
    17  	"sigs.k8s.io/kustomize/kyaml/yaml"
    18  )
    19  
    20  func TestSortNodes_moreThan10(t *testing.T) {
    21  	input := `
    22  a: b
    23  ---
    24  c: d
    25  ---
    26  e: f
    27  ---
    28  g: h
    29  ---
    30  i: j
    31  ---
    32  k: l
    33  ---
    34  m: n
    35  ---
    36  o: p
    37  ---
    38  q: r
    39  ---
    40  s: t
    41  ---
    42  u: v
    43  ---
    44  w: x
    45  ---
    46  y: z
    47  `
    48  	actual := &bytes.Buffer{}
    49  	rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(input), Writer: actual}
    50  	nodes, err := rw.Read()
    51  	if !assert.NoError(t, err) {
    52  		t.Fail()
    53  	}
    54  
    55  	// randomize the list
    56  	rand.Seed(time.Now().UnixNano())
    57  	rand.Shuffle(len(nodes), func(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] })
    58  
    59  	// sort them back into their original order
    60  	if !assert.NoError(t, kioutil.SortNodes(nodes)) {
    61  		t.Fail()
    62  	}
    63  
    64  	// check the sorted values
    65  	expected := strings.Split(input, "---")
    66  	for i := range nodes {
    67  		a := strings.TrimSpace(nodes[i].MustString())
    68  		b := strings.TrimSpace(expected[i])
    69  		if !assert.Contains(t, a, b) {
    70  			t.Fail()
    71  		}
    72  	}
    73  
    74  	if !assert.NoError(t, rw.Write(nodes)) {
    75  		t.Fail()
    76  	}
    77  
    78  	assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(actual.String()))
    79  }
    80  
    81  func TestDefaultPathAnnotation(t *testing.T) {
    82  	var tests = []struct {
    83  		dir      string
    84  		input    string // input
    85  		expected string // expected result
    86  		name     string
    87  	}{
    88  		{
    89  			`foo`,
    90  			`apiVersion: v1
    91  kind: Bar
    92  metadata:
    93    name: a
    94    namespace: b
    95  `,
    96  			`apiVersion: v1
    97  kind: Bar
    98  metadata:
    99    name: a
   100    namespace: b
   101    annotations:
   102      internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
   103      config.kubernetes.io/path: 'foo/b/bar_a.yaml'
   104  `, `with namespace`},
   105  		{
   106  			`foo`,
   107  			`apiVersion: v1
   108  kind: Bar
   109  metadata:
   110    name: a
   111  `,
   112  			`apiVersion: v1
   113  kind: Bar
   114  metadata:
   115    name: a
   116    annotations:
   117      internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
   118      config.kubernetes.io/path: 'foo/bar_a.yaml'
   119  `, `without namespace`},
   120  
   121  		{
   122  			``,
   123  			`apiVersion: v1
   124  kind: Bar
   125  metadata:
   126    name: a
   127    namespace: b
   128  `,
   129  			`apiVersion: v1
   130  kind: Bar
   131  metadata:
   132    name: a
   133    namespace: b
   134    annotations:
   135      internal.config.kubernetes.io/path: 'b/bar_a.yaml'
   136      config.kubernetes.io/path: 'b/bar_a.yaml'
   137  `, `without dir`},
   138  		{
   139  			``,
   140  			`apiVersion: v1
   141  kind: Bar
   142  metadata:
   143    name: a
   144    namespace: b
   145    annotations:
   146      internal.config.kubernetes.io/path: 'a/b.yaml'
   147      config.kubernetes.io/path: 'a/b.yaml'
   148  `,
   149  			`apiVersion: v1
   150  kind: Bar
   151  metadata:
   152    name: a
   153    namespace: b
   154    annotations:
   155      internal.config.kubernetes.io/path: 'a/b.yaml'
   156      config.kubernetes.io/path: 'a/b.yaml'
   157  `, `skip`},
   158  	}
   159  
   160  	for _, s := range tests {
   161  		n := yaml.MustParse(s.input)
   162  		err := kioutil.DefaultPathAnnotation(s.dir, []*yaml.RNode{n})
   163  		if !assert.NoError(t, err, s.name) {
   164  			t.FailNow()
   165  		}
   166  		if !assert.Equal(t, s.expected, n.MustString(), s.name) {
   167  			t.FailNow()
   168  		}
   169  	}
   170  }
   171  
   172  func TestDefaultPathAndIndexAnnotation(t *testing.T) {
   173  	var tests = []struct {
   174  		dir      string
   175  		input    string // input
   176  		expected string // expected result
   177  		name     string
   178  	}{
   179  		{
   180  			`foo`,
   181  			`apiVersion: v1
   182  kind: Bar
   183  metadata:
   184    name: a
   185    namespace: b
   186  `,
   187  			`apiVersion: v1
   188  kind: Bar
   189  metadata:
   190    name: a
   191    namespace: b
   192    annotations:
   193      internal.config.kubernetes.io/path: 'foo/b/bar_a.yaml'
   194      config.kubernetes.io/path: 'foo/b/bar_a.yaml'
   195      internal.config.kubernetes.io/index: '0'
   196      config.kubernetes.io/index: '0'
   197  `, `with namespace`},
   198  		{
   199  			`foo`,
   200  			`apiVersion: v1
   201  kind: Bar
   202  metadata:
   203    name: a
   204  `,
   205  			`apiVersion: v1
   206  kind: Bar
   207  metadata:
   208    name: a
   209    annotations:
   210      internal.config.kubernetes.io/path: 'foo/bar_a.yaml'
   211      config.kubernetes.io/path: 'foo/bar_a.yaml'
   212      internal.config.kubernetes.io/index: '0'
   213      config.kubernetes.io/index: '0'
   214  `, `without namespace`},
   215  
   216  		{
   217  			``,
   218  			`apiVersion: v1
   219  kind: Bar
   220  metadata:
   221    name: a
   222    namespace: b
   223  `,
   224  			`apiVersion: v1
   225  kind: Bar
   226  metadata:
   227    name: a
   228    namespace: b
   229    annotations:
   230      internal.config.kubernetes.io/path: 'b/bar_a.yaml'
   231      config.kubernetes.io/path: 'b/bar_a.yaml'
   232      internal.config.kubernetes.io/index: '0'
   233      config.kubernetes.io/index: '0'
   234  `, `without dir`},
   235  		{
   236  			``,
   237  			`apiVersion: v1
   238  kind: Bar
   239  metadata:
   240    name: a
   241    namespace: b
   242    annotations:
   243      internal.config.kubernetes.io/path: 'a/b.yaml'
   244      config.kubernetes.io/path: 'a/b.yaml'
   245      internal.config.kubernetes.io/index: '5'
   246      config.kubernetes.io/index: '5'
   247  `,
   248  			`apiVersion: v1
   249  kind: Bar
   250  metadata:
   251    name: a
   252    namespace: b
   253    annotations:
   254      internal.config.kubernetes.io/path: 'a/b.yaml'
   255      config.kubernetes.io/path: 'a/b.yaml'
   256      internal.config.kubernetes.io/index: '5'
   257      config.kubernetes.io/index: '5'
   258  `, `skip`},
   259  	}
   260  
   261  	for _, s := range tests {
   262  		out := &bytes.Buffer{}
   263  		r := kio.ByteReadWriter{
   264  			Reader:                bytes.NewBufferString(s.input),
   265  			Writer:                out,
   266  			KeepReaderAnnotations: true,
   267  			OmitReaderAnnotations: true,
   268  		}
   269  		n, err := r.Read()
   270  		if !assert.NoError(t, err, s.name) {
   271  			t.FailNow()
   272  		}
   273  		if !assert.NoError(t, kioutil.DefaultPathAndIndexAnnotation(s.dir, n), s.name) {
   274  			t.FailNow()
   275  		}
   276  		if !assert.NoError(t, r.Write(n), s.name) {
   277  			t.FailNow()
   278  		}
   279  		if !assert.Equal(t, s.expected, out.String(), s.name) {
   280  			t.FailNow()
   281  		}
   282  	}
   283  }
   284  
   285  func TestCreatePathAnnotationValue(t *testing.T) {
   286  	var tests = []struct {
   287  		dir      string
   288  		meta     yaml.ResourceMeta // input
   289  		expected string            // expected result
   290  		name     string
   291  	}{
   292  		{
   293  			`dir`,
   294  			yaml.ResourceMeta{
   295  				TypeMeta: yaml.TypeMeta{
   296  					APIVersion: "apps/v1",
   297  					Kind:       "foo",
   298  				},
   299  				ObjectMeta: yaml.ObjectMeta{
   300  					NameMeta: yaml.NameMeta{
   301  						Name: "bar", Namespace: "baz",
   302  					},
   303  				},
   304  			},
   305  			`dir/baz/foo_bar.yaml`, `with namespace`,
   306  		},
   307  		{
   308  			``,
   309  			yaml.ResourceMeta{
   310  				TypeMeta: yaml.TypeMeta{
   311  					APIVersion: "apps/v1",
   312  					Kind:       "foo",
   313  				},
   314  				ObjectMeta: yaml.ObjectMeta{
   315  					NameMeta: yaml.NameMeta{
   316  						Name: "bar", Namespace: "baz",
   317  					},
   318  				},
   319  			},
   320  			`baz/foo_bar.yaml`, `without dir`,
   321  		},
   322  		{
   323  			`dir`,
   324  			yaml.ResourceMeta{
   325  				TypeMeta: yaml.TypeMeta{
   326  					APIVersion: "apps/v1",
   327  					Kind:       "foo",
   328  				},
   329  				ObjectMeta: yaml.ObjectMeta{
   330  					NameMeta: yaml.NameMeta{Name: "bar"},
   331  				},
   332  			},
   333  			`dir/foo_bar.yaml`, `without namespace`,
   334  		},
   335  		{
   336  			``,
   337  			yaml.ResourceMeta{
   338  				TypeMeta: yaml.TypeMeta{
   339  					APIVersion: "apps/v1",
   340  					Kind:       "foo",
   341  				},
   342  				ObjectMeta: yaml.ObjectMeta{
   343  					NameMeta: yaml.NameMeta{Name: "bar"},
   344  				},
   345  			},
   346  			`foo_bar.yaml`, `without namespace or dir`,
   347  		},
   348  		{
   349  			``,
   350  			yaml.ResourceMeta{
   351  				TypeMeta: yaml.TypeMeta{
   352  					APIVersion: "apps/v1",
   353  					Kind:       "foo",
   354  				},
   355  				ObjectMeta: yaml.ObjectMeta{},
   356  			},
   357  			`foo_.yaml`, `without namespace, dir or name`,
   358  		},
   359  		{
   360  			``,
   361  			yaml.ResourceMeta{
   362  				TypeMeta: yaml.TypeMeta{
   363  					APIVersion: "apps/v1",
   364  				},
   365  				ObjectMeta: yaml.ObjectMeta{},
   366  			},
   367  			`_.yaml`, `without any`,
   368  		},
   369  	}
   370  
   371  	for _, s := range tests {
   372  		p := kioutil.CreatePathAnnotationValue(s.dir, s.meta)
   373  		if !assert.Equal(t, s.expected, p, s.name) {
   374  			t.FailNow()
   375  		}
   376  	}
   377  }
   378  
   379  func TestCopyLegacyAnnotations(t *testing.T) {
   380  	var tests = []struct {
   381  		input    string
   382  		expected string
   383  	}{
   384  		{
   385  			input: `apiVersion: v1
   386  kind: Foo
   387  metadata:
   388    name: foobar
   389    annotations:
   390      config.kubernetes.io/path: 'a/b.yaml'
   391      config.kubernetes.io/index: '5'
   392  `,
   393  			expected: `apiVersion: v1
   394  kind: Foo
   395  metadata:
   396    name: foobar
   397    annotations:
   398      config.kubernetes.io/path: 'a/b.yaml'
   399      config.kubernetes.io/index: '5'
   400      internal.config.kubernetes.io/path: 'a/b.yaml'
   401      internal.config.kubernetes.io/index: '5'
   402  `,
   403  		},
   404  		{
   405  			input: `apiVersion: v1
   406  kind: Foo
   407  metadata:
   408    name: foobar
   409    annotations:
   410      internal.config.kubernetes.io/path: 'a/b.yaml'
   411      internal.config.kubernetes.io/index: '5'
   412  `,
   413  			expected: `apiVersion: v1
   414  kind: Foo
   415  metadata:
   416    name: foobar
   417    annotations:
   418      internal.config.kubernetes.io/path: 'a/b.yaml'
   419      internal.config.kubernetes.io/index: '5'
   420      config.kubernetes.io/path: 'a/b.yaml'
   421      config.kubernetes.io/index: '5'
   422  `,
   423  		},
   424  		{
   425  			input: `apiVersion: v1
   426  kind: Foo
   427  metadata:
   428   name: foobar
   429   annotations:
   430     internal.config.kubernetes.io/path: 'a/b.yaml'
   431     config.kubernetes.io/path: 'c/d.yaml'
   432  `,
   433  			expected: `apiVersion: v1
   434  kind: Foo
   435  metadata:
   436    name: foobar
   437    annotations:
   438      internal.config.kubernetes.io/path: 'a/b.yaml'
   439      config.kubernetes.io/path: 'c/d.yaml'
   440  `,
   441  		},
   442  	}
   443  
   444  	for _, tc := range tests {
   445  		rw := kio.ByteReadWriter{
   446  			Reader:                bytes.NewBufferString(tc.input),
   447  			OmitReaderAnnotations: true,
   448  		}
   449  		nodes, err := rw.Read()
   450  		require.NoError(t, err)
   451  		require.NoError(t, kioutil.CopyLegacyAnnotations(nodes[0]))
   452  		assert.Equal(t, tc.expected, nodes[0].MustString())
   453  	}
   454  }
   455  
   456  func TestCopyInternalAnnotations(t *testing.T) {
   457  	var tests = []struct {
   458  		input      string
   459  		exclusions []kioutil.AnnotationKey
   460  		expected   string
   461  	}{
   462  		{
   463  			input: `apiVersion: v1
   464  kind: Foo
   465  metadata:
   466    name: src
   467    annotations:
   468      internal.config.kubernetes.io/path: 'a/b.yaml'
   469      internal.config.kubernetes.io/index: '5'
   470      internal.config.kubernetes.io/foo: 'bar'
   471  ---
   472  apiVersion: v1
   473  kind: Foo
   474  metadata:
   475    name: dst
   476    annotations:
   477      internal.config.kubernetes.io/path: 'c/d.yaml'
   478      internal.config.kubernetes.io/index: '10'
   479  `,
   480  			expected: `apiVersion: v1
   481  kind: Foo
   482  metadata:
   483    name: dst
   484    annotations:
   485      internal.config.kubernetes.io/path: 'a/b.yaml'
   486      internal.config.kubernetes.io/index: '5'
   487      internal.config.kubernetes.io/foo: 'bar'
   488  `,
   489  		},
   490  		{
   491  			input: `apiVersion: v1
   492  kind: Foo
   493  metadata:
   494    name: src
   495    annotations:
   496      internal.config.kubernetes.io/path: 'a/b.yaml'
   497      internal.config.kubernetes.io/index: '5'
   498      internal.config.kubernetes.io/foo: 'bar-src'
   499  ---
   500  apiVersion: v1
   501  kind: Foo
   502  metadata:
   503    name: dst
   504    annotations:
   505      internal.config.kubernetes.io/path: 'c/d.yaml'
   506      internal.config.kubernetes.io/index: '10'
   507      internal.config.kubernetes.io/foo: 'bar-dst'
   508  `,
   509  			exclusions: []kioutil.AnnotationKey{
   510  				kioutil.PathAnnotation,
   511  				kioutil.IndexAnnotation,
   512  			},
   513  			expected: `apiVersion: v1
   514  kind: Foo
   515  metadata:
   516    name: dst
   517    annotations:
   518      internal.config.kubernetes.io/path: 'c/d.yaml'
   519      internal.config.kubernetes.io/index: '10'
   520      internal.config.kubernetes.io/foo: 'bar-src'
   521  `,
   522  		},
   523  	}
   524  
   525  	for _, tc := range tests {
   526  		rw := kio.ByteReadWriter{
   527  			Reader:                bytes.NewBufferString(tc.input),
   528  			OmitReaderAnnotations: true,
   529  		}
   530  		nodes, err := rw.Read()
   531  		require.NoError(t, err)
   532  		require.NoError(t, kioutil.CopyInternalAnnotations(nodes[0], nodes[1], tc.exclusions...))
   533  		assert.Equal(t, tc.expected, nodes[1].MustString())
   534  	}
   535  }
   536  
   537  func TestConfirmInternalAnnotationUnchanged(t *testing.T) {
   538  	var tests = []struct {
   539  		input       string
   540  		exclusions  []kioutil.AnnotationKey
   541  		expectedErr string
   542  	}{
   543  		{
   544  			input: `apiVersion: v1
   545  kind: Foo
   546  metadata:
   547    name: foo-1
   548    annotations:
   549      internal.config.kubernetes.io/path: 'a/b.yaml'
   550      internal.config.kubernetes.io/index: '5'
   551  ---
   552  apiVersion: v1
   553  kind: Foo
   554  metadata:
   555    name: foo-2
   556    annotations:
   557      internal.config.kubernetes.io/path: 'c/d.yaml'
   558      internal.config.kubernetes.io/index: '10'
   559  `,
   560  			expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
   561  		},
   562  		{
   563  			input: `apiVersion: v1
   564  kind: Foo
   565  metadata:
   566    name: foo-1
   567    annotations:
   568      internal.config.kubernetes.io/path: 'a/b.yaml'
   569      internal.config.kubernetes.io/index: '5'
   570  ---
   571  apiVersion: v1
   572  kind: Foo
   573  metadata:
   574    name: foo-2
   575    annotations:
   576      internal.config.kubernetes.io/path: 'c/d.yaml'
   577      internal.config.kubernetes.io/index: '10'
   578  `,
   579  			exclusions: []kioutil.AnnotationKey{
   580  				kioutil.PathAnnotation,
   581  				kioutil.IndexAnnotation,
   582  			},
   583  			expectedErr: ``,
   584  		},
   585  		{
   586  			input: `apiVersion: v1
   587  kind: Foo
   588  metadata:
   589    name: foo-1
   590    annotations:
   591      internal.config.kubernetes.io/path: 'a/b.yaml'
   592      internal.config.kubernetes.io/index: '5'
   593      internal.config.kubernetes.io/foo: 'bar-1'
   594  ---
   595  apiVersion: v1
   596  kind: Foo
   597  metadata:
   598    name: foo-2
   599    annotations:
   600      internal.config.kubernetes.io/path: 'c/d.yaml'
   601      internal.config.kubernetes.io/index: '10'
   602      internal.config.kubernetes.io/foo: 'bar-2'
   603  `,
   604  			exclusions: []kioutil.AnnotationKey{
   605  				kioutil.PathAnnotation,
   606  				kioutil.IndexAnnotation,
   607  			},
   608  			expectedErr: `internal annotations differ: internal.config.kubernetes.io/foo`,
   609  		},
   610  		{
   611  			input: `apiVersion: v1
   612  kind: Foo
   613  metadata:
   614    name: foo-1
   615    annotations:
   616      internal.config.kubernetes.io/path: 'a/b.yaml'
   617      internal.config.kubernetes.io/index: '5'
   618      internal.config.kubernetes.io/foo: 'bar-1'
   619  ---
   620  apiVersion: v1
   621  kind: Foo
   622  metadata:
   623    name: foo-2
   624    annotations:
   625      internal.config.kubernetes.io/path: 'c/d.yaml'
   626      internal.config.kubernetes.io/index: '10'
   627      internal.config.kubernetes.io/foo: 'bar-1'
   628  `,
   629  			expectedErr: `internal annotations differ: internal.config.kubernetes.io/index, internal.config.kubernetes.io/path`,
   630  		},
   631  		{
   632  			input: `apiVersion: v1
   633  kind: Foo
   634  metadata:
   635    name: foo-1
   636    annotations:
   637      internal.config.kubernetes.io/a: 'b'
   638      internal.config.kubernetes.io/c: 'd'
   639  ---
   640  apiVersion: v1
   641  kind: Foo
   642  metadata:
   643    name: foo-2
   644    annotations:
   645      internal.config.kubernetes.io/e: 'f'
   646      internal.config.kubernetes.io/g: 'h'
   647  `,
   648  			expectedErr: `internal annotations differ: ` +
   649  				`internal.config.kubernetes.io/a, internal.config.kubernetes.io/c, ` +
   650  				`internal.config.kubernetes.io/e, internal.config.kubernetes.io/g`,
   651  		},
   652  	}
   653  
   654  	for _, tc := range tests {
   655  		rw := kio.ByteReadWriter{
   656  			Reader:                bytes.NewBufferString(tc.input),
   657  			OmitReaderAnnotations: true,
   658  		}
   659  		nodes, err := rw.Read()
   660  		require.NoError(t, err)
   661  		err = kioutil.ConfirmInternalAnnotationUnchanged(nodes[0], nodes[1], tc.exclusions...)
   662  		if tc.expectedErr == "" {
   663  			require.NoError(t, err)
   664  		} else {
   665  			if err == nil {
   666  				t.Fatalf("expected error: %s\n", tc.expectedErr)
   667  			}
   668  			assert.Equal(t, tc.expectedErr, err.Error())
   669  		}
   670  	}
   671  }
   672  
   673  func TestGetInternalAnnotations(t *testing.T) {
   674  	var tests = []struct {
   675  		input    string
   676  		expected map[string]string
   677  	}{
   678  		{
   679  			input: `apiVersion: v1
   680  kind: Foo
   681  metadata:
   682    name: foobar
   683    annotations:
   684      foo: bar
   685      internal.config.kubernetes.io/path: 'a/b.yaml'
   686      internal.config.kubernetes.io/index: '5'
   687      internal.config.kubernetes.io/foo: 'bar'
   688  `,
   689  			expected: map[string]string{
   690  				"internal.config.kubernetes.io/path":  "a/b.yaml",
   691  				"internal.config.kubernetes.io/index": "5",
   692  				"internal.config.kubernetes.io/foo":   "bar",
   693  			},
   694  		},
   695  	}
   696  
   697  	for _, tc := range tests {
   698  		rw := kio.ByteReadWriter{
   699  			Reader:                bytes.NewBufferString(tc.input),
   700  			OmitReaderAnnotations: true,
   701  		}
   702  		nodes, err := rw.Read()
   703  		require.NoError(t, err)
   704  		assert.Equal(t, tc.expected, kioutil.GetInternalAnnotations(nodes[0]))
   705  	}
   706  }
   707  

View as plain text