merge upstream master

This commit is contained in:
koba1t
2022-07-02 15:18:26 +09:00
51 changed files with 419 additions and 145 deletions

View File

@@ -4,7 +4,10 @@
package filesys
import (
"fmt"
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/errors"
)
const (
@@ -16,40 +19,70 @@ const (
// FileSystem groups basic os filesystem methods.
// It's supposed be functional subset of https://golang.org/pkg/os
type FileSystem interface {
// Create a file.
Create(path string) (File, error)
// MkDir makes a directory.
Mkdir(path string) error
// MkDirAll makes a directory path, creating intervening directories.
MkdirAll(path string) error
// RemoveAll removes path and any children it contains.
RemoveAll(path string) error
// Open opens the named file for reading.
Open(path string) (File, error)
// IsDir returns true if the path is a directory.
IsDir(path string) bool
// ReadDir returns a list of files and directories within a directory.
ReadDir(path string) ([]string, error)
// CleanedAbs converts the given path into a
// directory and a file name, where the directory
// is represented as a ConfirmedDir and all that implies.
// If the entire path is a directory, the file component
// is an empty string.
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,
// 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)
// WriteFile writes the data to a file at the given path,
// overwriting anything that's already there.
WriteFile(path string, data []byte) error
// Walk walks the file system with the given WalkFunc.
Walk(path string, walkFn filepath.WalkFunc) error
}
// ConfirmDir returns an error if the user-specified path is not an existing directory on fSys.
// Otherwise, ConfirmDir returns path, which can be relative, as a ConfirmedDir and all that implies.
func ConfirmDir(fSys FileSystem, path string) (ConfirmedDir, error) {
if path == "" {
return "", errors.Errorf("directory path cannot be empty")
}
d, f, err := fSys.CleanedAbs(path)
if err != nil {
return "", errors.WrapPrefixf(err, "not a valid directory")
}
if f != "" {
return "", errors.WrapPrefixf(errors.Errorf("file is not directory"), fmt.Sprintf("'%s'", path))
}
return d, nil
}
// FileSystemOrOnDisk satisfies the FileSystem interface by forwarding
// all of its method calls to the given FileSystem whenever it's not nil.
// If it's nil, the call is forwarded to the OS's underlying file system.

View File

@@ -5,16 +5,26 @@ package filesys
import (
"os"
"path/filepath"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
disk = "MakeFsOnDisk"
memoryAbs = "MakeFsInMemory"
memoryEmpty = "MakeEmptyDirInMemory"
existMsg = "expected '%s' to exist"
)
var filesysBuilders = map[string]func() FileSystem{
"MakeFsInMemory": MakeFsInMemory,
"MakeFsOnDisk": MakeFsOnDisk,
"MakeEmptyDirInMemory": func() FileSystem { return MakeEmptyDirInMemory() },
memoryAbs: MakeFsInMemory,
disk: MakeFsOnDisk,
memoryEmpty: func() FileSystem { return MakeEmptyDirInMemory() },
}
func TestNotExistErr(t *testing.T) {
@@ -42,3 +52,89 @@ func testNotExistErr(t *testing.T, fs FileSystem) {
err = fs.Walk(path, func(_ string, _ os.FileInfo, err error) error { return err })
assert.Truef(t, errors.Is(err, os.ErrNotExist), "Walk should return ErrNotExist, got %v", err)
}
// setupFileSys returns file system, default directory to test in, and working directory
func setupFileSys(t *testing.T, name string) (FileSystem, string, string) {
t.Helper()
switch name {
case disk:
fSys, testDir := makeTestDir(t)
return fSys, testDir, cleanWd(t)
case memoryAbs:
return filesysBuilders[name](), Separator, Separator
case memoryEmpty:
return filesysBuilders[name](), "", ""
default:
t.Fatalf("unexpected FileSystem implementation '%s'", name)
panic("unreachable point of execution")
}
}
func TestConfirmDir(t *testing.T) {
for name := range filesysBuilders {
name := name
t.Run(name, func(t *testing.T) {
fSys, prefixPath, wd := setupFileSys(t, name)
d1Path := filepath.Join(prefixPath, "d1")
d2Path := filepath.Join(d1Path, ".d2")
err := fSys.MkdirAll(d2Path)
require.NoError(t, err)
require.Truef(t, fSys.Exists(d2Path), existMsg, d2Path)
tests := map[string]*struct {
dir string
expected string
}{
"Simple": {
d1Path,
d1Path,
},
"Hidden": {
d2Path,
d2Path,
},
"Relative": {
SelfDir,
wd,
},
}
for subName, test := range tests {
test := test
t.Run(subName, func(t *testing.T) {
cleanDir, err := ConfirmDir(fSys, test.dir)
require.NoError(t, err)
require.Equal(t, test.expected, cleanDir.String())
})
}
})
}
}
func TestConfirmDirErr(t *testing.T) {
for name := range filesysBuilders {
thisName := name
t.Run(thisName, func(t *testing.T) {
fSys, prefixPath, _ := setupFileSys(t, thisName)
fPath := filepath.Join(prefixPath, "foo")
err := fSys.WriteFile(fPath, []byte(`foo`))
require.NoError(t, err)
require.Truef(t, fSys.Exists(fPath), existMsg, fPath)
tests := map[string]string{
"Empty": "",
"File": fPath,
"Non-existent": filepath.Join(prefixPath, "bar"),
}
for subName, invalidPath := range tests {
invalidPath := invalidPath
t.Run(subName, func(t *testing.T) {
_, err := ConfirmDir(fSys, invalidPath)
require.Error(t, err)
})
}
})
}
}

View File

@@ -14,7 +14,7 @@ import (
"sort"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/kyaml/errors"
)
var _ File = &fsNode{}
@@ -232,7 +232,7 @@ func (n *fsNode) AddDir(path string) (result *fsNode, err error) {
func (n *fsNode) CleanedAbs(path string) (ConfirmedDir, string, error) {
node, err := n.Find(path)
if err != nil {
return "", "", errors.Wrap(err, "unable to clean")
return "", "", errors.WrapPrefixf(err, "unable to clean")
}
if node == nil {
return "", "", notExistError(path)

View File

@@ -16,6 +16,8 @@ import (
"sort"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
const content = `
@@ -840,6 +842,32 @@ func TestCleanedAbs(t *testing.T) {
}
}
func TestConfirmDirMemRoot(t *testing.T) {
fSys := MakeFsInMemory()
actual, err := ConfirmDir(fSys, Separator)
require.NoError(t, err)
require.Equal(t, Separator, actual.String())
}
func TestConfirmDirRelativeNode(t *testing.T) {
req := require.New(t)
fSysEmpty := MakeEmptyDirInMemory()
fSysRoot, err := fSysEmpty.AddDir("a")
req.NoError(err)
fSysSub, err := fSysRoot.AddDir("b")
req.NoError(err)
err = fSysSub.Mkdir("c")
req.NoError(err)
expected := filepath.Join("a", "b", "c")
req.Truef(fSysEmpty.Exists(expected), existMsg, expected)
actual, err := ConfirmDir(fSysSub, "c")
req.NoError(err)
req.Equal(expected, actual.String())
}
func TestFileOps(t *testing.T) {
const path = "foo.txt"
content := strings.Repeat("longest content", 100)

View File

@@ -1,137 +1,164 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows
package filesys
import (
"os"
"path"
"path/filepath"
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/require"
)
const dirMsg = "expected '%s' to be a dir"
func makeTestDir(t *testing.T) (FileSystem, string) {
t.Helper()
req := require.New(t)
fSys := MakeFsOnDisk()
td := t.TempDir()
testDir, err := filepath.EvalSymlinks(td)
if err != nil {
t.Fatalf("unexpected error %s", err)
}
if !fSys.Exists(testDir) {
t.Fatalf("expected existence")
}
if !fSys.IsDir(testDir) {
t.Fatalf("expected directory")
}
req.NoError(err)
req.Truef(fSys.Exists(testDir), existMsg, testDir)
req.Truef(fSys.IsDir(testDir), dirMsg, testDir)
return fSys, testDir
}
func cleanWd(t *testing.T) string {
t.Helper()
wd, err := os.Getwd()
require.NoError(t, err)
cleanedWd, err := filepath.EvalSymlinks(wd)
require.NoError(t, err)
return cleanedWd
}
func TestCleanedAbs_1(t *testing.T) {
req := require.New(t)
fSys, _ := makeTestDir(t)
d, f, err := fSys.CleanedAbs("")
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
wd, err := os.Getwd()
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
if d.String() != wd {
t.Fatalf("unexpected d=%s", d)
}
if f != "" {
t.Fatalf("unexpected f=%s", f)
}
req.NoError(err)
wd := cleanWd(t)
req.Equal(wd, d.String())
req.Empty(f)
}
func TestCleanedAbs_2(t *testing.T) {
req := require.New(t)
fSys, _ := makeTestDir(t)
d, f, err := fSys.CleanedAbs("/")
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
if d != "/" {
t.Fatalf("unexpected d=%s", d)
}
if f != "" {
t.Fatalf("unexpected f=%s", f)
}
root := getOSRoot(t)
d, f, err := fSys.CleanedAbs(root)
req.NoError(err)
req.Equal(root, d.String())
req.Empty(f)
}
func TestCleanedAbs_3(t *testing.T) {
req := require.New(t)
fSys, testDir := makeTestDir(t)
err := fSys.WriteFile(
filepath.Join(testDir, "foo"), []byte(`foo`))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
req.NoError(err)
d, f, err := fSys.CleanedAbs(filepath.Join(testDir, "foo"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
if d.String() != testDir {
t.Fatalf("unexpected d=%s", d)
}
if f != "foo" {
t.Fatalf("unexpected f=%s", f)
}
req.NoError(err)
req.Equal(testDir, d.String())
req.Equal("foo", f)
}
func TestCleanedAbs_4(t *testing.T) {
req := require.New(t)
fSys, testDir := makeTestDir(t)
err := fSys.MkdirAll(filepath.Join(testDir, "d1", "d2"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
req.NoError(err)
err = fSys.WriteFile(
filepath.Join(testDir, "d1", "d2", "bar"),
[]byte(`bar`))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
req.NoError(err)
d, f, err := fSys.CleanedAbs(
filepath.Join(testDir, "d1", "d2"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
if d.String() != filepath.Join(testDir, "d1", "d2") {
t.Fatalf("unexpected d=%s", d)
}
if f != "" {
t.Fatalf("unexpected f=%s", f)
}
req.NoError(err)
req.Equal(filepath.Join(testDir, "d1", "d2"), d.String())
req.Empty(f)
d, f, err = fSys.CleanedAbs(
filepath.Join(testDir, "d1", "d2", "bar"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
req.NoError(err)
req.Equal(filepath.Join(testDir, "d1", "d2"), d.String())
req.Equal("bar", f)
}
func TestConfirmDirDisk(t *testing.T) {
req := require.New(t)
fSys, testDir := makeTestDir(t)
wd := cleanWd(t)
relDir := "actual_foo_431432"
err := fSys.Mkdir(relDir)
t.Cleanup(func() {
err := fSys.RemoveAll(relDir)
req.NoError(err)
})
req.NoError(err)
req.Truef(fSys.Exists(relDir), existMsg, relDir)
linkDir := filepath.Join(testDir, "pointer")
err = os.Symlink(filepath.Join(wd, relDir), linkDir)
req.NoError(err)
root := getOSRoot(t)
tests := map[string]*struct {
path string
expected string
}{
"root": {
root,
root,
},
"non-selfdir relative path": {
relDir,
filepath.Join(wd, relDir),
},
"symlink": {
linkDir,
filepath.Join(wd, relDir),
},
}
if d.String() != filepath.Join(testDir, "d1", "d2") {
t.Fatalf("unexpected d=%s", d)
}
if f != "bar" {
t.Fatalf("unexpected f=%s", f)
for name, test := range tests {
test := test
t.Run(name, func(t *testing.T) {
actualPath, err := ConfirmDir(fSys, test.path)
require.NoError(t, err)
require.Equal(t, test.expected, actualPath.String())
})
}
}
func TestReadFilesRealFS(t *testing.T) {
req := require.New(t)
fSys, testDir := makeTestDir(t)
dir := path.Join(testDir, "dir")
nestedDir := path.Join(dir, "nestedDir")
hiddenDir := path.Join(testDir, ".hiddenDir")
dir := filepath.Join(testDir, "dir")
nestedDir := filepath.Join(dir, "nestedDir")
hiddenDir := filepath.Join(testDir, ".hiddenDir")
dirs := []string{
testDir,
dir,
@@ -149,27 +176,19 @@ func TestReadFilesRealFS(t *testing.T) {
}
err := fSys.MkdirAll(nestedDir)
if err != nil {
t.Fatalf("Unexpected Error %v\n", err)
}
req.NoError(err)
err = fSys.MkdirAll(hiddenDir)
if err != nil {
t.Fatalf("Unexpected Error %v\n", err)
}
req.NoError(err)
// adding all files in every directory that we had defined
for _, d := range dirs {
if !fSys.IsDir(d) {
t.Fatalf("Expected %s to be a dir\n", d)
}
req.Truef(fSys.IsDir(d), dirMsg, d)
for _, f := range files {
err = fSys.WriteFile(path.Join(d, f), []byte(f))
if err != nil {
t.Fatalf("unexpected error %s", err)
}
if !fSys.Exists(path.Join(d, f)) {
t.Fatalf("expected %s", f)
}
fPath := filepath.Join(d, f)
err = fSys.WriteFile(fPath, []byte(f))
req.NoError(err)
req.Truef(fSys.Exists(fPath), existMsg, fPath)
}
}
@@ -221,15 +240,14 @@ func TestReadFilesRealFS(t *testing.T) {
for _, d := range dirs {
var expectedPaths []string
for _, f := range c.expectedFiles {
expectedPaths = append(expectedPaths, path.Join(d, f))
expectedPaths = append(expectedPaths, filepath.Join(d, f))
}
if c.expectedDirs != nil {
expectedPaths = append(expectedPaths, c.expectedDirs[d]...)
}
actualPaths, globErr := fSys.Glob(path.Join(d, c.globPattern))
if globErr != nil {
t.Fatalf("Unexpected Error : %v\n", globErr)
}
actualPaths, globErr := fSys.Glob(filepath.Join(d, c.globPattern))
require.NoError(t, globErr)
sort.Strings(actualPaths)
sort.Strings(expectedPaths)
if !reflect.DeepEqual(actualPaths, expectedPaths) {

View File

@@ -0,0 +1,17 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:build !windows
// +build !windows
package filesys
import (
"path/filepath"
"testing"
)
func getOSRoot(t *testing.T) string {
t.Helper()
return string(filepath.Separator)
}

View File

@@ -0,0 +1,20 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/sys/windows"
)
func getOSRoot(t *testing.T) string {
t.Helper()
sysDir, err := windows.GetSystemDirectory()
require.NoError(t, err)
return filepath.VolumeName(sysDir) + `\`
}

View File

@@ -14,6 +14,8 @@ require (
github.com/stretchr/testify v1.7.0
github.com/xlab/treeprint v1.1.0
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.0
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
gopkg.in/yaml.v2 v2.4.0

View File

@@ -144,6 +144,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=