...

Source file src/sigs.k8s.io/kustomize/api/filters/fieldspec/fieldspec_test.go

Documentation: sigs.k8s.io/kustomize/api/filters/fieldspec

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package fieldspec_test
     5  
     6  import (
     7  	"bytes"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	"sigs.k8s.io/kustomize/api/filters/fieldspec"
    14  	"sigs.k8s.io/kustomize/api/filters/filtersutil"
    15  	"sigs.k8s.io/kustomize/kyaml/kio"
    16  	"sigs.k8s.io/kustomize/kyaml/yaml"
    17  )
    18  
    19  func TestFilter_Filter(t *testing.T) {
    20  	testCases := map[string]struct {
    21  		input     string
    22  		expected  string
    23  		filter    fieldspec.Filter
    24  		fieldSpec string
    25  		error     string
    26  	}{
    27  		"path not found": {
    28  			fieldSpec: `
    29  path: a/b
    30  group: foo
    31  kind: Bar
    32  `,
    33  			input: `
    34  apiVersion: foo
    35  kind: Bar
    36  xxx:
    37  `,
    38  			expected: `
    39  apiVersion: foo
    40  kind: Bar
    41  xxx:
    42  `,
    43  			filter: fieldspec.Filter{
    44  				SetValue: filtersutil.SetScalar("e"),
    45  			},
    46  		},
    47  		"empty path": {
    48  			fieldSpec: `
    49  group: foo
    50  version: v1
    51  kind: Bar
    52  `,
    53  			input: `
    54  apiVersion: foo/v1
    55  kind: Bar
    56  xxx:
    57  `,
    58  			expected: `
    59  apiVersion: foo
    60  kind: Bar
    61  xxx:
    62  `,
    63  			error: `considering field '' of object Bar.v1.foo/[noName].[noNs]: cannot set or create an empty field name`,
    64  			filter: fieldspec.Filter{
    65  				SetValue: filtersutil.SetScalar("e"),
    66  			},
    67  		},
    68  
    69  		"update": {
    70  			fieldSpec: `
    71  path: a/b
    72  group: foo
    73  kind: Bar
    74  `,
    75  			input: `
    76  apiVersion: foo/v1beta1
    77  kind: Bar
    78  a:
    79    b: c
    80  `,
    81  			expected: `
    82  apiVersion: foo/v1beta1
    83  kind: Bar
    84  a:
    85    b: e
    86  `,
    87  			filter: fieldspec.Filter{
    88  				SetValue: filtersutil.SetScalar("e"),
    89  			},
    90  		},
    91  
    92  		"update-kind-not-match": {
    93  			fieldSpec: `
    94  path: a/b
    95  group: foo
    96  kind: Bar1
    97  `,
    98  			input: `
    99  apiVersion: foo/v1beta1
   100  kind: Bar2
   101  a:
   102    b: c
   103  `,
   104  			expected: `
   105  apiVersion: foo/v1beta1
   106  kind: Bar2
   107  a:
   108    b: c
   109  `,
   110  			filter: fieldspec.Filter{
   111  				SetValue: filtersutil.SetScalar("e"),
   112  			},
   113  		},
   114  
   115  		"update-group-not-match": {
   116  			fieldSpec: `
   117  path: a/b
   118  group: foo1
   119  kind: Bar
   120  `,
   121  			input: `
   122  apiVersion: foo2/v1beta1
   123  kind: Bar
   124  a:
   125    b: c
   126  `,
   127  			expected: `
   128  apiVersion: foo2/v1beta1
   129  kind: Bar
   130  a:
   131    b: c
   132  `,
   133  			filter: fieldspec.Filter{
   134  				SetValue: filtersutil.SetScalar("e"),
   135  			},
   136  		},
   137  
   138  		"update-version-not-match": {
   139  			fieldSpec: `
   140  path: a/b
   141  group: foo
   142  version: v1beta1
   143  kind: Bar
   144  `,
   145  			input: `
   146  apiVersion: foo/v1beta2
   147  kind: Bar
   148  a:
   149    b: c
   150  `,
   151  			expected: `
   152  apiVersion: foo/v1beta2
   153  kind: Bar
   154  a:
   155    b: c
   156  `,
   157  			filter: fieldspec.Filter{
   158  				SetValue: filtersutil.SetScalar("e"),
   159  			},
   160  		},
   161  
   162  		"bad-version": {
   163  			fieldSpec: `
   164  path: a/b
   165  group: foo
   166  version: v1beta1
   167  kind: Bar
   168  `,
   169  			input: `
   170  apiVersion: foo/v1beta2/something
   171  kind: Bar
   172  a:
   173    b: c
   174  `,
   175  			expected: `
   176  apiVersion: foo/v1beta2/something
   177  kind: Bar
   178  a:
   179    b: c
   180  `,
   181  			filter: fieldspec.Filter{
   182  				SetValue: filtersutil.SetScalar("e"),
   183  			},
   184  		},
   185  
   186  		"bad-meta": {
   187  			fieldSpec: `
   188  path: a/b
   189  group: foo
   190  version: v1beta1
   191  kind: Bar
   192  `,
   193  			input: `
   194  a:
   195    b: c
   196  `,
   197  			expected: `
   198  a:
   199    b: c
   200  `,
   201  			filter: fieldspec.Filter{
   202  				SetValue: filtersutil.SetScalar("e"),
   203  			},
   204  		},
   205  
   206  		"miss-match-type": {
   207  			fieldSpec: `
   208  path: a/b/c
   209  kind: Bar
   210  `,
   211  			input: `
   212  kind: Bar
   213  a:
   214    b: a
   215  `,
   216  			error: `considering field 'a/b/c' of object Bar.[noVer].[noGrp]/[noName].[noNs]: expected sequence or mapping node`,
   217  			filter: fieldspec.Filter{
   218  				SetValue: filtersutil.SetScalar("e"),
   219  			},
   220  		},
   221  
   222  		"add": {
   223  			fieldSpec: `
   224  path: a/b/c/d
   225  group: foo
   226  create: true
   227  kind: Bar
   228  `,
   229  			input: `
   230  apiVersion: foo/v1beta1
   231  kind: Bar
   232  a: {}
   233  `,
   234  			expected: `
   235  apiVersion: foo/v1beta1
   236  kind: Bar
   237  a: {b: {c: {d: e}}}
   238  `,
   239  			filter: fieldspec.Filter{
   240  				SetValue:   filtersutil.SetScalar("e"),
   241  				CreateKind: yaml.ScalarNode,
   242  			},
   243  		},
   244  
   245  		"update-in-sequence": {
   246  			fieldSpec: `
   247  path: a/b[]/c/d
   248  group: foo
   249  kind: Bar
   250  `,
   251  			input: `
   252  apiVersion: foo/v1beta1
   253  kind: Bar
   254  a:
   255    b:
   256    - c:
   257        d: a
   258  `,
   259  			expected: `
   260  apiVersion: foo/v1beta1
   261  kind: Bar
   262  a:
   263    b:
   264    - c:
   265        d: e
   266  `,
   267  			filter: fieldspec.Filter{
   268  				SetValue: filtersutil.SetScalar("e"),
   269  			},
   270  		},
   271  
   272  		// Don't create a sequence
   273  		"empty-sequence-no-create": {
   274  			fieldSpec: `
   275  path: a/b[]/c/d
   276  group: foo
   277  create: true
   278  kind: Bar
   279  `,
   280  			input: `
   281  apiVersion: foo/v1beta1
   282  kind: Bar
   283  a: {}
   284  `,
   285  			expected: `
   286  apiVersion: foo/v1beta1
   287  kind: Bar
   288  a: {}
   289  `,
   290  			filter: fieldspec.Filter{
   291  				SetValue:   filtersutil.SetScalar("e"),
   292  				CreateKind: yaml.ScalarNode,
   293  			},
   294  		},
   295  
   296  		// Create a new field for an element in a sequence
   297  		"empty-sequence-create": {
   298  			fieldSpec: `
   299  path: a/b[]/c/d
   300  group: foo
   301  create: true
   302  kind: Bar
   303  `,
   304  			input: `
   305  apiVersion: foo/v1beta1
   306  kind: Bar
   307  a:
   308    b:
   309    - c: {}
   310  `,
   311  			expected: `
   312  apiVersion: foo/v1beta1
   313  kind: Bar
   314  a:
   315    b:
   316    - c: {d: e}
   317  `,
   318  			filter: fieldspec.Filter{
   319  				SetValue:   filtersutil.SetScalar("e"),
   320  				CreateKind: yaml.ScalarNode,
   321  			},
   322  		},
   323  
   324  		"group v1": {
   325  			fieldSpec: `
   326  path: a/b
   327  group: v1
   328  create: true
   329  kind: Bar
   330  `,
   331  			input: `
   332  apiVersion: v1
   333  kind: Bar
   334  `,
   335  			expected: `
   336  apiVersion: v1
   337  kind: Bar
   338  `,
   339  			filter: fieldspec.Filter{
   340  				SetValue:   filtersutil.SetScalar("e"),
   341  				CreateKind: yaml.ScalarNode,
   342  			},
   343  		},
   344  
   345  		"version v1": {
   346  			fieldSpec: `
   347  path: a/b
   348  version: v1
   349  create: true
   350  kind: Bar
   351  `,
   352  			input: `
   353  apiVersion: v1
   354  kind: Bar
   355  `,
   356  			expected: `
   357  apiVersion: v1
   358  kind: Bar
   359  a:
   360    b: e
   361  `,
   362  			filter: fieldspec.Filter{
   363  				SetValue:   filtersutil.SetScalar("e"),
   364  				CreateKind: yaml.ScalarNode,
   365  			},
   366  		},
   367  
   368  		"successfully set field on array entry no sequence hint": {
   369  			fieldSpec: `
   370  path: spec/containers/image
   371  version: v1
   372  kind: Bar
   373  `,
   374  			input: `
   375  apiVersion: v1
   376  kind: Bar
   377  spec:
   378    containers:
   379    - image: foo
   380  `,
   381  			expected: `
   382  apiVersion: v1
   383  kind: Bar
   384  spec:
   385    containers:
   386    - image: bar
   387  `,
   388  			filter: fieldspec.Filter{
   389  				SetValue:   filtersutil.SetScalar("bar"),
   390  				CreateKind: yaml.ScalarNode,
   391  			},
   392  		},
   393  
   394  		"successfully set field on array entry with sequence hint": {
   395  			fieldSpec: `
   396  path: spec/containers[]/image
   397  version: v1
   398  kind: Bar
   399  `,
   400  			input: `
   401  apiVersion: v1
   402  kind: Bar
   403  spec:
   404    containers:
   405    - image: foo
   406  `,
   407  			expected: `
   408  apiVersion: v1
   409  kind: Bar
   410  spec:
   411    containers:
   412    - image: bar
   413  `,
   414  			filter: fieldspec.Filter{
   415  				SetValue:   filtersutil.SetScalar("bar"),
   416  				CreateKind: yaml.ScalarNode,
   417  			},
   418  		},
   419  		"failure to set field on array entry with sequence hint in path": {
   420  			fieldSpec: `
   421  path: spec/containers[]/image
   422  version: v1
   423  kind: Bar
   424  `,
   425  			input: `
   426  apiVersion: v1
   427  kind: Bar
   428  spec:
   429    containers:
   430  `,
   431  			expected: `
   432  apiVersion: v1
   433  kind: Bar
   434  spec:
   435    containers: []
   436  `,
   437  			filter: fieldspec.Filter{
   438  				SetValue:   filtersutil.SetScalar("bar"),
   439  				CreateKind: yaml.ScalarNode,
   440  			},
   441  		},
   442  
   443  		"failure to set field on array entry, no sequence hint in path": {
   444  			fieldSpec: `
   445  path: spec/containers/image
   446  version: v1
   447  kind: Bar
   448  `,
   449  			input: `
   450  apiVersion: v1
   451  kind: Bar
   452  spec:
   453    containers:
   454  `,
   455  			expected: `
   456  apiVersion: v1
   457  kind: Bar
   458  spec:
   459    containers:
   460  `,
   461  			filter: fieldspec.Filter{
   462  				SetValue:   filtersutil.SetScalar("bar"),
   463  				CreateKind: yaml.ScalarNode,
   464  			},
   465  		},
   466  		"fieldname with slash '/'": {
   467  			fieldSpec: `
   468  path: a/b\/c/d
   469  version: v1
   470  kind: Bar
   471  `,
   472  			input: `
   473  apiVersion: v1
   474  kind: Bar
   475  a:
   476    b/c:
   477      d: foo
   478  `,
   479  			expected: `
   480  apiVersion: v1
   481  kind: Bar
   482  a:
   483    b/c:
   484      d: bar
   485  `,
   486  			filter: fieldspec.Filter{
   487  				SetValue:   filtersutil.SetScalar("bar"),
   488  				CreateKind: yaml.ScalarNode,
   489  			},
   490  		},
   491  		"fieldname with multiple '/'": {
   492  			fieldSpec: `
   493  path: a/b\/c/d\/e/f
   494  version: v1
   495  kind: Bar
   496  `,
   497  			input: `
   498  apiVersion: v1
   499  kind: Bar
   500  a:
   501    b/c:
   502      d/e:
   503        f: foo
   504  `,
   505  			expected: `
   506  apiVersion: v1
   507  kind: Bar
   508  a:
   509    b/c:
   510      d/e:
   511        f: bar
   512  `,
   513  			filter: fieldspec.Filter{
   514  				SetValue:   filtersutil.SetScalar("bar"),
   515  				CreateKind: yaml.ScalarNode,
   516  			},
   517  		},
   518  	}
   519  
   520  	for n := range testCases {
   521  		tc := testCases[n]
   522  		t.Run(n, func(t *testing.T) {
   523  			err := yaml.Unmarshal([]byte(tc.fieldSpec), &tc.filter.FieldSpec)
   524  			if !assert.NoError(t, err) {
   525  				t.FailNow()
   526  			}
   527  
   528  			out := &bytes.Buffer{}
   529  			rw := &kio.ByteReadWriter{
   530  				Reader:                bytes.NewBufferString(tc.input),
   531  				Writer:                out,
   532  				OmitReaderAnnotations: true,
   533  			}
   534  
   535  			// run the filter
   536  			err = kio.Pipeline{
   537  				Inputs:  []kio.Reader{rw},
   538  				Filters: []kio.Filter{kio.FilterAll(tc.filter)},
   539  				Outputs: []kio.Writer{rw},
   540  			}.Execute()
   541  			if tc.error != "" {
   542  				if !assert.EqualError(t, err, tc.error) {
   543  					t.FailNow()
   544  				}
   545  				// stop rest of test
   546  				return
   547  			}
   548  
   549  			if !assert.NoError(t, err) {
   550  				t.FailNow()
   551  			}
   552  
   553  			// check results
   554  			if !assert.Equal(t,
   555  				strings.TrimSpace(tc.expected),
   556  				strings.TrimSpace(out.String())) {
   557  				t.FailNow()
   558  			}
   559  		})
   560  	}
   561  }
   562  
   563  func TestFilter_FieldPaths(t *testing.T) {
   564  	testCases := map[string]struct {
   565  		input     string
   566  		fieldSpec string
   567  		expected  []string
   568  	}{
   569  		"fieldpath containing SequenceNode": {
   570  			input: `
   571  apiVersion: v1
   572  kind: Pod
   573  metadata:
   574    name: app
   575  spec:
   576    containers:
   577    - name: store
   578      image: redis:6.2.6
   579    - name: server
   580      image: nginx:latest
   581  `,
   582  			fieldSpec: `
   583  path: spec/containers[]/image
   584  kind: Pod
   585  `,
   586  			expected: []string{
   587  				"spec.containers.image",
   588  				"spec.containers.image",
   589  			},
   590  		},
   591  		"fieldpath with MappingNode": {
   592  			input: `
   593  apiVersion: v1
   594  kind: Pod
   595  metadata:
   596    name: app
   597  spec:
   598    containers:
   599    - name: store
   600      image: redis:6.2.6
   601    - name: server
   602      image: nginx:latest
   603  `,
   604  			fieldSpec: `
   605  path: metadata/name
   606  kind: Pod
   607  `,
   608  			expected: []string{
   609  				"metadata.name",
   610  			},
   611  		},
   612  	}
   613  	for name, tc := range testCases {
   614  		var fieldPaths []string
   615  		trackableSetter := filtersutil.TrackableSetter{}
   616  		trackableSetter.WithMutationTracker(func(key, value, tag string, node *yaml.RNode) {
   617  			fieldPaths = append(fieldPaths, strings.Join(node.FieldPath(), "."))
   618  		})
   619  		filter := fieldspec.Filter{
   620  			SetValue: trackableSetter.SetScalar("foo"),
   621  		}
   622  
   623  		t.Run(name, func(t *testing.T) {
   624  			err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec)
   625  			require.NoError(t, err)
   626  			rw := &kio.ByteReadWriter{
   627  				Reader:                bytes.NewBufferString(tc.input),
   628  				Writer:                &bytes.Buffer{},
   629  				OmitReaderAnnotations: true,
   630  			}
   631  
   632  			// run the filter
   633  			err = kio.Pipeline{
   634  				Inputs:  []kio.Reader{rw},
   635  				Filters: []kio.Filter{kio.FilterAll(filter)},
   636  				Outputs: []kio.Writer{rw},
   637  			}.Execute()
   638  
   639  			require.NoError(t, err)
   640  			assert.Equal(t, tc.expected, fieldPaths)
   641  		})
   642  	}
   643  }
   644  

View as plain text