Record and verify tarball digests in dependency update and build

Populate per-dependency digests after download, rewrite Chart.lock when
content changes, and fail dependency build when locked bytes do not match.

Signed-off-by: Simone Tollardo <simone.tollardo@redhat.com>
pull/32243/head
Simone Tollardo 4 months ago
parent 12200b00df
commit 02d358d8cf
No known key found for this signature in database
GPG Key ID: 1A829EE297D64CE7

@ -211,9 +211,8 @@ func (m *Manager) Update() error {
} }
lock.Digest = newDigest lock.Digest = newDigest
// If the lock file hasn't changed, don't write a new one. // Rewrite the lock when metadata or per-dependency content digests changed.
oldLock := c.Lock if !lockNeedsWrite(c.Lock, lock) {
if oldLock != nil && oldLock.Digest == lock.Digest {
return nil return nil
} }
@ -273,6 +272,7 @@ func (m *Manager) downloadAll(deps []*chart.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
churls := make(map[string]struct{}) churls := make(map[string]struct{})
churlFiles := make(map[string]string)
for _, dep := range deps { for _, dep := range deps {
// No repository means the chart is in charts directory // No repository means the chart is in charts directory
if dep.Repository == "" { if dep.Repository == "" {
@ -310,12 +310,17 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
break break
} }
dep.Version = ver dep.Version = ver
tgzPath := filepath.Join(tmpPath, fmt.Sprintf("%s-%s.tgz", dep.Name, ver))
if err := recordDependencyDigest(dep, tgzPath); err != nil {
saveError = err
break
}
continue continue
} }
// Any failure to resolve/download a chart should fail: // Any failure to resolve/download a chart should fail:
// https://github.com/helm/helm/issues/1439 // https://github.com/helm/helm/issues/1439
churl, username, password, insecureSkipTLSVerify, passCredentialsAll, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) churl, username, password, insecureSkipTLSVerify, passCredentialsAll, caFile, certFile, keyFile, indexDigest, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
if err != nil { if err != nil {
saveError = fmt.Errorf("could not find %s: %w", churl, err) saveError = fmt.Errorf("could not find %s: %w", churl, err)
break break
@ -323,6 +328,12 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
if _, ok := churls[churl]; ok { if _, ok := churls[churl]; ok {
fmt.Fprintf(m.Out, "Already downloaded %s from repo %s\n", dep.Name, dep.Repository) fmt.Fprintf(m.Out, "Already downloaded %s from repo %s\n", dep.Name, dep.Repository)
if tgzPath, ok := churlFiles[churl]; ok {
if err := recordDependencyDigest(dep, tgzPath); err != nil {
saveError = err
break
}
}
continue continue
} }
@ -343,6 +354,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
getter.WithInsecureSkipVerifyTLS(insecureSkipTLSVerify), getter.WithInsecureSkipVerifyTLS(insecureSkipTLSVerify),
getter.WithTLSClientConfig(certFile, keyFile, caFile), getter.WithTLSClientConfig(certFile, keyFile, caFile),
}, },
ExpectedDigest: indexDigest,
} }
version := "" version := ""
@ -356,12 +368,19 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
getter.WithTagName(version)) getter.WithTagName(version))
} }
if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil { destfile, _, err := dl.DownloadTo(churl, version, tmpPath)
if err != nil {
saveError = fmt.Errorf("could not download %s: %w", churl, err) saveError = fmt.Errorf("could not download %s: %w", churl, err)
break break
} }
if err := recordDependencyDigest(dep, destfile); err != nil {
saveError = err
break
}
churls[churl] = struct{}{} churls[churl] = struct{}{}
churlFiles[churl] = destfile
} }
// TODO: this should probably be refactored to be a []error, so we can capture and provide more information rather than "last error wins". // TODO: this should probably be refactored to be a []error, so we can capture and provide more information rather than "last error wins".
@ -722,9 +741,9 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
// repoURL is the repository to search // repoURL is the repository to search
// //
// If it finds a URL that is "relative", it will prepend the repoURL. // If it finds a URL that is "relative", it will prepend the repoURL.
func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureSkipTLSVerify, passCredentialsAll bool, caFile, certFile, keyFile string, err error) { func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, insecureSkipTLSVerify, passCredentialsAll bool, caFile, certFile, keyFile, indexDigest string, err error) {
if registry.IsOCI(repoURL) { if registry.IsOCI(repoURL) {
return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", nil return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", "", nil
} }
for _, cr := range repos { for _, cr := range repos {
@ -744,6 +763,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
//nolint:nakedret //nolint:nakedret
return return
} }
indexDigest = ve.Digest
url, err = repo.ResolveReferenceURL(repoURL, ve.URLs[0]) url, err = repo.ResolveReferenceURL(repoURL, ve.URLs[0])
if err != nil { if err != nil {
//nolint:nakedret //nolint:nakedret
@ -762,10 +782,10 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*
} }
url, err = repo.FindChartInRepoURL(repoURL, name, m.Getters, repo.WithChartVersion(version), repo.WithClientTLS(certFile, keyFile, caFile)) url, err = repo.FindChartInRepoURL(repoURL, name, m.Getters, repo.WithChartVersion(version), repo.WithClientTLS(certFile, keyFile, caFile))
if err == nil { if err == nil {
return url, username, password, false, false, "", "", "", err return url, username, password, false, false, "", "", "", "", err
} }
err = fmt.Errorf("chart %s not found in %s: %w", name, repoURL, err) err = fmt.Errorf("chart %s not found in %s: %w", name, repoURL, err)
return url, username, password, false, false, "", "", "", err return url, username, password, false, false, "", "", "", "", err
} }
// findEntryByName finds an entry in the chart repository whose name matches the given name. // findEntryByName finds an entry in the chart repository whose name matches the given name.

@ -71,7 +71,7 @@ func TestFindChartURL(t *testing.T) {
version := "0.1.0" version := "0.1.0"
repoURL := "http://example.com/charts" repoURL := "http://example.com/charts"
churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err := m.findChartURL(name, version, repoURL, repos) churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, _, err := m.findChartURL(name, version, repoURL, repos)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -96,7 +96,7 @@ func TestFindChartURL(t *testing.T) {
version = "1.2.3" version = "1.2.3"
repoURL = "https://example-https-insecureskiptlsverify.com" repoURL = "https://example-https-insecureskiptlsverify.com"
churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, repoURL, repos) churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, _, err = m.findChartURL(name, version, repoURL, repos)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -121,7 +121,7 @@ func TestFindChartURL(t *testing.T) {
version = "1.2.3" version = "1.2.3"
repoURL = "http://example.com/helm" repoURL = "http://example.com/helm"
churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, repoURL, repos) churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, _, err = m.findChartURL(name, version, repoURL, repos)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

Loading…
Cancel
Save