perf: download charts in parallel

Signed-off-by: Felipe Santos <felipecassiors@gmail.com>
pull/11793/head
Felipe Santos 3 years ago
parent 5bf273d81b
commit 4ad501c85a

@ -270,7 +270,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{}) var depsToDownload []*chart.Dependency
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,57 +310,71 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
dep.Version = ver dep.Version = ver
continue continue
} }
// Add dep to download list
depsToDownload = append(depsToDownload, dep)
}
// Any failure to resolve/download a chart should fail: // De-duplicate depsToDownload
// https://github.com/helm/helm/issues/1439 deduplicatedDepsToDownload := deduplicateDeps(depsToDownload)
churl, username, password, insecureskiptlsverify, passcredentialsall, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
if err != nil {
saveError = errors.Wrapf(err, "could not find %s", churl)
break
}
if _, ok := churls[churl]; ok { var wg sync.WaitGroup
fmt.Fprintf(m.Out, "Already downloaded %s from repo %s\n", dep.Name, dep.Repository)
continue
}
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) sem := make(chan struct{}, 4)
dl := ChartDownloader{ for _, dep := range deduplicatedDepsToDownload {
Out: m.Out, wg.Add(1)
Verify: m.Verify, sem <- struct{}{}
Keyring: m.Keyring,
RepositoryConfig: m.RepositoryConfig,
RepositoryCache: m.RepositoryCache,
RegistryClient: m.RegistryClient,
Getters: m.Getters,
Options: []getter.Option{
getter.WithBasicAuth(username, password),
getter.WithPassCredentialsAll(passcredentialsall),
getter.WithInsecureSkipVerifyTLS(insecureskiptlsverify),
getter.WithTLSClientConfig(certFile, keyFile, caFile),
},
}
version := "" go func(dep *chart.Dependency) {
if registry.IsOCI(churl) { defer wg.Done()
churl, version, err = parseOCIRef(churl)
fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository)
churl, username, password, insecureskiptlsverify, passcredentialsall, caFile, certFile, keyFile, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
if err != nil { if err != nil {
return errors.Wrapf(err, "could not parse OCI reference") saveError = errors.Wrapf(err, "could not find %s", churl)
return
} }
dl.Options = append(dl.Options,
getter.WithRegistryClient(m.RegistryClient),
getter.WithTagName(version))
}
if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil { dl := ChartDownloader{
saveError = errors.Wrapf(err, "could not download %s", churl) Out: m.Out,
break Verify: m.Verify,
} Keyring: m.Keyring,
RepositoryConfig: m.RepositoryConfig,
RepositoryCache: m.RepositoryCache,
RegistryClient: m.RegistryClient,
Getters: m.Getters,
Options: []getter.Option{
getter.WithBasicAuth(username, password),
getter.WithPassCredentialsAll(passcredentialsall),
getter.WithInsecureSkipVerifyTLS(insecureskiptlsverify),
getter.WithTLSClientConfig(certFile, keyFile, caFile),
},
}
churls[churl] = struct{}{} version := ""
if registry.IsOCI(churl) {
churl, version, err = parseOCIRef(churl)
if err != nil {
saveError = errors.Wrapf(err, "could not parse OCI reference")
return
}
dl.Options = append(dl.Options,
getter.WithRegistryClient(m.RegistryClient),
getter.WithTagName(version))
}
if _, _, err = dl.DownloadTo(churl, version, tmpPath); err != nil {
saveError = errors.Wrapf(err, "could not download %s", churl)
return
}
<-sem
}(dep)
} }
wg.Wait()
// 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".
if saveError == nil { if saveError == nil {
// now we can move all downloaded charts to destPath and delete outdated dependencies // now we can move all downloaded charts to destPath and delete outdated dependencies
@ -374,6 +388,21 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
return nil return nil
} }
func deduplicateDeps(deps []*chart.Dependency) []*chart.Dependency {
depsMap := make(map[string]bool)
var depsResult []*chart.Dependency
for _, dep := range deps {
depKey := fmt.Sprintf("%s/%s/%s", dep.Repository, dep.Name, dep.Version)
if _, ok := depsMap[depKey]; !ok {
depsMap[depKey] = true
depsResult = append(deps, dep)
}
}
return depsResult
}
func parseOCIRef(chartRef string) (string, string, error) { func parseOCIRef(chartRef string) (string, string, error) {
refTagRegexp := regexp.MustCompile(`^(oci://[^:]+(:[0-9]{1,5})?[^:]+):(.*)$`) refTagRegexp := regexp.MustCompile(`^(oci://[^:]+(:[0-9]{1,5})?[^:]+):(.*)$`)
caps := refTagRegexp.FindStringSubmatch(chartRef) caps := refTagRegexp.FindStringSubmatch(chartRef)

Loading…
Cancel
Save