fix(helm): delete outdated deps

This fixes a bug in 'helm dep up' which left old versions of a
dependency after an update.

Closes #1864
pull/1913/head
Matt Butcher 8 years ago
parent 33ccd6b2c9
commit 91da555c85
No known key found for this signature in database
GPG Key ID: DCD5F5E5EF32C345

@ -28,10 +28,15 @@ const dependencyUpDesc = `
Update the on-disk dependencies to mirror the requirements.yaml file.
This command verifies that the required charts, as expressed in 'requirements.yaml',
are present in 'charts/' and are at an acceptable version.
are present in 'charts/' and are at an acceptable version. It will pull down
the latest charts that satisfy the dependencies, and clean up old dependencies.
On successful update, this will generate a lock file that can be used to
rebuild the requirements to an exact version.
Dependencies are not required to be represented in 'requirements.yaml'. For that
reason, an update command will not remove charts unless they are (a) present
in the requirements.yaml file, but (b) at the wrong version.
`
// dependencyUpdateCmd describes a 'helm dependency update'

@ -98,7 +98,34 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Errorf("Failed hash match: expected %s, got %s", hash, h)
}
t.Logf("Results: %s", out.String())
// Now change the dependencies and update. This verifies that on update,
// old dependencies are cleansed and new dependencies are added.
reqfile := &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
},
}
dir := filepath.Join(hh, chartname)
if err := writeRequirements(dir, reqfile); err != nil {
t.Fatal(err)
}
if err := duc.run(); err != nil {
output := out.String()
t.Logf("Output: %s", output)
t.Fatal(err)
}
// In this second run, we should see compressedchart-0.3.0.tgz, and not
// the 0.1.0 version.
expect = filepath.Join(hh, chartname, "charts/compressedchart-0.3.0.tgz")
if _, err := os.Stat(expect); err != nil {
t.Fatalf("Expected %q: %s", expect, err)
}
dontExpect := filepath.Join(hh, chartname, "charts/compressedchart-0.1.0.tgz")
if _, err := os.Stat(dontExpect); err == nil {
t.Fatalf("Unexpected %q", dontExpect)
}
}
// createTestingChart creates a basic chart that depends on reqtest-0.1.0
@ -117,8 +144,13 @@ func createTestingChart(dest, name, baseURL string) error {
req := &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
},
}
return writeRequirements(dir, req)
}
func writeRequirements(dir string, req *chartutil.Requirements) error {
data, err := yaml.Marshal(req)
if err != nil {
return err

@ -169,6 +169,9 @@ func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]stri
}
// downloadAll takes a list of dependencies and downloads them into charts/
//
// It will delete versions of the chart that exist on disk and might cause
// a conflict.
func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
repos, err := m.loadChartRepositories()
if err != nil {
@ -195,6 +198,9 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps))
for _, dep := range deps {
if err := m.safeDeleteDep(dep.Name, destPath); err != nil {
return err
}
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository)
// Any failure to resolve/download a chart should fail:
@ -211,6 +217,39 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
return nil
}
// safeDeleteDep deletes any versions of the given dependency in the given directory.
//
// It does this by first matching the file name to an expected pattern, then loading
// the file to verify that it is a chart with the same name as the given name.
//
// Because it requires tar file introspection, it is more intensive than a basic delete.
//
// This will only return errors that should stop processing entirely. Other errors
// will emit log messages or be ignored.
func (m *Manager) safeDeleteDep(name, dir string) error {
files, err := filepath.Glob(filepath.Join(dir, name+"-*.tgz"))
if err != nil {
// Only for ErrBadPattern
return err
}
for _, fname := range files {
ch, err := chartutil.LoadFile(fname)
if err != nil {
fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)", fname, err)
continue
}
if ch.Metadata.Name != name {
// This is not the file you are looking for.
continue
}
if err := os.Remove(fname); err != nil {
fmt.Fprintf(m.Out, "Could not delete %s: %s (Skipping)", fname, err)
continue
}
}
return nil
}
// hasAllRepos ensures that all of the referenced deps are in the local repo cache.
func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error {
rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile())

Loading…
Cancel
Save