// Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package kio_test import ( "path/filepath" "runtime" "strings" "testing" "github.com/stretchr/testify/require" "sigs.k8s.io/kustomize/kyaml/filesys" . "sigs.k8s.io/kustomize/kyaml/kio" ) var readFileA = []byte(`--- a: b #first --- c: d # second `) var readFileB = []byte(`# second thing e: f g: h: - i # has a list - j `) var readFileC = []byte(`--- a: b #third metadata: annotations: `) var readFileD = []byte(`--- a: b #forth metadata: `) var pkgFile = []byte(``) func TestLocalPackageReader_Read_empty(t *testing.T) { var r LocalPackageReader nodes, err := r.Read() require.Error(t, err) require.Contains(t, err.Error(), "must specify package path") require.Nil(t, nodes) } func TestLocalPackageReader_Read_pkg(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.yaml", content: readFileA}, {path: "b_test.yaml", content: readFileB}, {path: "c_test.yaml", content: readFileC}, {path: "d_test.yaml", content: readFileD}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 5) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a_test.yaml' `, `# second thing e: f g: h: - i # has a list - j metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'b_test.yaml' `, `a: b #third metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'c_test.yaml' `, `a: b #forth metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'd_test.yaml' `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_pkgAndSkipFile(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.yaml", content: readFileA}, {path: "b_test.yaml", content: readFileB}, {path: "c_test.yaml", content: readFileC}, {path: "d_test.yaml", content: readFileD}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, FileSkipFunc: func(relPath string) bool { return relPath == "d_test.yaml" }, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 4) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a_test.yaml' `, `# second thing e: f g: h: - i # has a list - j metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'b_test.yaml' `, `a: b #third metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'c_test.yaml' `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_JSON(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.json", content: []byte(`{ "a": "b" }`), }, {path: "b_test.json", content: []byte(`{ "e": "f", "g": { "h": ["i", "j"] } }`), }, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, MatchFilesGlob: []string{"*.json"}, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 2) expected := []string{ `{"a": "b", metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'a_test.json'}}} `, `{"e": "f", "g": {"h": ["i", "j"]}, metadata: {annotations: {config.kubernetes.io/index: '0', config.kubernetes.io/path: 'b_test.json'}}} `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_file(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.yaml", content: readFileA}, {path: "b_test.yaml", content: readFileB}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: filepath.Join(path, "a_test.yaml"), FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 2) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a_test.yaml' `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_pkgOmitAnnotations(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.yaml", content: readFileA}, {path: "b_test.yaml", content: readFileB}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, OmitReaderAnnotations: true, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 3) expected := []string{ `a: b #first `, `c: d # second `, `# second thing e: f g: h: - i # has a list - j `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_PreserveSeqIndent(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a_test.yaml", content: readFileA}, {path: "b_test.yaml", content: readFileB}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, PreserveSeqIndent: true, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 3) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a_test.yaml' internal.config.kubernetes.io/seqindent: 'compact' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a_test.yaml' internal.config.kubernetes.io/seqindent: 'compact' `, `# second thing e: f g: h: - i # has a list - j metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'b_test.yaml' internal.config.kubernetes.io/seqindent: 'compact' `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) require.Equal(t, expected[i], val) } }) } func TestLocalPackageReader_Read_nestedDirs(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a/b/a_test.yaml", content: readFileA}, {path: "a/b/b_test.yaml", content: readFileB}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 3) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `# second thing e: f g: h: - i # has a list - j metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}b${SEP}b_test.yaml' `, } for i := range nodes { val, err := nodes[i].String() require.NoError(t, err) want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator)) require.Equal(t, want, val) } }) } func TestLocalPackageReader_Read_matchRegex(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a/b/a_test.yaml", content: readFileA}, {path: "a/b/b_test.yaml", content: readFileB}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, MatchFilesGlob: []string{`a*.yaml`}, FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 2) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, } for i, node := range nodes { val, err := node.String() require.NoError(t, err) want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator)) require.Equal(t, want, val) } }) } func TestLocalPackageReader_Read_skipSubpackage(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a/b/a_test.yaml", content: readFileA}, {path: "a/c/c_test.yaml", content: readFileB}, {path: "a/c/pkgFile", content: pkgFile}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, PackageFileName: "pkgFile", FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 2) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, } for i, node := range nodes { val, err := node.String() require.NoError(t, err) want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator)) require.Equal(t, want, val) } }) } func TestLocalPackageReader_Read_includeSubpackage(t *testing.T) { testOnDiskAndOnMem(t, []mockFile{ {path: "a/b"}, {path: "a/c"}, {path: "a/b/a_test.yaml", content: readFileA}, {path: "a/c/c_test.yaml", content: readFileB}, {path: "a/c/pkgFile", content: pkgFile}, }, func(t *testing.T, path string, mockFS filesys.FileSystem) { rfr := LocalPackageReader{ PackagePath: path, IncludeSubpackages: true, PackageFileName: "pkgFile", FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS}, } nodes, err := rfr.Read() require.NoError(t, err) require.Len(t, nodes, 3) expected := []string{ `a: b #first metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `c: d # second metadata: annotations: config.kubernetes.io/index: '1' config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml' `, `# second thing e: f g: h: - i # has a list - j metadata: annotations: config.kubernetes.io/index: '0' config.kubernetes.io/path: 'a${SEP}c${SEP}c_test.yaml' `, } for i, node := range nodes { val, err := node.String() require.NoError(t, err) want := strings.ReplaceAll(expected[i], "${SEP}", string(filepath.Separator)) require.Equal(t, want, val) } }) } type mockFile struct { path string // nil content implies this is a directory content []byte } func testOnDiskAndOnMem(t *testing.T, files []mockFile, f func(t *testing.T, path string, fs filesys.FileSystem)) { t.Run("on_disk", func(t *testing.T) { var dirs []string for _, file := range files { if file.content == nil { dirs = append(dirs, filepath.FromSlash(file.path)) } } s := SetupDirectories(t, dirs...) defer s.Clean() for _, file := range files { if file.content != nil { s.WriteFile(t, filepath.FromSlash(file.path), file.content) } } f(t, "./", nil) f(t, s.Root, nil) }) // TODO: Once fsnode supports Windows, we should also run the tests below. if runtime.GOOS == "windows" { return } t.Run("on_mem", func(t *testing.T) { fs := filesys.MakeFsInMemory() for _, file := range files { path := filepath.FromSlash(file.path) if file.content == nil { require.NoError(t, fs.MkdirAll(path)) } else { require.NoError(t, fs.WriteFile(path, file.content)) } } f(t, "/", fs) }) }