mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Start api directory, which will become a module.
This commit is contained in:
79
api/filesys/confirmeddir.go
Normal file
79
api/filesys/confirmeddir.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConfirmedDir is a clean, absolute, delinkified path
|
||||
// that was confirmed to point to an existing directory.
|
||||
type ConfirmedDir string
|
||||
|
||||
// NewTmpConfirmedDir returns a temporary dir, else error.
|
||||
// The directory is cleaned, no symlinks, etc. so it's
|
||||
// returned as a ConfirmedDir.
|
||||
func NewTmpConfirmedDir() (ConfirmedDir, error) {
|
||||
n, err := ioutil.TempDir("", "kustomize-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// In MacOs `ioutil.TempDir` creates a directory
|
||||
// with root in the `/var` folder, which is in turn
|
||||
// a symlinked path to `/private/var`.
|
||||
// Function `filepath.EvalSymlinks`is used to
|
||||
// resolve the real absolute path.
|
||||
deLinked, err := filepath.EvalSymlinks(n)
|
||||
return ConfirmedDir(deLinked), err
|
||||
}
|
||||
|
||||
// HasPrefix returns true if the directory argument
|
||||
// is a prefix of self (d) from the point of view of
|
||||
// a file system.
|
||||
//
|
||||
// I.e., it's true if the argument equals or contains
|
||||
// self (d) in a file path sense.
|
||||
//
|
||||
// HasPrefix emulates the semantics of strings.HasPrefix
|
||||
// such that the following are true:
|
||||
//
|
||||
// strings.HasPrefix("foobar", "foobar")
|
||||
// strings.HasPrefix("foobar", "foo")
|
||||
// strings.HasPrefix("foobar", "")
|
||||
//
|
||||
// d := fSys.ConfirmDir("/foo/bar")
|
||||
// d.HasPrefix("/foo/bar")
|
||||
// d.HasPrefix("/foo")
|
||||
// d.HasPrefix("/")
|
||||
//
|
||||
// Not contacting a file system here to check for
|
||||
// actual path existence.
|
||||
//
|
||||
// This is tested on linux, but will have trouble
|
||||
// on other operating systems.
|
||||
// TODO(monopole) Refactor when #golang/go/18358 closes.
|
||||
// See also:
|
||||
// https://github.com/golang/go/issues/18358
|
||||
// https://github.com/golang/dep/issues/296
|
||||
// https://github.com/golang/dep/blob/master/internal/fs/fs.go#L33
|
||||
// https://codereview.appspot.com/5712045
|
||||
func (d ConfirmedDir) HasPrefix(path ConfirmedDir) bool {
|
||||
if path.String() == string(filepath.Separator) || path == d {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(
|
||||
string(d),
|
||||
string(path)+string(filepath.Separator))
|
||||
}
|
||||
|
||||
func (d ConfirmedDir) Join(path string) string {
|
||||
return filepath.Join(string(d), path)
|
||||
}
|
||||
|
||||
func (d ConfirmedDir) String() string {
|
||||
return string(d)
|
||||
}
|
||||
109
api/filesys/confirmeddir_test.go
Normal file
109
api/filesys/confirmeddir_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/v3/api/filesys"
|
||||
)
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
fSys := MakeFsInMemory()
|
||||
err := fSys.Mkdir("/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
d, f, err := fSys.CleanedAbs("/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if f != "" {
|
||||
t.Fatalf("unexpected file: %v", f)
|
||||
}
|
||||
if d.Join("bar") != "/foo/bar" {
|
||||
t.Fatalf("expected join %s", d.Join("bar"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefix_Slash(t *testing.T) {
|
||||
fSys := MakeFsInMemory()
|
||||
d, f, err := fSys.CleanedAbs("/")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if f != "" {
|
||||
t.Fatalf("unexpected file: %v", f)
|
||||
}
|
||||
if d.HasPrefix("/hey") {
|
||||
t.Fatalf("should be false")
|
||||
}
|
||||
if !d.HasPrefix("/") {
|
||||
t.Fatalf("/ should have the prefix /")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefix_SlashFoo(t *testing.T) {
|
||||
fSys := MakeFsInMemory()
|
||||
err := fSys.Mkdir("/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
d, _, err := fSys.CleanedAbs("/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if d.HasPrefix("/fo") {
|
||||
t.Fatalf("/foo does not have path prefix /fo")
|
||||
}
|
||||
if d.HasPrefix("/fod") {
|
||||
t.Fatalf("/foo does not have path prefix /fod")
|
||||
}
|
||||
if !d.HasPrefix("/foo") {
|
||||
t.Fatalf("/foo should have prefix /foo")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrefix_SlashFooBar(t *testing.T) {
|
||||
fSys := MakeFsInMemory()
|
||||
err := fSys.MkdirAll("/foo/bar")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
d, _, err := fSys.CleanedAbs("/foo/bar")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if d.HasPrefix("/fo") {
|
||||
t.Fatalf("/foo/bar does not have path prefix /fo")
|
||||
}
|
||||
if d.HasPrefix("/foobar") {
|
||||
t.Fatalf("/foo/bar does not have path prefix /foobar")
|
||||
}
|
||||
if !d.HasPrefix("/foo/bar") {
|
||||
t.Fatalf("/foo/bar should have prefix /foo/bar")
|
||||
}
|
||||
if !d.HasPrefix("/foo") {
|
||||
t.Fatalf("/foo/bar should have prefix /foo")
|
||||
}
|
||||
if !d.HasPrefix("/") {
|
||||
t.Fatalf("/foo/bar should have prefix /")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewTempConfirmDir(t *testing.T) {
|
||||
tmp, err := NewTmpConfirmedDir()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
delinked, err := filepath.EvalSymlinks(string(tmp))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(tmp) != delinked {
|
||||
t.Fatalf("unexpected path containing symlinks")
|
||||
}
|
||||
}
|
||||
41
api/filesys/file.go
Normal file
41
api/filesys/file.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ os.FileInfo = &fileInfo{}
|
||||
|
||||
// fileInfo implements os.FileInfo for a fileInMemory instance.
|
||||
type fileInfo struct {
|
||||
*fileInMemory
|
||||
}
|
||||
|
||||
// Name returns the name of the file
|
||||
func (fi *fileInfo) Name() string { return fi.name }
|
||||
|
||||
// Size returns the size of the file
|
||||
func (fi *fileInfo) Size() int64 { return int64(len(fi.content)) }
|
||||
|
||||
// Mode returns the file mode
|
||||
func (fi *fileInfo) Mode() os.FileMode { return 0777 }
|
||||
|
||||
// ModTime returns the modification time
|
||||
func (fi *fileInfo) ModTime() time.Time { return time.Time{} }
|
||||
|
||||
// IsDir returns if it is a directory
|
||||
func (fi *fileInfo) IsDir() bool { return fi.dir }
|
||||
|
||||
// Sys should return underlying data source, but it now returns nil
|
||||
func (fi *fileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// File groups the basic os.File methods.
|
||||
type File interface {
|
||||
io.ReadWriteCloser
|
||||
Stat() (os.FileInfo, error)
|
||||
}
|
||||
56
api/filesys/fileinmemory.go
Normal file
56
api/filesys/fileinmemory.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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
|
||||
}
|
||||
27
api/filesys/fileondisk.go
Normal file
27
api/filesys/fileondisk.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var _ File = &fileOnDisk{}
|
||||
|
||||
// fileOnDisk implements File using the local filesystem.
|
||||
type fileOnDisk struct {
|
||||
file *os.File
|
||||
}
|
||||
|
||||
// Close closes a file.
|
||||
func (f *fileOnDisk) Close() error { return f.file.Close() }
|
||||
|
||||
// Read reads a file's content.
|
||||
func (f *fileOnDisk) Read(p []byte) (n int, err error) { return f.file.Read(p) }
|
||||
|
||||
// Write writes bytes to a file
|
||||
func (f *fileOnDisk) Write(p []byte) (n int, err error) { return f.file.Write(p) }
|
||||
|
||||
// Stat returns an interface which has all the information regarding the file.
|
||||
func (f *fileOnDisk) Stat() (os.FileInfo, error) { return f.file.Stat() }
|
||||
41
api/filesys/filesystem.go
Normal file
41
api/filesys/filesystem.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package filesys provides a file system abstraction layer.
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileSystem groups basic os filesystem methods.
|
||||
type FileSystem interface {
|
||||
// Create a file.
|
||||
Create(name string) (File, error)
|
||||
// MkDir makes a directory.
|
||||
Mkdir(path string) error
|
||||
// MkDir 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
|
||||
// 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
|
||||
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.
|
||||
WriteFile(path string, data []byte) error
|
||||
// Walk walks the file system with the given WalkFunc.
|
||||
Walk(path string, walkFn filepath.WalkFunc) error
|
||||
}
|
||||
223
api/filesys/fsinmemory.go
Normal file
223
api/filesys/fsinmemory.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
const (
|
||||
separator = string(filepath.Separator)
|
||||
doubleSep = separator + separator
|
||||
)
|
||||
|
||||
// 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("cannot read file %q", 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
|
||||
}
|
||||
149
api/filesys/fsinmemory_test.go
Normal file
149
api/filesys/fsinmemory_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/v3/api/filesys"
|
||||
)
|
||||
|
||||
func TestExists(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.Compare(c, content) != 0 {
|
||||
t.Fatalf("incorrect content: %v", content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlob(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)
|
||||
}
|
||||
}
|
||||
114
api/filesys/fsondisk.go
Normal file
114
api/filesys/fsondisk.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var _ FileSystem = fsOnDisk{}
|
||||
|
||||
// fsOnDisk implements FileSystem using the local filesystem.
|
||||
type fsOnDisk struct{}
|
||||
|
||||
// MakeFsOnDisk makes an instance of fsOnDisk.
|
||||
func MakeFsOnDisk() FileSystem {
|
||||
return fsOnDisk{}
|
||||
}
|
||||
|
||||
// Create delegates to os.Create.
|
||||
func (fsOnDisk) Create(name string) (File, error) { return os.Create(name) }
|
||||
|
||||
// Mkdir delegates to os.Mkdir.
|
||||
func (fsOnDisk) Mkdir(name string) error {
|
||||
return os.Mkdir(name, 0777|os.ModeDir)
|
||||
}
|
||||
|
||||
// MkdirAll delegates to os.MkdirAll.
|
||||
func (fsOnDisk) MkdirAll(name string) error {
|
||||
return os.MkdirAll(name, 0777|os.ModeDir)
|
||||
}
|
||||
|
||||
// RemoveAll delegates to os.RemoveAll.
|
||||
func (fsOnDisk) RemoveAll(name string) error {
|
||||
return os.RemoveAll(name)
|
||||
}
|
||||
|
||||
// Open delegates to os.Open.
|
||||
func (fsOnDisk) Open(name string) (File, error) { return os.Open(name) }
|
||||
|
||||
// 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.
|
||||
func (x fsOnDisk) CleanedAbs(
|
||||
path string) (ConfirmedDir, string, error) {
|
||||
absRoot, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf(
|
||||
"abs path error on '%s' : %v", path, err)
|
||||
}
|
||||
deLinked, err := filepath.EvalSymlinks(absRoot)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf(
|
||||
"evalsymlink failure on '%s' : %v", path, err)
|
||||
}
|
||||
if x.IsDir(deLinked) {
|
||||
return ConfirmedDir(deLinked), "", nil
|
||||
}
|
||||
d := filepath.Dir(deLinked)
|
||||
if !x.IsDir(d) {
|
||||
// Programmer/assumption error.
|
||||
log.Fatalf("first part of '%s' not a directory", deLinked)
|
||||
}
|
||||
if d == deLinked {
|
||||
// Programmer/assumption error.
|
||||
log.Fatalf("d '%s' should be a subset of deLinked", d)
|
||||
}
|
||||
f := filepath.Base(deLinked)
|
||||
if filepath.Join(d, f) != deLinked {
|
||||
// Programmer/assumption error.
|
||||
log.Fatalf("these should be equal: '%s', '%s'",
|
||||
filepath.Join(d, f), deLinked)
|
||||
}
|
||||
return ConfirmedDir(d), f, nil
|
||||
}
|
||||
|
||||
// Exists returns true if os.Stat succeeds.
|
||||
func (fsOnDisk) Exists(name string) bool {
|
||||
_, err := os.Stat(name)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Glob returns the list of matching files
|
||||
func (fsOnDisk) Glob(pattern string) ([]string, error) {
|
||||
return filepath.Glob(pattern)
|
||||
}
|
||||
|
||||
// IsDir delegates to os.Stat and FileInfo.IsDir
|
||||
func (fsOnDisk) IsDir(name string) bool {
|
||||
info, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return info.IsDir()
|
||||
}
|
||||
|
||||
// ReadFile delegates to ioutil.ReadFile.
|
||||
func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
|
||||
|
||||
// WriteFile delegates to ioutil.WriteFile with read/write permissions.
|
||||
func (fsOnDisk) WriteFile(name string, c []byte) error {
|
||||
return ioutil.WriteFile(name, c, 0666)
|
||||
}
|
||||
|
||||
// Walk delegates to filepath.Walk.
|
||||
func (fsOnDisk) Walk(path string, walkFn filepath.WalkFunc) error {
|
||||
return filepath.Walk(path, walkFn)
|
||||
}
|
||||
165
api/filesys/fsondisk_test.go
Normal file
165
api/filesys/fsondisk_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/v3/api/filesys"
|
||||
)
|
||||
|
||||
func makeTestDir(t *testing.T) (FileSystem, string) {
|
||||
fSys := MakeFsOnDisk()
|
||||
td, err := ioutil.TempDir("", "kustomize_testing_dir")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
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")
|
||||
}
|
||||
return fSys, testDir
|
||||
}
|
||||
|
||||
func TestCleanedAbs_1(t *testing.T) {
|
||||
fSys, testDir := makeTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanedAbs_2(t *testing.T) {
|
||||
fSys, testDir := makeTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanedAbs_3(t *testing.T) {
|
||||
fSys, testDir := makeTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
err := fSys.WriteFile(
|
||||
filepath.Join(testDir, "foo"), []byte(`foo`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err=%v", 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanedAbs_4(t *testing.T) {
|
||||
fSys, testDir := makeTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
err := fSys.MkdirAll(filepath.Join(testDir, "d1", "d2"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err=%v", err)
|
||||
}
|
||||
err = fSys.WriteFile(
|
||||
filepath.Join(testDir, "d1", "d2", "bar"),
|
||||
[]byte(`bar`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err=%v", 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)
|
||||
}
|
||||
|
||||
d, f, err = fSys.CleanedAbs(
|
||||
filepath.Join(testDir, "d1", "d2", "bar"))
|
||||
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 != "bar" {
|
||||
t.Fatalf("unexpected f=%s", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadFilesRealFS(t *testing.T) {
|
||||
fSys, testDir := makeTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
err := fSys.WriteFile(path.Join(testDir, "foo"), []byte(`foo`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
if !fSys.Exists(path.Join(testDir, "foo")) {
|
||||
t.Fatalf("expected foo")
|
||||
}
|
||||
if fSys.IsDir(path.Join(testDir, "foo")) {
|
||||
t.Fatalf("expected foo not to be a directory")
|
||||
}
|
||||
|
||||
err = fSys.WriteFile(path.Join(testDir, "bar"), []byte(`bar`))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
files, err := fSys.Glob(path.Join("testDir", "*"))
|
||||
expected := []string{
|
||||
path.Join(testDir, "bar"),
|
||||
path.Join(testDir, "foo"),
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error")
|
||||
}
|
||||
if reflect.DeepEqual(files, expected) {
|
||||
t.Fatalf("incorrect files found by glob: %v", files)
|
||||
}
|
||||
}
|
||||
12
api/filesys/rpath.go
Normal file
12
api/filesys/rpath.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// RootedPath returns a rooted path, e.g. "/foo/bar" as
|
||||
// opposed to "foo/bar".
|
||||
func RootedPath(elem ...string) string {
|
||||
return separator + filepath.Join(elem...)
|
||||
}
|
||||
Reference in New Issue
Block a user