Unverified Commit e1027fae authored by Matthew Fisher's avatar Matthew Fisher Committed by GitHub
Browse files

refactor symWalk() into pkg/sympath (#3199)

* refactor symWalk() into pkg/sympath

* add Go copyright

* remove dead code
parent 9d0f9a24
......@@ -32,6 +32,7 @@ import (
"k8s.io/helm/pkg/ignore"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/sympath"
)
// Load takes a string name, tries to resolve it to a file or directory, and then loads it.
......@@ -279,44 +280,9 @@ func LoadDir(dir string) (*chart.Chart, error) {
files = append(files, &BufferedFile{Name: n, Data: data})
return nil
}
if err = filepath.Walk(topdir, symWalk(topdir, "", walk)); err != nil {
if err = sympath.Walk(topdir, walk); err != nil {
return c, err
}
return LoadFiles(files)
}
// symWalk walks topdir with optional symbolic link dir, symdir. The symdir will
// be used as the path name sent to walkFn.
func symWalk(topdir, symdir string, walkFn filepath.WalkFunc) filepath.WalkFunc {
return func(name string, fi os.FileInfo, err error) error {
// Recover the symbolic path instead of the real path.
if symdir != "" {
relative, err := filepath.Rel(topdir, name)
if err != nil {
return err
}
name = filepath.Join(symdir, relative)
}
// Recursively walk symlinked directories.
if isSymlink(fi) {
resolved, err := filepath.EvalSymlinks(name)
if err != nil {
return fmt.Errorf("error evaluating symlink %s: %s", name, err)
}
if fi, err = os.Lstat(resolved); err != nil {
return err
}
if fi.IsDir() {
return filepath.Walk(resolved, symWalk(resolved, name, walkFn))
}
}
return walkFn(name, fi, err)
}
}
func isSymlink(fi os.FileInfo) bool {
return fi.Mode()&os.ModeSymlink != 0
}
/*
Copyright (c) for portions of walk.go are held by The Go Authors, 2009 and are provided under
the BSD license.
https://github.com/golang/go/blob/master/LICENSE
Copyright 2017 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sympath
import (
"fmt"
"os"
"path/filepath"
"sort"
)
// Walk walks the file tree rooted at root, calling walkFn for each file or directory
// in the tree, including root. All errors that arise visiting files and directories
// are filtered by walkFn. The files are walked in lexical order, which makes the
// output deterministic but means that for very large directories Walk can be
// inefficient. Walk follows symbolic links.
func Walk(root string, walkFn filepath.WalkFunc) error {
info, err := os.Lstat(root)
if err != nil {
err = walkFn(root, nil, err)
} else {
err = symwalk(root, info, walkFn)
}
if err == filepath.SkipDir {
return nil
}
return err
}
// readDirNames reads the directory named by dirname and returns
// a sorted list of directory entries.
func readDirNames(dirname string) ([]string, error) {
f, err := os.Open(dirname)
if err != nil {
return nil, err
}
names, err := f.Readdirnames(-1)
f.Close()
if err != nil {
return nil, err
}
sort.Strings(names)
return names, nil
}
// symwalk recursively descends path, calling walkFn.
func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
// Recursively walk symlinked directories.
if IsSymlink(info) {
resolved, err := filepath.EvalSymlinks(path)
if err != nil {
return fmt.Errorf("error evaluating symlink %s: %s", path, err)
}
if info, err = os.Lstat(resolved); err != nil {
return err
}
if err := symwalk(resolved, info, walkFn); err != nil && err != filepath.SkipDir {
return err
}
}
if err := walkFn(path, info, nil); err != nil {
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := filepath.Join(path, name)
fileInfo, err := os.Lstat(filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = symwalk(filename, fileInfo, walkFn)
if err != nil {
if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// IsSymlink is used to determine if the fileinfo is a symbolic link.
func IsSymlink(fi os.FileInfo) bool {
return fi.Mode()&os.ModeSymlink != 0
}
/*
Copyright (c) for portions of walk_test.go are held by The Go Authors, 2009 and are provided under
the BSD license.
https://github.com/golang/go/blob/master/LICENSE
Copyright 2017 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sympath
import (
"os"
"path/filepath"
"testing"
)
type Node struct {
name string
entries []*Node // nil if the entry is a file
mark int
}
var tree = &Node{
"testdata",
[]*Node{
{"a", nil, 0},
{"b", []*Node{}, 0},
{"c", nil, 0},
{
"d",
[]*Node{
{"x", nil, 0},
{"y", []*Node{}, 0},
{
"z",
[]*Node{
{"u", nil, 0},
{"v", nil, 0},
{"w", nil, 0},
},
0,
},
},
0,
},
},
0,
}
func walkTree(n *Node, path string, f func(path string, n *Node)) {
f(path, n)
for _, e := range n.entries {
walkTree(e, filepath.Join(path, e.name), f)
}
}
func makeTree(t *testing.T) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.entries == nil {
fd, err := os.Create(path)
if err != nil {
t.Errorf("makeTree: %v", err)
return
}
fd.Close()
} else {
os.Mkdir(path, 0770)
}
})
}
func checkMarks(t *testing.T, report bool) {
walkTree(tree, tree.name, func(path string, n *Node) {
if n.mark != 1 && report {
t.Errorf("node %s mark = %d; expected 1", path, n.mark)
}
n.mark = 0
})
}
// Assumes that each node name is unique. Good enough for a test.
// If clear is true, any incoming error is cleared before return. The errors
// are always accumulated, though.
func mark(info os.FileInfo, err error, errors *[]error, clear bool) error {
if err != nil {
*errors = append(*errors, err)
if clear {
return nil
}
return err
}
name := info.Name()
walkTree(tree, tree.name, func(path string, n *Node) {
if n.name == name {
n.mark++
}
})
return nil
}
func TestWalk(t *testing.T) {
makeTree(t)
errors := make([]error, 0, 10)
clear := true
markFn := func(path string, info os.FileInfo, err error) error {
return mark(info, err, &errors, clear)
}
// Expect no errors.
err := Walk(tree.name, markFn)
if err != nil {
t.Fatalf("no error expected, found: %s", err)
}
if len(errors) != 0 {
t.Fatalf("unexpected errors: %s", errors)
}
checkMarks(t, true)
// cleanup
if err := os.RemoveAll(tree.name); err != nil {
t.Errorf("removeTree: %v", err)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment