...

Source file src/sigs.k8s.io/kustomize/kyaml/kio/pkgio_writer_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  	"fmt"
     8  	"math/rand"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"testing"
    13  	"time"
    14  
    15  	"sigs.k8s.io/kustomize/kyaml/filesys"
    16  	"sigs.k8s.io/kustomize/kyaml/testutil"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  	. "sigs.k8s.io/kustomize/kyaml/kio"
    21  	"sigs.k8s.io/kustomize/kyaml/yaml"
    22  )
    23  
    24  // TestLocalPackageWriter_Write tests:
    25  // - ReaderAnnotations are cleared when writing the Resources
    26  func TestLocalPackageWriter_Write(t *testing.T) {
    27  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
    28  		t.Helper()
    29  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
    30  		defer cleanup()
    31  
    32  		w := LocalPackageWriter{
    33  			PackagePath: d,
    34  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
    35  		}
    36  		err := w.Write([]*yaml.RNode{node2, node1, node3})
    37  		require.NoError(t, err)
    38  
    39  		b, err := fs.ReadFile(filepath.Join(d, "a", "b", "a_test.yaml"))
    40  		require.NoError(t, err)
    41  		require.Equal(t, `a: b #first
    42  ---
    43  c: d # second
    44  `, string(b))
    45  
    46  		b, err = fs.ReadFile(filepath.Join(d, "a", "b", "b_test.yaml"))
    47  		require.NoError(t, err)
    48  		require.Equal(t, `e: f
    49  g:
    50    h:
    51    - i # has a list
    52    - j
    53  `, string(b))
    54  	})
    55  }
    56  
    57  // TestLocalPackageWriter_Write_keepReaderAnnotations tests:
    58  // - ReaderAnnotations are kept when writing the Resources
    59  func TestLocalPackageWriter_Write_keepReaderAnnotations(t *testing.T) {
    60  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
    61  		t.Helper()
    62  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
    63  		defer cleanup()
    64  
    65  		w := LocalPackageWriter{
    66  			PackagePath:           d,
    67  			KeepReaderAnnotations: true,
    68  			FileSystem:            filesys.FileSystemOrOnDisk{FileSystem: fs},
    69  		}
    70  		err := w.Write([]*yaml.RNode{node2, node1, node3})
    71  		require.NoError(t, err)
    72  
    73  		b, err := fs.ReadFile(filepath.Join(d, "a", "b", "a_test.yaml"))
    74  		require.NoError(t, err)
    75  		require.Equal(t, `a: b #first
    76  metadata:
    77    annotations:
    78      config.kubernetes.io/index: "0"
    79      config.kubernetes.io/path: "a/b/a_test.yaml"
    80      internal.config.kubernetes.io/path: 'a/b/a_test.yaml'
    81      internal.config.kubernetes.io/index: '0'
    82  ---
    83  c: d # second
    84  metadata:
    85    annotations:
    86      config.kubernetes.io/index: "1"
    87      config.kubernetes.io/path: "a/b/a_test.yaml"
    88      internal.config.kubernetes.io/path: 'a/b/a_test.yaml'
    89      internal.config.kubernetes.io/index: '1'
    90  `, string(b))
    91  
    92  		b, err = fs.ReadFile(filepath.Join(d, "a", "b", "b_test.yaml"))
    93  		require.NoError(t, err)
    94  		require.Equal(t, `e: f
    95  g:
    96    h:
    97    - i # has a list
    98    - j
    99  metadata:
   100    annotations:
   101      config.kubernetes.io/index: "0"
   102      config.kubernetes.io/path: "a/b/b_test.yaml"
   103      internal.config.kubernetes.io/path: 'a/b/b_test.yaml'
   104      internal.config.kubernetes.io/index: '0'
   105  `, string(b))
   106  	})
   107  }
   108  
   109  // TestLocalPackageWriter_Write_clearAnnotations tests:
   110  // - ClearAnnotations are removed from Resources
   111  func TestLocalPackageWriter_Write_clearAnnotations(t *testing.T) {
   112  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   113  		t.Helper()
   114  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   115  		defer cleanup()
   116  
   117  		w := LocalPackageWriter{
   118  			PackagePath:      d,
   119  			ClearAnnotations: []string{"config.kubernetes.io/mode"},
   120  			FileSystem:       filesys.FileSystemOrOnDisk{FileSystem: fs},
   121  		}
   122  		err := w.Write([]*yaml.RNode{node2, node1, node3})
   123  		require.NoError(t, err)
   124  
   125  		b, err := fs.ReadFile(filepath.Join(d, "a", "b", "a_test.yaml"))
   126  		require.NoError(t, err)
   127  		require.Equal(t, `a: b #first
   128  ---
   129  c: d # second
   130  `, string(b))
   131  
   132  		b, err = fs.ReadFile(filepath.Join(d, "a", "b", "b_test.yaml"))
   133  		require.NoError(t, err)
   134  		require.Equal(t, `e: f
   135  g:
   136    h:
   137    - i # has a list
   138    - j
   139  `, string(b))
   140  	})
   141  }
   142  
   143  // TestLocalPackageWriter_Write_failRelativePath tests:
   144  // - If a relative path above the package is defined, write fails
   145  func TestLocalPackageWriter_Write_failRelativePath(t *testing.T) {
   146  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   147  		t.Helper()
   148  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   149  		defer cleanup()
   150  
   151  		node4, err := yaml.Parse(`e: f
   152  g:
   153    h:
   154    - i # has a list
   155    - j
   156  metadata:
   157    annotations:
   158      config.kubernetes.io/index: 0
   159      config.kubernetes.io/path: "a/b/../../../b_test.yaml"
   160  `)
   161  		require.NoError(t, err)
   162  
   163  		w := LocalPackageWriter{
   164  			PackagePath: d,
   165  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
   166  		}
   167  		err = w.Write([]*yaml.RNode{node2, node1, node3, node4})
   168  		if assert.Error(t, err) {
   169  			assert.Contains(t, err.Error(), "resource must be written under package")
   170  		}
   171  	})
   172  }
   173  
   174  // TestLocalPackageWriter_Write_invalidIndex tests:
   175  // - If a non-int index is given, fail
   176  func TestLocalPackageWriter_Write_invalidIndex(t *testing.T) {
   177  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   178  		t.Helper()
   179  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   180  		defer cleanup()
   181  
   182  		node4, err := yaml.Parse(`e: f
   183  g:
   184    h:
   185    - i # has a list
   186    - j
   187  metadata:
   188    annotations:
   189      config.kubernetes.io/index: a
   190      config.kubernetes.io/path: "a/b/b_test.yaml" # use a different path, should still collide
   191  `)
   192  		require.NoError(t, err)
   193  
   194  		w := LocalPackageWriter{
   195  			PackagePath: d,
   196  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
   197  		}
   198  		err = w.Write([]*yaml.RNode{node2, node1, node3, node4})
   199  		if assert.Error(t, err) {
   200  			assert.Contains(t, err.Error(), "unable to parse config.kubernetes.io/index")
   201  		}
   202  	})
   203  }
   204  
   205  // TestLocalPackageWriter_Write_absPath tests:
   206  // - If config.kubernetes.io/path is absolute, fail
   207  func TestLocalPackageWriter_Write_absPath(t *testing.T) {
   208  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   209  		t.Helper()
   210  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   211  		defer cleanup()
   212  
   213  		d = filepath.ToSlash(d)
   214  
   215  		n4 := fmt.Sprintf(`e: f
   216  g:
   217    h:
   218    - i # has a list
   219    - j
   220  metadata:
   221    annotations:
   222      config.kubernetes.io/index: a
   223      config.kubernetes.io/path: "%s/a/b/b_test.yaml" # use a different path, should still collide
   224  `, d)
   225  		node4, err := yaml.Parse(n4)
   226  		testutil.AssertNoError(t, err, n4)
   227  
   228  		w := LocalPackageWriter{
   229  			PackagePath: d,
   230  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
   231  		}
   232  		err = w.Write([]*yaml.RNode{node2, node1, node3, node4})
   233  		testutil.AssertErrorContains(t, err, "package paths may not be absolute paths")
   234  	})
   235  }
   236  
   237  // TestLocalPackageWriter_Write_missingPath tests:
   238  // - If config.kubernetes.io/path or index are missing, then default them
   239  func TestLocalPackageWriter_Write_missingAnnotations(t *testing.T) {
   240  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   241  		t.Helper()
   242  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   243  		defer cleanup()
   244  
   245  		node4String := `e: f
   246  g:
   247    h:
   248    - i # has a list
   249    - j
   250  kind: Foo
   251  metadata:
   252    name: bar
   253  `
   254  		node4, err := yaml.Parse(node4String)
   255  		require.NoError(t, err)
   256  
   257  		w := LocalPackageWriter{
   258  			PackagePath: d,
   259  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
   260  		}
   261  		err = w.Write([]*yaml.RNode{node2, node1, node3, node4})
   262  		require.NoError(t, err)
   263  		b, err := fs.ReadFile(filepath.Join(d, "foo_bar.yaml"))
   264  		require.NoError(t, err)
   265  		require.Equal(t, node4String, string(b))
   266  	})
   267  }
   268  
   269  // TestLocalPackageWriter_Write_pathIsDir tests:
   270  // - If  config.kubernetes.io/path is a directory, fail
   271  func TestLocalPackageWriter_Write_pathIsDir(t *testing.T) {
   272  	testWriterOnDiskAndOnMem(t, func(t *testing.T, fs filesys.FileSystem) {
   273  		t.Helper()
   274  		d, node1, node2, node3, cleanup := getWriterInputs(t, fs)
   275  		defer cleanup()
   276  
   277  		node4, err := yaml.Parse(`e: f
   278  g:
   279    h:
   280    - i # has a list
   281    - j
   282  metadata:
   283    annotations:
   284      config.kubernetes.io/path: a/
   285      config.kubernetes.io/index: "0"
   286  `)
   287  		require.NoError(t, err)
   288  
   289  		w := LocalPackageWriter{
   290  			PackagePath: d,
   291  			FileSystem:  filesys.FileSystemOrOnDisk{FileSystem: fs},
   292  		}
   293  		err = w.Write([]*yaml.RNode{node2, node1, node3, node4})
   294  		require.Error(t, err)
   295  		require.Contains(t, err.Error(), "config.kubernetes.io/path cannot be a directory")
   296  	})
   297  }
   298  
   299  func testWriterOnDiskAndOnMem(t *testing.T, f func(t *testing.T, fs filesys.FileSystem)) {
   300  	t.Helper()
   301  	t.Run("on_disk", func(t *testing.T) { f(t, filesys.MakeFsOnDisk()) })
   302  	// TODO: Once fsnode supports Windows, these tests should also be run.
   303  	if runtime.GOOS != "windows" {
   304  		t.Run("on_mem", func(t *testing.T) { f(t, filesys.MakeFsInMemory()) })
   305  	}
   306  }
   307  
   308  func getWriterInputs(t *testing.T, mockFS filesys.FileSystem) (string, *yaml.RNode, *yaml.RNode, *yaml.RNode, func()) {
   309  	t.Helper()
   310  	node1, err := yaml.Parse(`a: b #first
   311  metadata:
   312    annotations:
   313      config.kubernetes.io/index: "0"
   314      config.kubernetes.io/path: "a/b/a_test.yaml"
   315  `)
   316  	require.NoError(t, err)
   317  	node2, err := yaml.Parse(`c: d # second
   318  metadata:
   319    annotations:
   320      config.kubernetes.io/index: "1"
   321      config.kubernetes.io/path: "a/b/a_test.yaml"
   322  `)
   323  	require.NoError(t, err)
   324  	node3, err := yaml.Parse(`e: f
   325  g:
   326    h:
   327    - i # has a list
   328    - j
   329  metadata:
   330    annotations:
   331      config.kubernetes.io/index: "0"
   332      config.kubernetes.io/path: "a/b/b_test.yaml"
   333  `)
   334  	require.NoError(t, err)
   335  
   336  	// These two lines are similar to calling os.MkdirTemp, but we don't actually create any directory.
   337  	rand.Seed(time.Now().Unix())
   338  	path := filepath.Join(os.TempDir(), fmt.Sprintf("kyaml-test%d", rand.Int31())) //nolint:gosec
   339  	require.NoError(t, mockFS.MkdirAll(filepath.Join(path, "a")))
   340  	return path, node1, node2, node3, func() { require.NoError(t, mockFS.RemoveAll(path)) }
   341  }
   342  

View as plain text