pull/3973/merge
Christoph Hösler 7 years ago committed by GitHub
commit 1b5052cbca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -68,6 +68,8 @@ type Dependency struct {
ImportValues []interface{} `json:"import-values,omitempty"` ImportValues []interface{} `json:"import-values,omitempty"`
// Alias usable alias to be used for the chart // Alias usable alias to be used for the chart
Alias string `json:"alias,omitempty"` Alias string `json:"alias,omitempty"`
// SHA256 digest of the tarball
Digest string `json:"digest,omitempty"`
} }
// ErrNoRequirementsFile to detect error condition // ErrNoRequirementsFile to detect error condition

@ -34,6 +34,7 @@ import (
"k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/provenance"
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/resolver" "k8s.io/helm/pkg/resolver"
"k8s.io/helm/pkg/urlutil" "k8s.io/helm/pkg/urlutil"
@ -86,8 +87,8 @@ func (m *Manager) Build() error {
return fmt.Errorf("requirements.lock is out of sync with requirements.yaml") return fmt.Errorf("requirements.lock is out of sync with requirements.yaml")
} }
// Check that all of the repos we're dependent on actually exist. repoNames, err := m.getRepoNames(lock.Dependencies)
if err := m.hasAllRepos(lock.Dependencies); err != nil { if err != nil {
return err return err
} }
@ -99,10 +100,16 @@ func (m *Manager) Build() error {
} }
// Now we need to fetch every package here into charts/ // Now we need to fetch every package here into charts/
if err := m.downloadAll(lock.Dependencies); err != nil { files, err := m.downloadAll(lock.Dependencies, repoNames)
if err != nil {
return err return err
} }
// Verify that stored digests match calculated digests of the downloaded files
if err := m.verifyDigests(lock.Dependencies, files); err != nil {
return fmt.Errorf("dependency verification failed: %s", err)
}
return nil return nil
} }
@ -156,18 +163,23 @@ func (m *Manager) Update() error {
} }
// Now we need to fetch every package here into charts/ // Now we need to fetch every package here into charts/
if err := m.downloadAll(lock.Dependencies); err != nil { files, err := m.downloadAll(lock.Dependencies, repoNames)
if err != nil {
return err return err
} }
// If the lock file hasn't changed, don't write a new one. // Update digest of locked dependencies
oldLock, err := chartutil.LoadRequirementsLock(c) if err := m.updateDigests(lock.Dependencies, files); err != nil {
if err == nil && oldLock.Digest == lock.Digest { return fmt.Errorf("failed to update digests of locked dependencies: %s", err)
return nil
} }
// Finally, we need to write the lockfile. // Finally, we need to write the lockfile.
return writeLock(m.ChartPath, lock) fmt.Fprintf(m.Out, "Updating requirements.lock\n")
if err := writeLock(m.ChartPath, lock); err != nil {
return fmt.Errorf("failed to write requirements.lock: %s", err)
}
return nil
} }
func (m *Manager) loadChartDir() (*chart.Chart, error) { func (m *Manager) loadChartDir() (*chart.Chart, error) {
@ -191,10 +203,12 @@ func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]stri
// //
// It will delete versions of the chart that exist on disk and might cause // It will delete versions of the chart that exist on disk and might cause
// a conflict. // a conflict.
func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { //
// This returns the paths to the downloaded files
func (m *Manager) downloadAll(deps []*chartutil.Dependency, repoNames map[string]string) ([]string, error) {
repos, err := m.loadChartRepositories() repos, err := m.loadChartRepositories()
if err != nil { if err != nil {
return err return nil, err
} }
destPath := filepath.Join(m.ChartPath, "charts") destPath := filepath.Join(m.ChartPath, "charts")
@ -203,28 +217,31 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
// Create 'charts' directory if it doesn't already exist. // Create 'charts' directory if it doesn't already exist.
if fi, err := os.Stat(destPath); err != nil { if fi, err := os.Stat(destPath); err != nil {
if err := os.MkdirAll(destPath, 0755); err != nil { if err := os.MkdirAll(destPath, 0755); err != nil {
return err return nil, err
} }
} else if !fi.IsDir() { } else if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", destPath) return nil, fmt.Errorf("%q is not a directory", destPath)
} }
if err := os.Rename(destPath, tmpPath); err != nil { if err := os.Rename(destPath, tmpPath); err != nil {
return fmt.Errorf("Unable to move current charts to tmp dir: %v", err) return nil, fmt.Errorf("unable to move current charts to tmp dir: %v", err)
} }
if err := os.MkdirAll(destPath, 0755); err != nil { if err := os.MkdirAll(destPath, 0755); err != nil {
return err return nil, err
} }
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
var files []string
for _, dep := range deps { for _, dep := range deps {
if strings.HasPrefix(dep.Repository, "file://") { repoURL := repos[repoNames[dep.Name]].Config.URL
if strings.HasPrefix(repoURL, "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, repoURL)
} }
ver, err := tarFromLocalDir(m.ChartPath, dep.Name, dep.Repository, dep.Version) ver, err := tarFromLocalDir(m.ChartPath, dep.Name, repoURL, dep.Version)
if err != nil { if err != nil {
saveError = err saveError = err
break break
@ -233,13 +250,13 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
continue continue
} }
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) fmt.Fprintf(m.Out, "Downloading %s %s from repo %s\n", dep.Name, dep.Version, repoURL)
// Any failure to resolve/download a chart should fail: // Any failure to resolve/download a chart should fail:
// https://github.com/kubernetes/helm/issues/1439 // https://github.com/kubernetes/helm/issues/1439
churl, username, password, err := findChartURL(dep.Name, dep.Version, dep.Repository, repos) churl, username, password, err := findChartURL(dep.Name, dep.Version, repoURL, repos)
if err != nil { if err != nil {
saveError = fmt.Errorf("could not find %s: %s", churl, err) saveError = fmt.Errorf("could not find chart %s %s in repo %s: %s", dep.Name, dep.Version, repoURL, err)
break break
} }
@ -253,42 +270,46 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
Password: password, Password: password,
} }
if _, _, err := dl.DownloadTo(churl, "", destPath); err != nil { file, _, err := dl.DownloadTo(churl, "", destPath)
if err != nil {
saveError = fmt.Errorf("could not download %s: %s", churl, err) saveError = fmt.Errorf("could not download %s: %s", churl, err)
break break
} }
files = append(files, file)
} }
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 { if err := m.safeDeleteDep(dep.Name, tmpPath); err != nil {
return err return nil, err
} }
} }
if err := move(tmpPath, destPath); err != nil { if err := move(tmpPath, destPath); err != nil {
return err return nil, err
} }
if err := os.RemoveAll(tmpPath); err != nil { if err := os.RemoveAll(tmpPath); err != nil {
return fmt.Errorf("Failed to remove %v: %v", tmpPath, err) return nil, fmt.Errorf("Failed to remove %v: %v", tmpPath, err)
} }
} else { } else {
fmt.Fprintln(m.Out, "Save error occurred: ", saveError) fmt.Fprintln(m.Out, "Save error occurred: ", saveError)
fmt.Fprintln(m.Out, "Deleting newly downloaded charts, restoring pre-update state") fmt.Fprintln(m.Out, "Deleting newly downloaded charts, restoring pre-update state")
for _, dep := range deps { for _, dep := range deps {
if err := m.safeDeleteDep(dep.Name, destPath); err != nil { if err := m.safeDeleteDep(dep.Name, destPath); err != nil {
return err return nil, err
} }
} }
if err := os.RemoveAll(destPath); err != nil { if err := os.RemoveAll(destPath); err != nil {
return fmt.Errorf("Failed to remove %v: %v", destPath, err) return nil, fmt.Errorf("failed to remove %v: %v", destPath, err)
} }
if err := os.Rename(tmpPath, destPath); err != nil { if err := os.Rename(tmpPath, destPath); err != nil {
return fmt.Errorf("Unable to move current charts to tmp dir: %v", err) return nil, fmt.Errorf("unable to move current charts to tmp dir: %v", err)
} }
return saveError return nil, saveError
} }
return nil
return files, nil
} }
// safeDeleteDep deletes any versions of the given dependency in the given directory. // safeDeleteDep deletes any versions of the given dependency in the given directory.
@ -324,44 +345,7 @@ func (m *Manager) safeDeleteDep(name, dir string) error {
return nil return nil
} }
// hasAllRepos ensures that all of the referenced deps are in the local repo cache. // getRepoNames returns the repo names of the referenced deps which can be used to fetch the cached index file.
func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error {
rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile())
if err != nil {
return err
}
repos := rf.Repositories
// Verify that all repositories referenced in the deps are actually known
// by Helm.
missing := []string{}
for _, dd := range deps {
// If repo is from local path, continue
if strings.HasPrefix(dd.Repository, "file://") {
continue
}
found := false
if dd.Repository == "" {
found = true
} else {
for _, repo := range repos {
if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) {
found = true
}
}
}
if !found {
missing = append(missing, dd.Repository)
}
}
if len(missing) > 0 {
return fmt.Errorf("no repository definition for %s. Please add the missing repos via 'helm repo add'", strings.Join(missing, ", "))
}
return nil
}
// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file.
func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, error) { func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, error) {
rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile())
if err != nil { if err != nil {
@ -394,7 +378,6 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
if (strings.HasPrefix(dd.Repository, "@") && strings.TrimPrefix(dd.Repository, "@") == repo.Name) || if (strings.HasPrefix(dd.Repository, "@") && strings.TrimPrefix(dd.Repository, "@") == repo.Name) ||
(strings.HasPrefix(dd.Repository, "alias:") && strings.TrimPrefix(dd.Repository, "alias:") == repo.Name) { (strings.HasPrefix(dd.Repository, "alias:") && strings.TrimPrefix(dd.Repository, "alias:") == repo.Name) {
found = true found = true
dd.Repository = repo.URL
reposMap[dd.Name] = repo.Name reposMap[dd.Name] = repo.Name
break break
} else if urlutil.Equal(repo.URL, dd.Repository) { } else if urlutil.Equal(repo.URL, dd.Repository) {
@ -654,3 +637,49 @@ func move(tmpPath, destPath string) error {
} }
return nil return nil
} }
// check for each dependency that has a digest, if it matches the digest of the corresponding file
func (m *Manager) verifyDigests(deps []*chartutil.Dependency, files []string) error {
for i, dep := range deps {
if dep.Digest == "" {
continue
}
split := strings.SplitN(dep.Digest, ":", 2)
if len(split) != 2 {
return fmt.Errorf("found invalid digest string in requirements.lock "+
"for dependency %s: %s", dep.Name, dep.Digest)
}
algorithm := split[0]
expectedSum := split[1]
if algorithm == "sha256" {
file := files[i]
sum, err := provenance.DigestFile(file)
if err != nil {
return err
}
if sum != expectedSum {
return fmt.Errorf("checksums do not match for dependency %s", dep.Name)
}
} else {
return fmt.Errorf("unsupported hash algorithm for dependency %s: %s", dep.Name, algorithm)
}
}
return nil
}
// update the digest of all given dependencies with the sha256 checksum of the corresponding file
func (m *Manager) updateDigests(deps []*chartutil.Dependency, files []string) error {
for i, dep := range deps {
sum, err := provenance.DigestFile(files[i])
if err != nil {
return err
}
dep.Digest = "sha256:" + sum
}
return nil
}

Loading…
Cancel
Save