From c722d4cd17c1128792b9bda54f351eebafa37837 Mon Sep 17 00:00:00 2001 From: Jeffrey Regan Date: Mon, 25 Nov 2019 16:55:50 -0800 Subject: [PATCH] Make filesys walk work. --- api/filesys/fileinfo.go | 8 +- api/filesys/fileinmemory.go | 56 -- api/filesys/filesystem.go | 4 +- api/filesys/fsinmemory.go | 218 ----- api/filesys/fsinmemory_test.go | 149 ---- api/filesys/fsnode.go | 557 +++++++++++++ api/filesys/fsnode_test.go | 788 ++++++++++++++++++ api/krusty/kustomizer_test.go | 11 +- api/loader/fileloader.go | 3 +- api/loader/fileloader_test.go | 2 +- .../internal/commands/create/create_test.go | 2 +- .../commands/edit/add/addpatch_test.go | 6 +- .../commands/edit/add/addresource_test.go | 2 +- .../commands/edit/add/flagsandargs_test.go | 4 +- .../commands/edit/remove/removepatch_test.go | 2 +- 15 files changed, 1369 insertions(+), 443 deletions(-) delete mode 100644 api/filesys/fileinmemory.go delete mode 100644 api/filesys/fsinmemory.go delete mode 100644 api/filesys/fsinmemory_test.go create mode 100644 api/filesys/fsnode.go create mode 100644 api/filesys/fsnode_test.go diff --git a/api/filesys/fileinfo.go b/api/filesys/fileinfo.go index 991580997..57646d244 100644 --- a/api/filesys/fileinfo.go +++ b/api/filesys/fileinfo.go @@ -12,14 +12,14 @@ var _ os.FileInfo = fileInfo{} // fileInfo implements os.FileInfo for a fileInMemory instance. type fileInfo struct { - *fileInMemory + node *fsNode } // Name returns the name of the file -func (fi fileInfo) Name() string { return fi.name } +func (fi fileInfo) Name() string { return fi.node.Name() } // Size returns the size of the file -func (fi fileInfo) Size() int64 { return int64(len(fi.content)) } +func (fi fileInfo) Size() int64 { return fi.node.Size() } // Mode returns the file mode func (fi fileInfo) Mode() os.FileMode { return 0777 } @@ -28,7 +28,7 @@ func (fi fileInfo) Mode() os.FileMode { return 0777 } func (fi fileInfo) ModTime() time.Time { return time.Time{} } // IsDir returns true if it is a directory -func (fi fileInfo) IsDir() bool { return fi.dir } +func (fi fileInfo) IsDir() bool { return fi.node.isNodeADir() } // Sys should return underlying data source, but it now returns nil func (fi fileInfo) Sys() interface{} { return nil } diff --git a/api/filesys/fileinmemory.go b/api/filesys/fileinmemory.go deleted file mode 100644 index 8f90522c2..000000000 --- a/api/filesys/fileinmemory.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package filesys - -import ( - "bytes" - "os" -) - -var _ File = &fileInMemory{} - -// fileInMemory implements File in-memory for tests. -type fileInMemory struct { - name string - content []byte - dir bool - open bool -} - -// makeDir makes a fake directory. -func makeDir(name string) *fileInMemory { - return &fileInMemory{name: name, dir: true} -} - -// Close marks the fake file closed. -func (f *fileInMemory) Close() error { - f.open = false - return nil -} - -// Read never fails, and doesn't mutate p. -func (f *fileInMemory) Read(p []byte) (n int, err error) { - return len(p), nil -} - -// Write saves the contents of the argument to memory. -func (f *fileInMemory) Write(p []byte) (n int, err error) { - f.content = p - return len(p), nil -} - -// ContentMatches returns true if v matches fake file's content. -func (f *fileInMemory) ContentMatches(v []byte) bool { - return bytes.Equal(v, f.content) -} - -// GetContent the content of a fake file. -func (f *fileInMemory) GetContent() []byte { - return f.content -} - -// Stat returns nil. -func (f *fileInMemory) Stat() (os.FileInfo, error) { - return nil, nil -} diff --git a/api/filesys/filesystem.go b/api/filesys/filesystem.go index 066247dc9..041b4bdb3 100644 --- a/api/filesys/filesystem.go +++ b/api/filesys/filesystem.go @@ -10,7 +10,6 @@ import ( const ( Separator = string(filepath.Separator) - doubleSep = Separator + Separator SelfDir = "." ParentDir = ".." ) @@ -38,7 +37,8 @@ type FileSystem interface { CleanedAbs(path string) (ConfirmedDir, string, error) // Exists is true if the path exists in the file system. Exists(path string) bool - // Glob returns the list of matching files + // Glob returns the list of matching files, + // emulating https://golang.org/pkg/path/filepath/#Glob Glob(pattern string) ([]string, error) // ReadFile returns the contents of the file at the given path. ReadFile(path string) ([]byte, error) diff --git a/api/filesys/fsinmemory.go b/api/filesys/fsinmemory.go deleted file mode 100644 index 7710a0e4a..000000000 --- a/api/filesys/fsinmemory.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package filesys - -import ( - "fmt" - "os" - "path/filepath" - "sort" - "strings" -) - -var _ FileSystem = &fsInMemory{} - -// fsInMemory implements FileSystem using a in-memory filesystem -// primarily for use in tests. -type fsInMemory struct { - m map[string]*fileInMemory -} - -// MakeFsInMemory returns an instance of fsInMemory with no files in it. -func MakeFsInMemory() FileSystem { - result := &fsInMemory{m: map[string]*fileInMemory{}} - result.Mkdir(Separator) - return result -} - -// Create assures a fake file appears in the in-memory file system. -func (fs *fsInMemory) Create(name string) (File, error) { - f := &fileInMemory{} - f.open = true - fs.m[name] = f - return fs.m[name], nil -} - -// Mkdir assures a fake directory appears in the in-memory file system. -func (fs *fsInMemory) Mkdir(name string) error { - fs.m[name] = makeDir(name) - return nil -} - -// MkdirAll delegates to Mkdir -func (fs *fsInMemory) MkdirAll(name string) error { - return fs.Mkdir(name) -} - -// RemoveAll presumably does rm -r on a path. -// There's no error. -func (fs *fsInMemory) RemoveAll(name string) error { - var toRemove []string - for k := range fs.m { - if strings.HasPrefix(k, name) { - toRemove = append(toRemove, k) - } - } - for _, k := range toRemove { - delete(fs.m, k) - } - return nil -} - -// Open returns a fake file in the open state. -func (fs *fsInMemory) Open(name string) (File, error) { - if _, found := fs.m[name]; !found { - return nil, fmt.Errorf("file %q cannot be opened", name) - } - return fs.m[name], nil -} - -// CleanedAbs cannot fail. -func (fs *fsInMemory) CleanedAbs(path string) (ConfirmedDir, string, error) { - if fs.IsDir(path) { - return ConfirmedDir(path), "", nil - } - d := filepath.Dir(path) - if d == path { - return ConfirmedDir(d), "", nil - } - return ConfirmedDir(d), filepath.Base(path), nil -} - -// Exists returns true if file is known. -func (fs *fsInMemory) Exists(name string) bool { - _, found := fs.m[name] - return found -} - -// Glob returns the list of matching files -func (fs *fsInMemory) Glob(pattern string) ([]string, error) { - var result []string - for p := range fs.m { - if fs.pathMatch(p, pattern) { - result = append(result, p) - } - } - sort.Strings(result) - return result, nil -} - -// IsDir returns true if the file exists and is a directory. -func (fs *fsInMemory) IsDir(name string) bool { - f, found := fs.m[name] - if found && f.dir { - return true - } - if !strings.HasSuffix(name, Separator) { - name = name + Separator - } - for k := range fs.m { - if strings.HasPrefix(k, name) { - return true - } - } - return false -} - -// ReadFile always returns an empty bytes and error depending on content of m. -func (fs *fsInMemory) ReadFile(name string) ([]byte, error) { - if ff, found := fs.m[name]; found { - return ff.content, nil - } - return nil, fmt.Errorf("'%s' doesn't exist", name) -} - -// WriteFile always succeeds and does nothing. -func (fs *fsInMemory) WriteFile(name string, c []byte) error { - ff := &fileInMemory{} - ff.Write(c) - fs.m[name] = ff - return nil -} - -// Walk implements filepath.Walk using the fake filesystem. -func (fs *fsInMemory) Walk(path string, walkFn filepath.WalkFunc) error { - info, err := fs.lstat(path) - if err != nil { - err = walkFn(path, info, err) - } else { - err = fs.walk(path, info, walkFn) - } - if err == filepath.SkipDir { - return nil - } - return err -} - -func (fs *fsInMemory) pathMatch(path, pattern string) bool { - match, _ := filepath.Match(pattern, path) - return match -} - -func (fs *fsInMemory) lstat(path string) (*fileInfo, error) { - f, found := fs.m[path] - if !found { - return nil, os.ErrNotExist - } - return &fileInfo{f}, nil -} - -func (fs *fsInMemory) join(elem ...string) string { - for i, e := range elem { - if e != "" { - return strings.Replace( - strings.Join(elem[i:], Separator), doubleSep, Separator, -1) - } - } - return "" -} - -func (fs *fsInMemory) readDirNames(path string) []string { - var names []string - if !strings.HasSuffix(path, Separator) { - path += Separator - } - pathSegments := strings.Count(path, Separator) - for name := range fs.m { - if name == path { - continue - } - if strings.Count(name, Separator) > pathSegments { - continue - } - if strings.HasPrefix(name, path) { - names = append(names, filepath.Base(name)) - } - } - sort.Strings(names) - return names -} - -func (fs *fsInMemory) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { - if !info.IsDir() { - return walkFn(path, info, nil) - } - - names := fs.readDirNames(path) - if err := walkFn(path, info, nil); err != nil { - return err - } - for _, name := range names { - filename := fs.join(path, name) - fileInfo, err := fs.lstat(filename) - if err != nil { - if err := walkFn(filename, fileInfo, os.ErrNotExist); err != nil && err != filepath.SkipDir { - return err - } - } else { - err = fs.walk(filename, fileInfo, walkFn) - if err != nil { - if !fileInfo.IsDir() || err != filepath.SkipDir { - return err - } - } - } - } - return nil -} diff --git a/api/filesys/fsinmemory_test.go b/api/filesys/fsinmemory_test.go deleted file mode 100644 index 4520febbe..000000000 --- a/api/filesys/fsinmemory_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package filesys_test - -import ( - "bytes" - "reflect" - "testing" - - . "sigs.k8s.io/kustomize/api/filesys" -) - -func TestOldExists(t *testing.T) { - fSys := MakeFsInMemory() - if fSys.Exists("foo") { - t.Fatalf("expected no foo") - } - fSys.Mkdir("/") - if !fSys.IsDir("/") { - t.Fatalf("expected dir at /") - } -} - -func TestIsDir(t *testing.T) { - fSys := MakeFsInMemory() - expectedName := "my-dir" - err := fSys.Mkdir(expectedName) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - shouldExist(t, fSys, expectedName) - if !fSys.IsDir(expectedName) { - t.Fatalf(expectedName + " should be a dir") - } -} - -func shouldExist(t *testing.T, fSys FileSystem, name string) { - if !fSys.Exists(name) { - t.Fatalf(name + " should exist") - } -} - -func shouldNotExist(t *testing.T, fSys FileSystem, name string) { - if fSys.Exists(name) { - t.Fatalf(name + " should not exist") - } -} - -func TestRemoveAll(t *testing.T) { - fSys := MakeFsInMemory() - fSys.WriteFile("/foo/project/file.yaml", []byte("Unused")) - fSys.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused")) - fSys.WriteFile("/foo/apple/subdir/file.yaml", []byte("Unused")) - shouldExist(t, fSys, "/foo/project/file.yaml") - shouldExist(t, fSys, "/foo/project/subdir/file.yaml") - shouldExist(t, fSys, "/foo/apple/subdir/file.yaml") - err := fSys.RemoveAll("/foo/project") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - shouldNotExist(t, fSys, "/foo/project/file.yaml") - shouldNotExist(t, fSys, "/foo/project/subdir/file.yaml") - shouldExist(t, fSys, "/foo/apple/subdir/file.yaml") -} - -func TestIsDirDeeper(t *testing.T) { - fSys := MakeFsInMemory() - fSys.WriteFile("/foo/project/file.yaml", []byte("Unused")) - fSys.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused")) - if !fSys.IsDir("/") { - t.Fatalf("/ should be a dir") - } - if !fSys.IsDir("/foo") { - t.Fatalf("/foo should be a dir") - } - if !fSys.IsDir("/foo/project") { - t.Fatalf("/foo/project should be a dir") - } - if fSys.IsDir("/fo") { - t.Fatalf("/fo should not be a dir") - } - if fSys.IsDir("/x") { - t.Fatalf("/x should not be a dir") - } -} - -func TestCreate(t *testing.T) { - fSys := MakeFsInMemory() - f, err := fSys.Create("foo") - if f == nil { - t.Fatalf("expected file") - } - if err != nil { - t.Fatalf("unexpected error") - } - shouldExist(t, fSys, "foo") -} - -func TestReadFile(t *testing.T) { - fSys := MakeFsInMemory() - f, err := fSys.Create("foo") - if f == nil { - t.Fatalf("expected file") - } - if err != nil { - t.Fatalf("unexpected error") - } - content, err := fSys.ReadFile("foo") - if len(content) != 0 { - t.Fatalf("expected no content") - } - if err != nil { - t.Fatalf("expected no error") - } -} - -func TestWriteFile(t *testing.T) { - fSys := MakeFsInMemory() - c := []byte("heybuddy") - err := fSys.WriteFile("foo", c) - if err != nil { - t.Fatalf("expected no error") - } - content, err := fSys.ReadFile("foo") - if err != nil { - t.Fatalf("expected read to work: %v", err) - } - if !bytes.Equal(c, content) { - t.Fatalf("incorrect content: %v", content) - } -} - -func TestOldGlob(t *testing.T) { - fSys := MakeFsInMemory() - fSys.Create("dir/foo") - fSys.Create("dir/bar") - files, err := fSys.Glob("dir/*") - if err != nil { - t.Fatalf("expected no error") - } - expected := []string{ - "dir/bar", - "dir/foo", - } - if !reflect.DeepEqual(files, expected) { - t.Fatalf("incorrect files found by glob: %v", files) - } -} diff --git a/api/filesys/fsnode.go b/api/filesys/fsnode.go new file mode 100644 index 000000000..e81c3df78 --- /dev/null +++ b/api/filesys/fsnode.go @@ -0,0 +1,557 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesys + +import ( + "bytes" + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/pkg/errors" +) + +var _ File = &fsNode{} +var _ FileSystem = &fsNode{} + +// fsNode is either a file or a directory. +type fsNode struct { + // What node owns me? + parent *fsNode + + // Value to return as the Name() when the + // parent is nil. + nilParentName string + + // A directory mapping names to nodes. + // If dir is nil, then self node is a file. + // If dir is non-nil, then self node is a directory, + // albeit possibly an empty directory. + dir map[string]*fsNode + + // if this node is a file, this is the content. + content []byte + + // if this node is a file, this tracks whether or + // not it is "open". + open bool +} + +// MakeEmptyDirInMemory returns an empty directory. +// The paths of nodes in this object will never +// report a leading Separator, meaning they +// aren't "absolute" in the sense defined by +// https://golang.org/pkg/path/filepath/#IsAbs. +func MakeEmptyDirInMemory() *fsNode { + return &fsNode{ + dir: make(map[string]*fsNode), + } +} + +// MakeFsInMemory returns an empty 'file system'. +// The paths of nodes in this object will always +// report a leading Separator, meaning they +// are "absolute" in the sense defined by +// https://golang.org/pkg/path/filepath/#IsAbs. +// This is a relevant difference when using Walk, +// Glob, Match, etc. +func MakeFsInMemory() FileSystem { + return &fsNode{ + nilParentName: Separator, + dir: make(map[string]*fsNode), + } +} + +// Name returns the name of the node. +func (n *fsNode) Name() string { + if n.parent == nil { + // Unable to lookup name in parent. + return n.nilParentName + } + if !n.parent.isNodeADir() { + log.Fatal("parent not a dir") + } + for key, value := range n.parent.dir { + if value == n { + return key + } + } + log.Fatal("unable to find fsNode name") + return "" +} + +// Path returns the full path to the node. +func (n *fsNode) Path() string { + if n.parent == nil { + return n.nilParentName + } + if !n.parent.isNodeADir() { + log.Fatal("parent not a dir, structural error") + } + return filepath.Join(n.parent.Path(), n.Name()) +} + +// mySplit trims trailing separators from the directory +// result of filepath.Split. +func mySplit(s string) (string, string) { + dName, fName := filepath.Split(s) + return StripTrailingSeps(dName), fName +} + +func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) { + parent := n + dName, fileName := mySplit(name) + if dName != "" { + parent, err = parent.addDir(dName) + if err != nil { + return nil, err + } + } + if !isLegalFileNameForCreation(fileName) { + return nil, fmt.Errorf( + "illegal name '%s' in file creation", fileName) + } + result, ok := parent.dir[fileName] + if ok { + // File already exists; overwrite it. + result.content = c + return result, nil + } + result = &fsNode{ + content: c, + parent: parent, + } + parent.dir[fileName] = result + return result, nil +} + +// Create implements FileSystem. +// Create makes an empty file. +func (n *fsNode) Create(path string) (result File, err error) { + return n.AddFile(path, []byte{}) +} + +// WriteFile implements FileSystem. +func (n *fsNode) WriteFile(path string, d []byte) error { + _, err := n.AddFile(path, d) + return err +} + +// AddFile adds a file and any necessary containing +// directories to the node. +func (n *fsNode) AddFile( + name string, c []byte) (result *fsNode, err error) { + if n.dir == nil { + return nil, fmt.Errorf( + "cannot add a file to a non-directory '%s'", n.Name()) + } + return n.addFile(cleanQueryPath(name), c) +} + +func (n *fsNode) addDir(path string) (result *fsNode, err error) { + parent := n + dName, subDirName := mySplit(path) + if dName != "" { + parent, err = n.addDir(dName) + if err != nil { + return nil, err + } + } + switch subDirName { + case "", SelfDir: + return n, nil + case ParentDir: + if n.parent == nil { + return nil, fmt.Errorf( + "cannot add a directory above '%s'", n.Path()) + } + return n.parent, nil + default: + if !isLegalFileNameForCreation(subDirName) { + return nil, fmt.Errorf( + "illegal name '%s' in directory creation", subDirName) + } + result, ok := parent.dir[subDirName] + if ok { + if result.isNodeADir() { + // it's already there. + return result, nil + } + return nil, fmt.Errorf( + "cannot make dir '%s'; a file of that name already exists in '%s'", + subDirName, parent.Name()) + } + result = &fsNode{ + dir: make(map[string]*fsNode), + parent: parent, + } + parent.dir[subDirName] = result + return result, nil + } +} + +// Mkdir implements FileSystem. +// Mkdir creates a directory. +func (n *fsNode) Mkdir(path string) error { + _, err := n.AddDir(path) + return err +} + +// MkdirAll implements FileSystem. +// MkdirAll creates a directory. +func (n *fsNode) MkdirAll(path string) error { + _, err := n.AddDir(path) + return err +} + +// AddDir adds a directory to the node, not complaining +// if it is already there. +func (n *fsNode) AddDir(path string) (result *fsNode, err error) { + if n.dir == nil { + return nil, fmt.Errorf( + "cannot add a directory to file node '%s'", n.Name()) + } + return n.addDir(cleanQueryPath(path)) +} + +// CleanedAbs implements FileSystem. +func (n *fsNode) CleanedAbs(path string) (ConfirmedDir, string, error) { + node, err := n.Find(path) + if err != nil { + return "", "", errors.Wrap(err, "unable to clean") + } + if node == nil { + return "", "", fmt.Errorf("'%s' doesn't exist", path) + } + if node.isNodeADir() { + return ConfirmedDir(node.Path()), "", nil + } + return ConfirmedDir(node.parent.Path()), node.Name(), nil +} + +// Exists implements FileSystem. +// Exists returns true if the path exists. +func (n *fsNode) Exists(path string) bool { + if !n.isNodeADir() { + return n.Name() == path + } + result, err := n.Find(path) + if err != nil { + return false + } + return result != nil +} + +func cleanQueryPath(path string) string { + // Always ignore leading separator? + // Remember that filepath.Clean returns "." if + // given an empty string argument. + return filepath.Clean(StripLeadingSeps(path)) +} + +// Find finds the given node, else nil if not found. +// Return error on structural/argument errors. +func (n *fsNode) Find(path string) (*fsNode, error) { + if !n.isNodeADir() { + return nil, fmt.Errorf("can only find inside a dir") + } + if path == "" { + // Special case; check *before* cleaning and *before* + // comparison to nilParentName. + return nil, nil + } + if (n.parent == nil && path == n.nilParentName) || path == SelfDir { + // Special case + return n, nil + } + return n.findIt(cleanQueryPath(path)) +} + +func (n *fsNode) findIt(path string) (result *fsNode, err error) { + parent := n + dName, item := mySplit(path) + if dName != "" { + parent, err = n.findIt(dName) + if err != nil { + return nil, err + } + if parent == nil { + // all done, target doesn't exist. + return nil, nil + } + } + if !parent.isNodeADir() { + return nil, fmt.Errorf("'%s' is not a directory", parent.Path()) + } + return parent.dir[item], nil +} + +// RemoveAll implements FileSystem. +// RemoveAll removes an item and everything it contains. +func (n *fsNode) RemoveAll(path string) error { + result, err := n.Find(path) + if err != nil { + return err + } + if result == nil { + return fmt.Errorf("cannot find '%s' to remove it", path) + } + return result.Remove() +} + +// Remove drop the node, and everything it contains, from its parent. +func (n *fsNode) Remove() error { + if n.parent == nil { + return fmt.Errorf("cannot remove a root node") + } + if !n.parent.isNodeADir() { + log.Fatal("parent not a dir") + } + for key, value := range n.parent.dir { + if value == n { + delete(n.parent.dir, key) + return nil + } + } + log.Fatal("unable to find self in parent") + return nil +} + +// isNodeADir returns true if the node is a directory. +// Cannot collide with the poorly named "IsDir". +func (n *fsNode) isNodeADir() bool { + return n.dir != nil +} + +// IsDir implements FileSystem. +// IsDir returns true if the argument resolves +// to a directory rooted at the node. +func (n *fsNode) IsDir(path string) bool { + result, err := n.Find(path) + if err != nil || result == nil { + return false + } + return result.isNodeADir() +} + +// Size returns the size of the node. +func (n *fsNode) Size() int64 { + if n.isNodeADir() { + return int64(len(n.dir)) + } + return int64(len(n.content)) +} + +// Open implements FileSystem. +// Open opens the node for reading (just marks it). +func (n *fsNode) Open(path string) (File, error) { + result, err := n.Find(path) + if err != nil { + return nil, err + } + if result == nil { + return nil, fmt.Errorf("cannot find '%s' to open it", path) + } + result.open = true + return result, nil +} + +// Close marks the node closed. +func (n *fsNode) Close() error { + n.open = false + return nil +} + +// ReadFile implements FileSystem. +func (n *fsNode) ReadFile(path string) (c []byte, err error) { + result, err := n.Find(path) + if err != nil { + return nil, err + } + if result == nil { + return nil, fmt.Errorf("cannot find '%s' to read it", path) + } + c = make([]byte, len(result.content)) + _, err = result.Read(c) + return c, err +} + +// Read returns the content of the file node. +func (n *fsNode) Read(d []byte) (c int, err error) { + if n.isNodeADir() { + return 0, fmt.Errorf( + "cannot read content from non-file '%s'", n.Path()) + } + return copy(d, n.content), nil +} + +// Write saves the contents of the argument to the file node. +func (n *fsNode) Write(p []byte) (c int, err error) { + if n.isNodeADir() { + return 0, fmt.Errorf( + "cannot write content to non-file '%s'", n.Path()) + } + n.content = make([]byte, len(p)) + return copy(n.content, p), nil +} + +// ContentMatches returns true if v matches fake file's content. +func (n *fsNode) ContentMatches(v []byte) bool { + return bytes.Equal(v, n.content) +} + +// GetContent the content of a fake file. +func (n *fsNode) GetContent() []byte { + return n.content +} + +// Stat returns an instance of FileInfo. +func (n *fsNode) Stat() (os.FileInfo, error) { + return fileInfo{node: n}, nil +} + +// Walk implements FileSystem. +func (n *fsNode) Walk(path string, walkFn filepath.WalkFunc) error { + result, err := n.Find(path) + if err != nil { + return err + } + if result == nil { + return fmt.Errorf("cannot find '%s' to walk it", path) + } + return result.WalkMe(walkFn) +} + +// Walk runs the given walkFn on each node. +func (n *fsNode) WalkMe(walkFn filepath.WalkFunc) error { + fi, err := n.Stat() + // always visit self first + err = walkFn(n.Path(), fi, err) + if !n.isNodeADir() { + // it's a file, so nothing more to do + return err + } + // process self as a directory + if err == filepath.SkipDir { + return nil + } + // Walk is supposed to visit in lexical order. + for _, k := range n.sortedDirEntries() { + if err := n.dir[k].WalkMe(walkFn); err != nil { + if err == filepath.SkipDir { + // stop processing this directory + break + } + // bail out completely + return err + } + } + return nil +} + +func (n *fsNode) sortedDirEntries() []string { + keys := make([]string, len(n.dir)) + i := 0 + for k := range n.dir { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +// FileCount returns a count of files. +// Directories, empty or otherwise, not counted. +func (n *fsNode) FileCount() int { + count := 0 + n.WalkMe(func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + count++ + } + return nil + }) + return count +} + +func (n *fsNode) DebugPrint() { + n.WalkMe(func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("err '%v' at path %q\n", err, path) + return nil + } + if info.IsDir() { + if info.Size() == 0 { + fmt.Println("empty dir: " + path) + } + } else { + fmt.Println(" file: " + path) + } + return nil + }) +} + +var legalFileNamePattern = regexp.MustCompile("^[a-zA-Z0-9-_.]+$") + +// This rules enforced here should be simpler and tighter +// than what's allowed on a real OS. +// Should be fine for testing or in-memory purposes. +func isLegalFileNameForCreation(n string) bool { + if n == "" || n == SelfDir || !legalFileNamePattern.MatchString(n) { + return false + } + return !strings.Contains(n, ParentDir) +} + +// RegExpGlob returns a list of file paths matching the regexp. +// Excludes directories. +func (n *fsNode) RegExpGlob(pattern string) ([]string, error) { + var result []string + var expression = regexp.MustCompile(pattern) + n.WalkMe(func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + if expression.MatchString(path) { + result = append(result, path) + } + } + return nil + }) + sort.Strings(result) + return result, nil +} + +// Glob implements FileSystem. +// Glob returns the list of file paths matching +// per filepath.Match semantics, i.e. unlike RegExpGlob, +// Match("foo/a*") will not match sub-sub directories of foo. +// This is how /bin/ls behaves. +func (n *fsNode) Glob(pattern string) ([]string, error) { + var result []string + n.WalkMe(func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + match, err := filepath.Match(pattern, path) + if err != nil { + return err + } + if match { + result = append(result, path) + } + } + return nil + }) + sort.Strings(result) + return result, nil +} diff --git a/api/filesys/fsnode_test.go b/api/filesys/fsnode_test.go new file mode 100644 index 000000000..7446232cd --- /dev/null +++ b/api/filesys/fsnode_test.go @@ -0,0 +1,788 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesys + +import ( + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "testing" +) + +const content = ` +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +` +const shortContent = "hi" + +var topCases = []pathCase{ + { + what: "dotdot", + arg: ParentDir, + errStr: "illegal name '..' in file creation", + }, + { + what: "empty", + arg: "", + name: "", + errStr: "illegal name '.' in file creation", + }, + { + what: "simple", + arg: "bob", + name: "bob", + path: "bob", + }, + { + what: "longer", + arg: filepath.Join("longer", "bob"), + name: "bob", + path: filepath.Join("longer", "bob"), + }, + { + what: "longer yet", + arg: filepath.Join("longer", "foo", "bar", "beans", "bob"), + name: "bob", + path: filepath.Join("longer", "foo", "bar", "beans", "bob"), + }, + { + what: "tricky", + arg: filepath.Join("bob", ParentDir, "sally"), + name: "sally", + path: "sally", + }, + { + what: "trickier", + arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"), + name: "jean", + path: "jean", + }, +} + +func TestMakeEmptyDirInMemory(t *testing.T) { + n := MakeEmptyDirInMemory() + if !n.isNodeADir() { + t.Fatalf("not a directory") + } + if n.Size() != 0 { + t.Fatalf("unexpected size %d", n.Size()) + } + if n.Name() != "" { + t.Fatalf("unexpected name '%s'", n.Name()) + } + if n.Path() != "" { + t.Fatalf("unexpected path '%s'", n.Path()) + } + runBasicOperations( + t, "MakeEmptyDirInMemory", false, topCases, n) +} + +func TestMakeFsInMemory(t *testing.T) { + runBasicOperations( + t, "MakeFsInMemory", true, topCases, MakeFsInMemory()) +} + +//nolint:gocyclo +func runBasicOperations( + t *testing.T, tName string, isFSysRooted bool, + cases []pathCase, fSys FileSystem) { + buff := make([]byte, 500) + for _, c := range cases { + err := fSys.WriteFile(c.arg, []byte(content)) + if c.errStr != "" { + if err == nil { + t.Fatalf("%s; expected error writing to '%s'!", c.what, c.arg) + } + if !strings.Contains(err.Error(), c.errStr) { + t.Fatalf("%s; expected err containing '%s', got '%v'", + c.what, c.errStr, err) + } + continue + } + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if !fSys.Exists(c.path) { + t.Fatalf("%s; expect existence of '%s'", c.what, c.path) + } + stuff, err := fSys.ReadFile(c.path) + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if string(stuff) != content { + t.Fatalf("%s; unexpected content '%s'", c.what, stuff) + } + f, err := fSys.Open(c.arg) + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + fi, err := f.Stat() + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if fi.Name() != c.name { + t.Fatalf("%s; expected name '%s', got '%s'", c.what, c.name, fi.Name()) + } + count, err := f.Read(buff) + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if string(buff[:count]) != content { + t.Fatalf("%s; unexpected buff '%s'", c.what, buff) + } + count, err = f.Write([]byte(shortContent)) + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if count != len(shortContent) { + t.Fatalf("%s; unexpected count: %d", c.what, len(shortContent)) + } + stuff, err = fSys.ReadFile(c.path) + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + if string(stuff) != shortContent { + t.Fatalf("%s; unexpected content '%s'", c.what, stuff) + } + } + + var actualPaths []string + var err error + prefix := "" + { + root := SelfDir + if isFSysRooted { + root = Separator + prefix = Separator + } + err = fSys.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("err '%v' at path %q\n", err, path) + return nil + } + if !info.IsDir() { + actualPaths = append(actualPaths, path) + } + return nil + }) + } + if err != nil { + t.Fatalf("unexpected error %v", err) + } + var expectedPaths []string + for _, c := range cases { + if c.errStr == "" { + expectedPaths = append(expectedPaths, prefix+c.path) + } + } + sort.Strings(expectedPaths) + assertEqualStringSlices(t, expectedPaths, actualPaths, tName) +} + +type pathCase struct { + what string + arg string + name string + path string + errStr string +} + +func TestAddDir(t *testing.T) { + cases := []pathCase{ + { + what: "dotdot", + arg: ParentDir, + errStr: "cannot add a directory above ''", + }, + { + what: "empty", + arg: "", + name: "", + path: "", + }, + { + what: "simple", + arg: "bob", + name: "bob", + path: "bob", + }, + { + what: "longer", + arg: filepath.Join("longer", "bob"), + name: "bob", + path: filepath.Join("longer", "bob"), + }, + { + what: "longer yet", + arg: filepath.Join("longer", "foo", "bar", "beans", "bob"), + name: "bob", + path: filepath.Join("longer", "foo", "bar", "beans", "bob"), + }, + { + what: "tricky", + arg: filepath.Join("bob", ParentDir, "sally"), + name: "sally", + path: "sally", + }, + { + what: "trickier", + arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"), + name: "jean", + path: "jean", + }, + } + for _, c := range cases { + n := MakeEmptyDirInMemory() + f, err := n.AddDir(c.arg) + if c.errStr != "" { + if err == nil { + t.Fatalf("%s; expected error!", c.what) + } + if !strings.Contains(err.Error(), c.errStr) { + t.Fatalf( + "%s; expected error with '%s', got '%v'", + c.what, c.errStr, err) + } + continue + } + if err != nil { + t.Fatalf("%s; unexpected error: %v", c.what, err) + } + checkNode(t, c.what, f, c.name, 0, true, c.path) + checkOsStat(t, c.what, f, f.Name(), 0, true) + } +} + +var bagOfCases = []pathCase{ + { + what: "empty", + arg: "", + errStr: "illegal name '.' in file creation", + }, + { + what: "simple", + arg: "bob", + name: "bob", + path: "bob", + }, + { + what: "longer", + arg: filepath.Join("longer", "bob"), + name: "bob", + path: filepath.Join("longer", "bob"), + }, + { + what: "longer", + arg: filepath.Join("longer", "sally"), + name: "sally", + path: filepath.Join("longer", "sally"), + }, + { + what: "even longer", + arg: filepath.Join("longer", "than", "the", "other", "bob"), + name: "bob", + path: filepath.Join("longer", "than", "the", "other", "bob"), + }, + { + what: "even longer", + arg: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"), + name: "bob", + path: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"), + }, +} + +func TestAddFile(t *testing.T) { + n := MakeEmptyDirInMemory() + if n.FileCount() != 0 { + t.Fatalf("expected no files, got %d", n.FileCount()) + } + expectedFileCount := 0 + for _, c := range bagOfCases { + f, err := n.AddFile(c.arg, []byte(content)) + if c.errStr != "" { + if err == nil { + t.Fatalf("%s; expected error!", c.what) + } + if !strings.Contains(err.Error(), c.errStr) { + t.Fatalf("%s; expected err containing '%s', got '%v'", + c.what, c.errStr, err) + } + continue + } + if err != nil { + t.Fatalf("%s; unexpected error %v", c.what, err) + } + checkNode(t, c.what, f, c.name, len(content), false, c.path) + checkOsStat(t, c.what, f, f.Name(), len(content), false) + + result, err := n.Find(c.arg) + if err != nil { + t.Fatalf("%s; unexpected find error %v", c.what, err) + } + if result != f { + t.Fatalf("%s; unexpected find result %v", c.what, result) + } + + result, err = n.Find(filepath.Join("longer", "bogus")) + if err != nil { + t.Fatalf("%s; unexpected find error %v", c.what, err) + } + if result != nil { + t.Fatalf("%s; unexpected find result %v", c.what, result) + } + + expectedFileCount++ + fc := n.FileCount() + if fc != expectedFileCount { + t.Fatalf("expected file count %d, got %d", + expectedFileCount, fc) + } + } +} + +func checkNode( + t *testing.T, what string, f *fsNode, name string, + size int, isDir bool, path string) { + if f.isNodeADir() != isDir { + t.Fatalf("%s; unexpected isNodeADir = %v", what, f.isNodeADir()) + } + if f.Size() != int64(size) { + t.Fatalf("%s; unexpected size %d", what, f.Size()) + } + if name != f.Name() { + t.Fatalf("%s; expected name '%s', got '%s'", what, name, f.Name()) + } + if path != f.Path() { + t.Fatalf("%s; expected path '%s', got '%s'", what, path, f.Path()) + } +} + +func checkOsStat( + t *testing.T, what string, f File, name string, + size int, isDir bool) { + info, err := f.Stat() + if err != nil { + t.Fatalf("%s; unexpected stat error %v", what, err) + } + if info.IsDir() != isDir { + t.Fatalf("%s; unexpected info.isNodeADir = %v", what, info.IsDir()) + } + if info.Size() != int64(size) { + t.Fatalf("%s; unexpected info.size %d", what, info.Size()) + } + if info.Name() != name { + t.Fatalf("%s; expected name '%s', got info.Name '%s'", what, name, info.Name()) + } +} + +var bunchOfFiles = []struct { + path string + addAsDir bool +}{ + { + path: filepath.Join("b", "e", "a", "c", "g"), + }, + { + path: filepath.Join("z", "r", "a", "b", "g"), + }, + { + path: filepath.Join("b", "q", "a", "c", "g"), + }, + { + path: filepath.Join("b", "a", "a", "m", "g"), + addAsDir: true, + }, + { + path: filepath.Join("b", "w"), + }, + { + path: filepath.Join("b", "d", "a", "c", "m"), + }, + { + path: filepath.Join("b", "d", "z"), + }, + { + path: filepath.Join("b", "d", "y"), + }, + { + path: filepath.Join("b", "d", "ignore", "c", "n"), + }, + { + path: filepath.Join("b", "d", "x"), + }, + { + path: filepath.Join("b", "d", "ignore", "c", "o"), + }, + { + path: filepath.Join("b", "d", "ignore", "c", "m"), + }, + { + path: filepath.Join("b", "d", "a", "c", "i"), + addAsDir: true, + }, + { + path: filepath.Join("x"), + }, + { + path: filepath.Join("y"), + }, + { + path: filepath.Join("b", "d", "a", "c", "i", "beans"), + }, + { + path: filepath.Join("b", "d", "a", "c", "r", "w"), + addAsDir: true, + }, + { + path: filepath.Join("b", "d", "a", "c", "u"), + }, +} + +func makeLoadedFileTree(t *testing.T) *fsNode { + n := MakeEmptyDirInMemory() + var err error + expectedFileCount := 0 + for _, item := range bunchOfFiles { + if item.addAsDir { + _, err = n.AddDir(item.path) + } else { + _, err = n.AddFile(item.path, []byte(content)) + expectedFileCount++ + } + if err != nil { + t.Fatalf("unexpected error %v", err) + } + } + fc := n.FileCount() + if fc != expectedFileCount { + t.Fatalf("expected file count %d, got %d", + expectedFileCount, fc) + } + return n +} + +func TestWalkMe(t *testing.T) { + n := makeLoadedFileTree(t) + var actualPaths []string + err := n.WalkMe(func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("err '%v' at path %q\n", err, path) + return nil + } + if info.IsDir() { + if info.Name() == "ignore" { + return filepath.SkipDir + } + } else { + actualPaths = append(actualPaths, path) + } + return nil + }) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + var expectedPaths []string + for _, c := range bunchOfFiles { + if !c.addAsDir && !strings.Contains(c.path, "ignore") { + expectedPaths = append(expectedPaths, c.path) + } + } + sort.Strings(expectedPaths) + assertEqualStringSlices(t, expectedPaths, actualPaths, "testWalkMe") +} + +func TestRemove(t *testing.T) { + n := makeLoadedFileTree(t) + orgCount := n.FileCount() + + // Remove the "ignore" directory and everything below it. + path := filepath.Join("b", "d", "ignore") + result, err := n.Find(path) + if err != nil { + t.Fatalf("%s; unexpected error %v", path, err) + } + if result == nil { + t.Fatalf("%s; expected to find '%s'", path, path) + } + if !result.isNodeADir() { + t.Fatalf("%s; expected to find a directory", path) + } + err = result.Remove() + if err != nil { + t.Fatalf("%s; unable to remove: %v", path, err) + } + result, err = n.Find(path) + if err != nil { + // Just because it's gone doesn't mean error. + t.Fatalf("%s; unexpected error %v", path, err) + } + if result != nil { + t.Fatalf("%s; should not have been able to find '%s'", path, path) + } + + // There were three files below "ignore". + orgCount -= 3 + + // Now drop one more for a total of four dropped. + result, _ = n.Find(filepath.Join("y")) + err = result.Remove() + if err != nil { + t.Fatalf("%s; unable to remove: %v", path, err) + } + orgCount -= 1 + + fc := n.FileCount() + if fc != orgCount { + t.Fatalf("expected file count %d, got %d", + orgCount, fc) + } +} + +func TestExists(t *testing.T) { + n := makeLoadedFileTree(t) + path := filepath.Join("b", "d", "a") + if !n.Exists(path) { + t.Fatalf("expected existence at %s", path) + } + if !n.IsDir(path) { + t.Fatalf("expected directory at %s", path) + } +} + +func TestRegExpGlob(t *testing.T) { + n := makeLoadedFileTree(t) + expected := []string{ + filepath.Join("b", "d", "a", "c", "i", "beans"), + filepath.Join("b", "d", "a", "c", "m"), + filepath.Join("b", "d", "a", "c", "u"), + filepath.Join("b", "d", "ignore", "c", "m"), + filepath.Join("b", "d", "ignore", "c", "n"), + filepath.Join("b", "d", "ignore", "c", "o"), + filepath.Join("b", "d", "x"), + filepath.Join("b", "d", "y"), + filepath.Join("b", "d", "z"), + } + paths, err := n.RegExpGlob("b/d/*") + if err != nil { + t.Fatalf("glob error: %v", err) + } + assertEqualStringSlices(t, expected, paths, "glob test") +} + +func TestGlob(t *testing.T) { + n := makeLoadedFileTree(t) + expected := []string{ + filepath.Join("b", "d", "x"), + filepath.Join("b", "d", "y"), + filepath.Join("b", "d", "z"), + } + paths, err := n.Glob("b/d/*") + if err != nil { + t.Fatalf("glob error: %v", err) + } + assertEqualStringSlices(t, expected, paths, "glob test") +} + +func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) { + if len(expected) != len(actual) { + t.Fatalf( + "%s; unequal sizes; len(expected)=%d, len(actual)=%d\n%+v\n%+v\n", + message, len(expected), len(actual), expected, actual) + } + for i := range expected { + if expected[i] != actual[i] { + t.Fatalf( + "%s; unequal entries; expected=%s, actual=%s", + message, expected[i], actual[i]) + } + } +} + +func TestFind(t *testing.T) { + cases := []struct { + what string + arg string + expectDir bool + expectFile bool + errStr string + }{ + { + what: "garbage", + arg: "///1(*&SA", + }, + { + what: "simple", + arg: "bob", + }, + { + what: "no directory", + arg: filepath.Join("b", "rrrrrr"), + }, + { + what: "is a directory", + arg: filepath.Join("b", "d", "ignore"), + expectDir: true, + }, + { + what: "longer, ending in file", + arg: filepath.Join("b", "d", "x"), + expectFile: true, + }, + { + what: "moar longer, ending in file", + arg: filepath.Join("b", "d", "a", "c", "u"), + expectFile: true, + }, + { + what: "directory", + arg: filepath.Join("b"), + expectDir: true, + }, + { + // Querying for the empty string could + // 1) be an error, + // 2) return no result (and no error) as with + // any illegal and therefore non-existent + // file name, + // 3) return the node itself, like running + // 'ls' with no argument. + // Going with option 2 (no result, no error), + // since at this low level it makes more sense + // if the results for the empty string query + // differ from the results for the "." query. + what: "empty name", + arg: "", + }, + { + what: "self dir", + arg: SelfDir, + expectDir: true, + }, + { + what: "parent dir - doesn't exist", + arg: ParentDir, + }, + { + what: "many parents - doesn't exist", + arg: filepath.Join(ParentDir, ParentDir, ParentDir), + }, + } + + n := makeLoadedFileTree(t) + for _, item := range cases { + result, err := n.Find(item.arg) + if item.errStr != "" { + if err == nil { + t.Fatalf("%s; expected error", item.what) + } + if !strings.Contains(err.Error(), item.errStr) { + t.Fatalf("%s; expected err containing '%s', got '%v'", + item.what, item.errStr, err) + } + continue + } + if err != nil { + t.Fatalf("%s; unexpected error: %v", item.what, err) + } + if result == nil { + if item.expectDir { + t.Fatalf( + "%s; expected to find directory '%s'", item.what, item.arg) + } + if item.expectFile { + t.Fatalf( + "%s; expected to find file '%s'", item.what, item.arg) + } + continue + } + if item.expectDir { + if !result.isNodeADir() { + t.Fatalf( + "%s; expected '%s' to be a directory", item.what, item.arg) + } + continue + } + if item.expectFile { + if result.isNodeADir() { + t.Fatalf("%s; expected '%s' to be a file", item.what, item.arg) + } + continue + } + t.Fatalf( + "%s; expected nothing for '%s', but got '%s'", + item.what, item.arg, result.Path()) + } +} + +func TestCleanedAbs(t *testing.T) { + cases := []struct { + what string + full string + cDir string + name string + errStr string + }{ + { + what: "empty", + full: "", + errStr: "doesn't exist", + }, + { + what: "simple", + full: "bob", + errStr: "'bob' doesn't exist", + }, + { + what: "no directory", + full: filepath.Join("b", "rrrrrr"), + errStr: "'b/rrrrrr' doesn't exist", + }, + { + what: "longer, ending in file", + full: filepath.Join("b", "d", "x"), + cDir: filepath.Join("b", "d"), + name: "x", + }, + { + what: "moar longer, ending in file", + full: filepath.Join("b", "d", "a", "c", "u"), + cDir: filepath.Join("b", "d", "a", "c"), + name: "u", + }, + { + what: "directory", + full: filepath.Join("b", "d"), + cDir: filepath.Join("b", "d"), + name: "", + }, + } + + n := makeLoadedFileTree(t) + for _, item := range cases { + cDir, name, err := n.CleanedAbs(item.full) + if item.errStr != "" { + if err == nil { + t.Fatalf("%s; expected error", item.what) + } + if !strings.Contains(err.Error(), item.errStr) { + t.Fatalf("%s; expected err containing '%s', got '%v'", + item.what, item.errStr, err) + } + continue + } + if err != nil { + t.Fatalf("%s; unexpected error: %v", item.what, err) + } + if cDir != ConfirmedDir(item.cDir) { + t.Fatalf("%s; expected cDir=%s, got '%s'", item.what, item.cDir, cDir) + } + if name != item.name { + t.Fatalf("%s; expected name=%s, got '%s'", item.what, item.name, name) + } + } +} diff --git a/api/krusty/kustomizer_test.go b/api/krusty/kustomizer_test.go index 35f5cea50..8c88732c4 100644 --- a/api/krusty/kustomizer_test.go +++ b/api/krusty/kustomizer_test.go @@ -11,14 +11,19 @@ import ( "sigs.k8s.io/kustomize/api/krusty" ) -func TestSomething1(t *testing.T) { +// TODO: move most of the tests in api/internal/target +// to this package, as they are all high level tests and +// examples appropriate to this level and package. +// The following test isn't much more than a usage example; +// everything is actually tested down in api/internal/target. +func TestSomething(t *testing.T) { fSys := filesys.MakeFsInMemory() b := krusty.MakeKustomizer(fSys, krusty.MakeDefaultOptions()) - _, err := b.Run("hey") + _, err := b.Run("noSuchThing") if err == nil { t.Fatalf("expected error") } - if err.Error() != "got file 'hey', but 'hey' must be a directory to be a root" { + if err.Error() != "'noSuchThing' doesn't exist" { t.Fatalf("unexpected error: %v", err) } } diff --git a/api/loader/fileloader.go b/api/loader/fileloader.go index bca41401d..0822d0c84 100644 --- a/api/loader/fileloader.go +++ b/api/loader/fileloader.go @@ -148,8 +148,7 @@ func demandDirectoryRoot( } d, f, err := fSys.CleanedAbs(path) if err != nil { - return "", fmt.Errorf( - "absolute path error in '%s' : %v", path, err) + return "", err } if f != "" { return "", fmt.Errorf( diff --git a/api/loader/fileloader_test.go b/api/loader/fileloader_test.go index fa0875102..41d01d711 100644 --- a/api/loader/fileloader_test.go +++ b/api/loader/fileloader_test.go @@ -45,7 +45,7 @@ var testCases = []testData{ func MakeFakeFs(td []testData) filesys.FileSystem { fSys := filesys.MakeFsInMemory() for _, x := range td { - fSys.WriteFile("/"+x.path, []byte(x.expectedContent)) + fSys.WriteFile(x.path, []byte(x.expectedContent)) } return fSys } diff --git a/kustomize/internal/commands/create/create_test.go b/kustomize/internal/commands/create/create_test.go index e55992c2b..2489255a3 100644 --- a/kustomize/internal/commands/create/create_test.go +++ b/kustomize/internal/commands/create/create_test.go @@ -37,7 +37,7 @@ func TestCreateNoArgs(t *testing.T) { } func TestCreateWithResources(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.WriteFile("foo.yaml", []byte("")) fSys.WriteFile("bar.yaml", []byte("")) opts := createFlags{resources: "foo.yaml,bar.yaml"} diff --git a/kustomize/internal/commands/edit/add/addpatch_test.go b/kustomize/internal/commands/edit/add/addpatch_test.go index b2666b027..1a5994c1d 100644 --- a/kustomize/internal/commands/edit/add/addpatch_test.go +++ b/kustomize/internal/commands/edit/add/addpatch_test.go @@ -20,7 +20,7 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ) func TestAddPatchHappyPath(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.WriteFile(patchFileName, []byte(patchFileContent)) fSys.WriteFile(patchFileName+"another", []byte(patchFileContent)) testutils_test.WriteTestKustomization(fSys) @@ -44,7 +44,7 @@ func TestAddPatchHappyPath(t *testing.T) { } func TestAddPatchAlreadyThere(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.WriteFile(patchFileName, []byte(patchFileContent)) testutils_test.WriteTestKustomization(fSys) @@ -63,7 +63,7 @@ func TestAddPatchAlreadyThere(t *testing.T) { } func TestAddPatchNoArgs(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() cmd := newCmdAddPatch(fSys) err := cmd.Execute() diff --git a/kustomize/internal/commands/edit/add/addresource_test.go b/kustomize/internal/commands/edit/add/addresource_test.go index d7f78cb5a..3b1afc674 100644 --- a/kustomize/internal/commands/edit/add/addresource_test.go +++ b/kustomize/internal/commands/edit/add/addresource_test.go @@ -20,7 +20,7 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ) func TestAddResourceHappyPath(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.WriteFile(resourceFileName, []byte(resourceFileContent)) fSys.WriteFile(resourceFileName+"another", []byte(resourceFileContent)) testutils_test.WriteTestKustomization(fSys) diff --git a/kustomize/internal/commands/edit/add/flagsandargs_test.go b/kustomize/internal/commands/edit/add/flagsandargs_test.go index c49365b28..80b4f160b 100644 --- a/kustomize/internal/commands/edit/add/flagsandargs_test.go +++ b/kustomize/internal/commands/edit/add/flagsandargs_test.go @@ -73,7 +73,7 @@ func TestDataConfigValidation_Flags(t *testing.T) { } func TestExpandFileSource(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.Create("dir/fa1") fSys.Create("dir/fa2") fSys.Create("dir/readme") @@ -91,7 +91,7 @@ func TestExpandFileSource(t *testing.T) { } func TestExpandFileSourceWithKey(t *testing.T) { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() fSys.Create("dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc") fSys.Create("dir/foobar") fSys.Create("dir/simplebar") diff --git a/kustomize/internal/commands/edit/remove/removepatch_test.go b/kustomize/internal/commands/edit/remove/removepatch_test.go index 8a29d3b97..2df4ae2c3 100644 --- a/kustomize/internal/commands/edit/remove/removepatch_test.go +++ b/kustomize/internal/commands/edit/remove/removepatch_test.go @@ -21,7 +21,7 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ) func makeKustomizationPatchFS() filesys.FileSystem { - fSys := filesys.MakeFsInMemory() + fSys := filesys.MakeEmptyDirInMemory() patches := []string{"patch1.yaml", "patch2.yaml"} testutils_test.WriteTestKustomizationWith(fSys, []byte(