From 16bbc2d67e510fbe87e977d5bc28d4817a4a9eee Mon Sep 17 00:00:00 2001 From: Morten Torkildsen Date: Wed, 26 Aug 2020 17:40:04 -0700 Subject: [PATCH] Add support for .krmignore file --- kyaml/ext/ext.go | 10 ++ kyaml/go.mod | 1 + kyaml/go.sum | 2 + kyaml/kio/ignorefilesmatcher.go | 99 +++++++++++++ kyaml/kio/ignorefilesmatcher_test.go | 203 +++++++++++++++++++++++++++ kyaml/kio/pkgio_reader.go | 44 ++++-- kyaml/kio/pkgio_reader_test.go | 133 ++++++------------ kyaml/kio/testing.go | 55 ++++++++ 8 files changed, 448 insertions(+), 99 deletions(-) create mode 100644 kyaml/ext/ext.go create mode 100644 kyaml/kio/ignorefilesmatcher.go create mode 100644 kyaml/kio/ignorefilesmatcher_test.go create mode 100644 kyaml/kio/testing.go diff --git a/kyaml/ext/ext.go b/kyaml/ext/ext.go new file mode 100644 index 000000000..312781b6e --- /dev/null +++ b/kyaml/ext/ext.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package ext + +// GetIgnoreFileName returns the name for ignore files in +// packages. It can be overridden by tools using this library. +var GetIgnoreFileName = func() string { + return ".krmignore" +} diff --git a/kyaml/go.mod b/kyaml/go.mod index 6b683303b..c9a29a565 100644 --- a/kyaml/go.mod +++ b/kyaml/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-openapi/spec v0.19.5 github.com/go-openapi/strfmt v0.19.5 github.com/go-openapi/validate v0.19.8 + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d github.com/sergi/go-diff v1.1.0 github.com/spf13/cobra v1.0.0 diff --git a/kyaml/go.sum b/kyaml/go.sum index d4af2875b..a0bf4e338 100644 --- a/kyaml/go.sum +++ b/kyaml/go.sum @@ -145,6 +145,8 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= diff --git a/kyaml/kio/ignorefilesmatcher.go b/kyaml/kio/ignorefilesmatcher.go new file mode 100644 index 000000000..917c1f6a8 --- /dev/null +++ b/kyaml/kio/ignorefilesmatcher.go @@ -0,0 +1,99 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kio + +import ( + "os" + "path/filepath" + "strings" + + "github.com/monochromegane/go-gitignore" + "sigs.k8s.io/kustomize/kyaml/ext" +) + +// ignoreFilesMatcher handles `.krmignore` files, which allows for ignoring +// files or folders in a package. The format of this file is a subset of the +// gitignore format, with recursive patterns (like a/**/c) not supported. If a +// file or folder matches any of the patterns in the .krmignore file for the +// package, it will be excluded. +// +// It works as follows: +// +// * It will look for .krmignore file in the top folder and on the top level +// of any subpackages. Subpackages are defined by the presence of a Krmfile +// in the folder. +// * `.krmignore` files only cover files and folders for the package in which +// it is defined. So ignore patterns defined in a parent package does not +// affect which files are ignored from a subpackage. +// * An ignore pattern can not ignore a subpackage. So even if the parent +// package contains a pattern that ignores the directory foo, if foo is a +// subpackage, it will still be included if the IncludeSubpackages property +// is set to true +type ignoreFilesMatcher struct { + matchers []matcher +} + +// readIgnoreFile checks whether there is a .krmignore file in the path, and +// if it is, reads it in and turns it into a matcher. If we can't find a file, +// we just add a matcher that match nothing. +func (i *ignoreFilesMatcher) readIgnoreFile(path string) error { + m, err := gitignore.NewGitIgnore(filepath.Join(path, ext.GetIgnoreFileName())) + if err != nil { + if os.IsNotExist(err) { + i.matchers = append(i.matchers, matcher{ + matcher: gitignore.DummyIgnoreMatcher(false), + basePath: path, + }) + return nil + } + return err + } + i.matchers = append(i.matchers, matcher{ + matcher: m, + basePath: path, + }) + return nil +} + +// verifyPath checks whether the top matcher on the stack +// is correct for the provided filepath. Matchers are removed once +// we encounter a filepath that is not a subpath of the basepath for +// the matcher. +func (i *ignoreFilesMatcher) verifyPath(path string) { + for j := len(i.matchers) - 1; j >= 0; j-- { + matcher := i.matchers[j] + if !strings.HasPrefix(path, matcher.basePath) { + i.matchers = i.matchers[:j] + return + } + } +} + +// matchFile checks whether the file given by the provided path matches +// any of the patterns in the .krmignore file for the package. +func (i *ignoreFilesMatcher) matchFile(path string) bool { + if len(i.matchers) == 0 { + return false + } + i.verifyPath(path) + return i.matchers[len(i.matchers)-1].matcher.Match(path, false) +} + +// matchFile checks whether the directory given by the provided path matches +// any of the patterns in the .krmignore file for the package. +func (i *ignoreFilesMatcher) matchDir(path string) bool { + if len(i.matchers) == 0 { + return false + } + i.verifyPath(path) + return i.matchers[len(i.matchers)-1].matcher.Match(path, true) +} + +// matcher wraps the gitignore matcher and the path to the folder +// where the file was found. +type matcher struct { + matcher gitignore.IgnoreMatcher + + basePath string +} diff --git a/kyaml/kio/ignorefilesmatcher_test.go b/kyaml/kio/ignorefilesmatcher_test.go new file mode 100644 index 000000000..981274299 --- /dev/null +++ b/kyaml/kio/ignorefilesmatcher_test.go @@ -0,0 +1,203 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kio + +import ( + "io/ioutil" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIgnoreFilesMatcher_readIgnoreFile(t *testing.T) { + testCases := []struct { + name string + writeIgnoreFile bool + isMatch bool + }{ + { + name: "has .krmignore file", + writeIgnoreFile: true, + isMatch: true, + }, + { + name: "no .krmignore file", + writeIgnoreFile: false, + isMatch: false, + }, + } + + for i := range testCases { + test := testCases[i] + t.Run(test.name, func(t *testing.T) { + dir, err := ioutil.TempDir("", "kyaml-test") + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + + if test.writeIgnoreFile { + ignoreFilePath := filepath.Join(dir, ".krmignore") + err = ioutil.WriteFile(ignoreFilePath, []byte(` +testfile.yaml +`), 0600) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + } + testFilePath := filepath.Join(dir, "testfile.yaml") + err = ioutil.WriteFile(testFilePath, []byte{}, 0600) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + + ignoreFilesMatcher := ignoreFilesMatcher{} + err = ignoreFilesMatcher.readIgnoreFile(dir) + if !assert.NoError(t, err) { + t.FailNow() + } + assert.Equal(t, test.isMatch, ignoreFilesMatcher.matchFile(testFilePath)) + }) + } +} + +var ( + readFileA = []byte(` +a: a +--- +c: c +`) + readFileB = []byte(` +b: b +`) +) + +func TestLocalPackageReader_Read_ignoreFile(t *testing.T) { + testCases := []struct { + name string + directories []string + files map[string][]byte + expected []string + }{ + { + name: "ignore file", + directories: []string{ + filepath.Join("a", "b"), + filepath.Join("a", "c"), + }, + files: map[string][]byte{ + filepath.Join("pkgFile"): {}, + filepath.Join("a", "b", "a_test.yaml"): readFileA, + filepath.Join("a", "c", "c_test.yaml"): readFileB, + filepath.Join(".krmignore"): []byte(` +a/c/c_test.yaml +`, + ), + }, + expected: []string{ + `a: a`, + `c: c`, + }, + }, + { + name: "ignore folder", + directories: []string{ + filepath.Join("a", "b"), + filepath.Join("a", "c"), + }, + files: map[string][]byte{ + filepath.Join("pkgFile"): {}, + filepath.Join("a", "b", "a_test.yaml"): readFileA, + filepath.Join("a", "c", "c_test.yaml"): readFileB, + filepath.Join(".krmignore"): []byte(` +a/c +`, + ), + }, + expected: []string{ + `a: a`, + `c: c`, + }, + }, + { + name: "krmignore file in subpackage", + directories: []string{ + filepath.Join("a", "c"), + }, + files: map[string][]byte{ + filepath.Join("pkgFile"): {}, + filepath.Join("a", "c", "a_test.yaml"): readFileA, + filepath.Join("a", "c", "c_test.yaml"): readFileB, + filepath.Join(".krmignore"): []byte(` +d/e/f.yaml +`, + ), + filepath.Join("a", "c", "pkgFile"): {}, + filepath.Join("a", "c", ".krmignore"): []byte(` +a_test.yaml +`), + }, + expected: []string{ + `b: b`, + }, + }, + { + name: "krmignore files does not affect subpackages", + directories: []string{ + filepath.Join("a", "c"), + }, + files: map[string][]byte{ + filepath.Join("pkgFile"): {}, + filepath.Join("a", "c", "a_test.yaml"): readFileA, + filepath.Join("a", "c", "c_test.yaml"): readFileB, + filepath.Join(".krmignore"): []byte(` +a/c/c_test.yaml +`, + ), + filepath.Join("a", "c", "pkgFile"): {}, + filepath.Join("a", "c", ".krmignore"): []byte(` +a_test.yaml +`), + }, + expected: []string{ + `b: b`, + }, + }, + } + + for i := range testCases { + test := testCases[i] + t.Run(test.name, func(t *testing.T) { + s := SetupDirectories(t, test.directories...) + defer s.Clean() + for path, content := range test.files { + s.WriteFile(t, path, content) + } + + // empty path + rfr := LocalPackageReader{ + PackagePath: s.Root, + IncludeSubpackages: true, + PackageFileName: "pkgFile", + OmitReaderAnnotations: true, + } + nodes, err := rfr.Read() + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + + if !assert.Len(t, nodes, len(test.expected)) { + assert.FailNow(t, "wrong number items") + } + + for i, node := range nodes { + val, err := node.String() + assert.NoError(t, err) + want := strings.ReplaceAll(test.expected[i], "${SEP}", string(filepath.Separator)) + assert.Equal(t, strings.TrimSpace(want), strings.TrimSpace(val)) + } + }) + } +} diff --git a/kyaml/kio/pkgio_reader.go b/kyaml/kio/pkgio_reader.go index d0f4c0ee6..1a9077bb3 100644 --- a/kyaml/kio/pkgio_reader.go +++ b/kyaml/kio/pkgio_reader.go @@ -184,8 +184,13 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) { var operand ResourceNodeSlice var pathRelativeTo string - r.PackagePath = filepath.Clean(r.PackagePath) - err := filepath.Walk(r.PackagePath, func( + var err error + ignoreFilesMatcher := &ignoreFilesMatcher{} + r.PackagePath, err = filepath.Abs(r.PackagePath) + if err != nil { + return nil, errors.Wrap(err) + } + err = filepath.Walk(r.PackagePath, func( path string, info os.FileInfo, err error) error { if err != nil { return errors.Wrap(err) @@ -194,9 +199,10 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) { // is this the user specified path? if path == r.PackagePath { if info.IsDir() { - // skip the root package directory + // skip the root package directory, but check for a + // .krmignore file first. pathRelativeTo = r.PackagePath - return nil + return ignoreFilesMatcher.readIgnoreFile(path) } // user specified path is a file rather than a directory. @@ -206,9 +212,9 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) { // check if we should skip the directory or file if info.IsDir() { - return r.ShouldSkipDir(path) + return r.ShouldSkipDir(path, ignoreFilesMatcher) } - if match, err := r.ShouldSkipFile(info); err != nil { + if match, err := r.ShouldSkipFile(path, ignoreFilesMatcher); err != nil { return err } else if !match { // skip this file @@ -251,10 +257,15 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode } // ShouldSkipFile returns true if the file should be skipped -func (r *LocalPackageReader) ShouldSkipFile(info os.FileInfo) (bool, error) { +func (r *LocalPackageReader) ShouldSkipFile(path string, matcher *ignoreFilesMatcher) (bool, error) { + // check if the file is covered by a .krmignore file. + if matcher.matchFile(path) { + return false, nil + } + // check if the files are in scope for _, g := range r.MatchFilesGlob { - if match, err := filepath.Match(g, info.Name()); err != nil { + if match, err := filepath.Match(g, filepath.Base(path)); err != nil { return false, errors.Wrap(err) } else if match { return true, nil @@ -274,13 +285,22 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) { } // ShouldSkipDir returns a filepath.SkipDir if the directory should be skipped -func (r *LocalPackageReader) ShouldSkipDir(path string) error { +func (r *LocalPackageReader) ShouldSkipDir(path string, matcher *ignoreFilesMatcher) error { if r.PackageFileName == "" { + // If the folder is not a package, but covered by the .krmignore file, + // we skip it. + if matcher.matchDir(path) { + return filepath.SkipDir + } return nil } // check if this is a subpackage _, err := os.Stat(filepath.Join(path, r.PackageFileName)) if os.IsNotExist(err) { + // Skip the folder if it is covered by the .krmignore file. + if matcher.matchDir(path) { + return filepath.SkipDir + } return nil } else if err != nil { return errors.Wrap(err) @@ -288,5 +308,9 @@ func (r *LocalPackageReader) ShouldSkipDir(path string) error { if !r.IncludeSubpackages { return filepath.SkipDir } - return nil + // We don't allow the .krmignore file in a package cause us to skip + // a subpackage. So if we have found a package file in the folder and + // we should include subpackages, we don't check the .krmignore file. We + // do however check whether the package contains a .krmignore file. + return matcher.readIgnoreFile(path) } diff --git a/kyaml/kio/pkgio_reader_test.go b/kyaml/kio/pkgio_reader_test.go index 7f68fdd73..4d2cc844b 100644 --- a/kyaml/kio/pkgio_reader_test.go +++ b/kyaml/kio/pkgio_reader_test.go @@ -4,59 +4,14 @@ package kio_test import ( - "io/ioutil" - "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" . "sigs.k8s.io/kustomize/kyaml/kio" - // "sigs.k8s.io/kustomize/kyaml/testutil" ) -// setup creates directories and files for testing -type setup struct { - // root is the tmp directory - root string -} - -// setupDirectories creates directories for reading test configuration from -func setupDirectories(t *testing.T, dirs ...string) setup { - d, err := ioutil.TempDir("", "kyaml-test") - if !assert.NoError(t, err) { - assert.FailNow(t, err.Error()) - } - err = os.Chdir(d) - if !assert.NoError(t, err) { - assert.FailNow(t, err.Error()) - } - for _, s := range dirs { - err = os.MkdirAll(s, 0700) - if !assert.NoError(t, err) { - assert.FailNow(t, err.Error()) - } - } - return setup{root: d} -} - -// writeFile writes a file under the test directory -func (s setup) writeFile(t *testing.T, path string, value []byte) { - err := os.MkdirAll(filepath.Dir(filepath.Join(s.root, path)), 0700) - if !assert.NoError(t, err) { - assert.FailNow(t, err.Error()) - } - err = ioutil.WriteFile(filepath.Join(s.root, path), value, 0600) - if !assert.NoError(t, err) { - assert.FailNow(t, err.Error()) - } -} - -// clean deletes the test config -func (s setup) clean() { - os.RemoveAll(s.root) -} - var readFileA = []byte(`--- a: b #first --- @@ -94,18 +49,18 @@ func TestLocalPackageReader_Read_empty(t *testing.T) { } func TestLocalPackageReader_Read_pkg(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("b_test.yaml"), readFileB) - s.writeFile(t, filepath.Join("c_test.yaml"), readFileC) - s.writeFile(t, filepath.Join("d_test.yaml"), readFileD) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("b_test.yaml"), readFileB) + s.WriteFile(t, filepath.Join("c_test.yaml"), readFileC) + s.WriteFile(t, filepath.Join("d_test.yaml"), readFileD) paths := []struct { path string }{ {path: "./"}, - {path: s.root}, + {path: s.Root}, } for _, p := range paths { rfr := LocalPackageReader{PackagePath: p.path} @@ -167,13 +122,13 @@ metadata: } func TestLocalPackageReader_Read_JSON(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() - s.writeFile(t, filepath.Join("a_test.json"), []byte(`{ + s.WriteFile(t, filepath.Join("a_test.json"), []byte(`{ "a": "b" }`)) - s.writeFile(t, filepath.Join("b_test.json"), []byte(`{ + s.WriteFile(t, filepath.Join("b_test.json"), []byte(`{ "e": "f", "g": { "h": ["i", "j"] @@ -184,7 +139,7 @@ func TestLocalPackageReader_Read_JSON(t *testing.T) { path string }{ {path: "./"}, - {path: s.root}, + {path: s.Root}, } for _, p := range paths { rfr := LocalPackageReader{PackagePath: p.path, MatchFilesGlob: []string{"*.json"}} @@ -217,16 +172,16 @@ func TestLocalPackageReader_Read_JSON(t *testing.T) { } func TestLocalPackageReader_Read_file(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("b_test.yaml"), readFileB) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("b_test.yaml"), readFileB) paths := []struct { path string }{ {path: "./"}, - {path: s.root}, + {path: s.Root}, } for _, p := range paths { rfr := LocalPackageReader{PackagePath: filepath.Join(p.path, "a_test.yaml")} @@ -265,16 +220,16 @@ metadata: } func TestLocalPackageReader_Read_pkgOmitAnnotations(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("b_test.yaml"), readFileB) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("b_test.yaml"), readFileB) paths := []struct { path string }{ {path: "./"}, - {path: s.root}, + {path: s.Root}, } for _, p := range paths { // empty path @@ -313,16 +268,16 @@ g: } func TestLocalPackageReader_Read_nestedDirs(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("a", "b", "b_test.yaml"), readFileB) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("a", "b", "b_test.yaml"), readFileB) paths := []struct { path string }{ {path: "./"}, - {path: s.root}, + {path: s.Root}, } for _, p := range paths { // empty path @@ -374,13 +329,13 @@ metadata: } func TestLocalPackageReader_Read_matchRegex(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("a", "b", "b_test.yaml"), readFileB) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("a", "b", "b_test.yaml"), readFileB) // empty path - rfr := LocalPackageReader{PackagePath: s.root, MatchFilesGlob: []string{`a*.yaml`}} + rfr := LocalPackageReader{PackagePath: s.Root, MatchFilesGlob: []string{`a*.yaml`}} nodes, err := rfr.Read() if !assert.NoError(t, err) { assert.FailNow(t, err.Error()) @@ -414,14 +369,14 @@ metadata: } func TestLocalPackageReader_Read_skipSubpackage(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("a", "c", "c_test.yaml"), readFileB) - s.writeFile(t, filepath.Join("a", "c", "pkgFile"), pkgFile) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("a", "c", "c_test.yaml"), readFileB) + s.WriteFile(t, filepath.Join("a", "c", "pkgFile"), pkgFile) // empty path - rfr := LocalPackageReader{PackagePath: s.root, PackageFileName: "pkgFile"} + rfr := LocalPackageReader{PackagePath: s.Root, PackageFileName: "pkgFile"} nodes, err := rfr.Read() if !assert.NoError(t, err) { assert.FailNow(t, err.Error()) @@ -455,14 +410,14 @@ metadata: } func TestLocalPackageReader_Read_includeSubpackage(t *testing.T) { - s := setupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) - defer s.clean() - s.writeFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) - s.writeFile(t, filepath.Join("a", "c", "c_test.yaml"), readFileB) - s.writeFile(t, filepath.Join("a", "c", "pkgFile"), pkgFile) + s := SetupDirectories(t, filepath.Join("a", "b"), filepath.Join("a", "c")) + defer s.Clean() + s.WriteFile(t, filepath.Join("a", "b", "a_test.yaml"), readFileA) + s.WriteFile(t, filepath.Join("a", "c", "c_test.yaml"), readFileB) + s.WriteFile(t, filepath.Join("a", "c", "pkgFile"), pkgFile) // empty path - rfr := LocalPackageReader{PackagePath: s.root, IncludeSubpackages: true, PackageFileName: "pkgFile"} + rfr := LocalPackageReader{PackagePath: s.Root, IncludeSubpackages: true, PackageFileName: "pkgFile"} nodes, err := rfr.Read() if !assert.NoError(t, err) { assert.FailNow(t, err.Error()) diff --git a/kyaml/kio/testing.go b/kyaml/kio/testing.go new file mode 100644 index 000000000..67471c665 --- /dev/null +++ b/kyaml/kio/testing.go @@ -0,0 +1,55 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package kio + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Setup creates directories and files for testing +type Setup struct { + // root is the tmp directory + Root string +} + +// setupDirectories creates directories for reading test configuration from +func SetupDirectories(t *testing.T, dirs ...string) Setup { + d, err := ioutil.TempDir("", "kyaml-test") + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + err = os.Chdir(d) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + for _, s := range dirs { + err = os.MkdirAll(s, 0700) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + } + return Setup{Root: d} +} + +// writeFile writes a file under the test directory +func (s Setup) WriteFile(t *testing.T, path string, value []byte) { + err := os.MkdirAll(filepath.Dir(filepath.Join(s.Root, path)), 0700) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } + err = ioutil.WriteFile(filepath.Join(s.Root, path), value, 0600) + if !assert.NoError(t, err) { + assert.FailNow(t, err.Error()) + } +} + +// clean deletes the test config +func (s Setup) Clean() { + os.RemoveAll(s.Root) +}