diff --git a/pkg/chartutil/load.go b/pkg/chartutil/load.go index f10c0eddd..6d70f97b4 100644 --- a/pkg/chartutil/load.go +++ b/pkg/chartutil/load.go @@ -243,7 +243,7 @@ func LoadDir(dir string) (*chart.Chart, error) { files := []*BufferedFile{} topdir += string(filepath.Separator) - err = filepath.Walk(topdir, func(name string, fi os.FileInfo, err error) error { + walk := func(name string, fi os.FileInfo, err error) error { n := strings.TrimPrefix(name, topdir) if n == "" { // No need to process top level. Avoid bug with helmignore .* matching @@ -278,10 +278,45 @@ func LoadDir(dir string) (*chart.Chart, error) { files = append(files, &BufferedFile{Name: n, Data: data}) return nil - }) - if err != nil { + } + if err = filepath.Walk(topdir, symWalk(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 +} diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 1ddaa78a1..24388f86c 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -425,6 +425,19 @@ func TestDependentChartWithSubChartsHelmignore(t *testing.T) { } } +func TestDependentChartsWithSubChartsSymlink(t *testing.T) { + c, err := Load("testdata/joonix") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + if c.Metadata.Name != "joonix" { + t.Fatalf("Unexpected chart name: %s", c.Metadata.Name) + } + if n := len(c.Dependencies); n != 1 { + t.Fatalf("Expected 1 dependency for this chart, but got %d", n) + } +} + func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) { c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml") if err != nil { diff --git a/pkg/chartutil/testdata/joonix/Chart.yaml b/pkg/chartutil/testdata/joonix/Chart.yaml new file mode 100644 index 000000000..c3464c56e --- /dev/null +++ b/pkg/chartutil/testdata/joonix/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: joonix +version: 1.2.3 diff --git a/pkg/chartutil/testdata/joonix/charts/frobnitz b/pkg/chartutil/testdata/joonix/charts/frobnitz new file mode 120000 index 000000000..fde1b78ac --- /dev/null +++ b/pkg/chartutil/testdata/joonix/charts/frobnitz @@ -0,0 +1 @@ +../../frobnitz \ No newline at end of file