Unverified Commit fb2cbb00 authored by Matthew Fisher's avatar Matthew Fisher Committed by GitHub

Merge pull request #6578 from bacongobbler/fix-3442

fix(helm): Accept dependency in requirements.yaml from charts directory
parents 4e8063b1 74653d37
...@@ -216,6 +216,31 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { ...@@ -216,6 +216,31 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps)) fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps))
var saveError error var saveError error
for _, dep := range deps { for _, dep := range deps {
// No repository means the chart is in charts directory
if dep.Repository == "" {
fmt.Fprintf(m.Out, "Dependency %s did not declare a repository. Assuming it exists in the charts directory\n", dep.Name)
chartPath := filepath.Join(tmpPath, dep.Name)
ch, err := chartutil.LoadDir(chartPath)
if err != nil {
return fmt.Errorf("Unable to load chart: %v", err)
}
constraint, err := semver.NewConstraint(dep.Version)
if err != nil {
return fmt.Errorf("Dependency %s has an invalid version/constraint format: %s", dep.Name, err)
}
v, err := semver.NewVersion(ch.Metadata.Version)
if err != nil {
return fmt.Errorf("Invalid version %s for dependency %s: %s", dep.Version, dep.Name, err)
}
if !constraint.Check(v) {
saveError = fmt.Errorf("Dependency %s at version %s does not satisfy the constraint %s", dep.Name, ch.Metadata.Version, dep.Version)
break
}
continue
}
if strings.HasPrefix(dep.Repository, "file://") { if strings.HasPrefix(dep.Repository, "file://") {
if m.Debug { if m.Debug {
fmt.Fprintf(m.Out, "Archiving %s from repo %s\n", dep.Name, dep.Repository) fmt.Fprintf(m.Out, "Archiving %s from repo %s\n", dep.Name, dep.Repository)
...@@ -258,8 +283,11 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { ...@@ -258,8 +283,11 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
if saveError == nil { if saveError == nil {
fmt.Fprintln(m.Out, "Deleting outdated charts") fmt.Fprintln(m.Out, "Deleting outdated charts")
for _, dep := range deps { for _, dep := range deps {
if err := m.safeDeleteDep(dep.Name, tmpPath); err != nil { // Chart from local charts directory stays in place
return err if dep.Repository != "" {
if err := m.safeDeleteDep(dep.Name, tmpPath); err != nil {
return err
}
} }
} }
if err := move(tmpPath, destPath); err != nil { if err := move(tmpPath, destPath); err != nil {
...@@ -371,8 +399,9 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, ...@@ -371,8 +399,9 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
// by Helm. // by Helm.
missing := []string{} missing := []string{}
for _, dd := range deps { for _, dd := range deps {
// Don't map the repository, we don't need to download chart from charts directory
if dd.Repository == "" { if dd.Repository == "" {
return nil, fmt.Errorf("no 'repository' field specified for dependency: %q", dd.Name) continue
} }
// if dep chart is from local path, verify the path is valid // if dep chart is from local path, verify the path is valid
if strings.HasPrefix(dd.Repository, "file://") { if strings.HasPrefix(dd.Repository, "file://") {
...@@ -663,3 +692,60 @@ func move(tmpPath, destPath string) error { ...@@ -663,3 +692,60 @@ func move(tmpPath, destPath string) error {
} }
return nil return nil
} }
func copyFile(source string, destination string) (err error) {
sourceFile, err := os.Open(source)
if err != nil {
return err
}
defer sourceFile.Close()
destinationFile, err := os.Create(destination)
if err != nil {
return err
}
defer destinationFile.Close()
_, err = io.Copy(destinationFile, sourceFile)
if err == nil {
stats, err := os.Stat(source)
if err == nil {
return os.Chmod(destination, stats.Mode())
}
}
return err
}
func copyDir(source string, destination string) (err error) {
fi, err := os.Stat(source)
if err != nil {
return err
}
if !fi.IsDir() {
return fmt.Errorf("Source is not a directory")
}
_, err = os.Open(destination)
if !os.IsNotExist(err) {
return fmt.Errorf("Destination already exists")
}
err = os.MkdirAll(destination, fi.Mode())
if err != nil {
return err
}
entries, err := ioutil.ReadDir(source)
for _, entry := range entries {
sourceFile := source + "/" + entry.Name()
destinationFile := destination + "/" + entry.Name()
if entry.IsDir() {
err = copyDir(sourceFile, destinationFile)
if err != nil {
return err
}
} else {
err = copyFile(sourceFile, destinationFile)
if err != nil {
return err
}
}
}
return
}
...@@ -113,14 +113,6 @@ func TestGetRepoNames(t *testing.T) { ...@@ -113,14 +113,6 @@ func TestGetRepoNames(t *testing.T) {
}, },
err: true, err: true,
}, },
{
name: "dependency entry missing 'repository' field -- e.g. spelled 'repo'",
req: []*chartutil.Dependency{
{Name: "dependency-missing-repository-field"},
},
err: true,
expectedErr: "no 'repository' field specified for dependency: \"dependency-missing-repository-field\"",
},
{ {
name: "dependency repository is url but not exist in repos", name: "dependency repository is url but not exist in repos",
req: []*chartutil.Dependency{ req: []*chartutil.Dependency{
...@@ -156,6 +148,13 @@ func TestGetRepoNames(t *testing.T) { ...@@ -156,6 +148,13 @@ func TestGetRepoNames(t *testing.T) {
}, },
expect: map[string]string{"oedipus-rex": "testing"}, expect: map[string]string{"oedipus-rex": "testing"},
}, },
{
name: "repo from local chart under charts path",
req: []*chartutil.Dependency{
{Name: "local-subchart", Repository: ""},
},
expect: map[string]string{},
},
} }
for _, tt := range tests { for _, tt := range tests {
......
description: A Helm chart for Kubernetes
name: local-subchart
version: 0.1.0
...@@ -53,6 +53,19 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st ...@@ -53,6 +53,19 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st
locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) locked := make([]*chartutil.Dependency, len(reqs.Dependencies))
missing := []string{} missing := []string{}
for i, d := range reqs.Dependencies { for i, d := range reqs.Dependencies {
if d.Repository == "" {
// Local chart subfolder
if _, err := GetLocalPath(filepath.Join("charts", d.Name), r.chartpath); err != nil {
return nil, err
}
locked[i] = &chartutil.Dependency{
Name: d.Name,
Repository: "",
Version: d.Version,
}
continue
}
if strings.HasPrefix(d.Repository, "file://") { if strings.HasPrefix(d.Repository, "file://") {
if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil {
......
...@@ -90,6 +90,33 @@ func TestResolve(t *testing.T) { ...@@ -90,6 +90,33 @@ func TestResolve(t *testing.T) {
}, },
err: true, err: true,
}, },
{
name: "repo from valid path under charts path",
req: &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "localdependency", Repository: "", Version: "0.1.0"},
},
},
expect: &chartutil.RequirementsLock{
Dependencies: []*chartutil.Dependency{
{Name: "localdependency", Repository: "", Version: "0.1.0"},
},
},
},
{
name: "repo from valid path under charts path",
req: &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "nonexistentdependency", Repository: "", Version: "0.1.0"},
},
},
expect: &chartutil.RequirementsLock{
Dependencies: []*chartutil.Dependency{
{Name: "nonexistentlocaldependency", Repository: "", Version: "0.1.0"},
},
},
err: true,
},
} }
repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"}
......
description: A Helm chart for Kubernetes
name: localdependency
version: 0.1.0
Markdown is supported
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