|
|
@ -85,25 +85,12 @@ type ChartDownloader struct {
|
|
|
|
// Returns a string path to the location where the file was downloaded and a verification
|
|
|
|
// Returns a string path to the location where the file was downloaded and a verification
|
|
|
|
// (if provenance was verified), or an error if something bad happened.
|
|
|
|
// (if provenance was verified), or an error if something bad happened.
|
|
|
|
func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) {
|
|
|
|
func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) {
|
|
|
|
u, r, err := c.ResolveChartVersionAndGetRepo(ref, version)
|
|
|
|
u, r, g, err := c.ResolveChartVersionAndGetRepo(ref, version)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If this ChartDownloader is not configured to use credentials, and the chart repository is,
|
|
|
|
data, err := g.Get(u.String())
|
|
|
|
// then we should use the repository's configured credentials.
|
|
|
|
|
|
|
|
username := c.Username
|
|
|
|
|
|
|
|
password := c.Password
|
|
|
|
|
|
|
|
if r.Config != nil {
|
|
|
|
|
|
|
|
if username == "" {
|
|
|
|
|
|
|
|
username = r.Config.Username
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if password == "" {
|
|
|
|
|
|
|
|
password = r.Config.Password
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data, err := r.Client.GetWithCredentials(u.String(), username, password)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -157,7 +144,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
|
|
|
|
// * If version is empty, this will return the URL for the latest version
|
|
|
|
// * If version is empty, this will return the URL for the latest version
|
|
|
|
// * If no version can be found, an error is returned
|
|
|
|
// * If no version can be found, an error is returned
|
|
|
|
func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, getter.Getter, error) {
|
|
|
|
func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, getter.Getter, error) {
|
|
|
|
u, r, err := c.ResolveChartVersionAndGetRepo(ref, version)
|
|
|
|
u, r, _, err := c.ResolveChartVersionAndGetRepo(ref, version)
|
|
|
|
if r != nil {
|
|
|
|
if r != nil {
|
|
|
|
return u, r.Client, err
|
|
|
|
return u, r.Client, err
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -165,16 +152,22 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Same as the ResolveChartVersion method, but returns the chart repositoryy.
|
|
|
|
// Same as the ResolveChartVersion method, but returns the chart repositoryy.
|
|
|
|
func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*url.URL, *repo.ChartRepository, error) {
|
|
|
|
func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*url.URL, *repo.ChartRepository, *getter.HttpGetter, error) {
|
|
|
|
u, err := url.Parse(ref)
|
|
|
|
u, err := url.Parse(ref)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("invalid chart URL format: %s", ref)
|
|
|
|
return nil, nil, nil, fmt.Errorf("invalid chart URL format: %s", ref)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rf, err := repo.LoadRepositoriesFile(c.HelmHome.RepositoryFile())
|
|
|
|
rf, err := repo.LoadRepositoriesFile(c.HelmHome.RepositoryFile())
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, nil, err
|
|
|
|
return u, nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
g, err := getter.NewHTTPGetter(ref, "", "", "")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return u, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g.SetCredentials(c.getRepoCredentials(nil))
|
|
|
|
|
|
|
|
|
|
|
|
if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 {
|
|
|
|
if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 {
|
|
|
|
// In this case, we have to find the parent repo that contains this chart
|
|
|
|
// In this case, we have to find the parent repo that contains this chart
|
|
|
@ -182,81 +175,101 @@ func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*u
|
|
|
|
// through each repo cache file and finding a matching URL. But basically
|
|
|
|
// through each repo cache file and finding a matching URL. But basically
|
|
|
|
// we want to find the repo in case we have special SSL cert config
|
|
|
|
// we want to find the repo in case we have special SSL cert config
|
|
|
|
// for that repo.
|
|
|
|
// for that repo.
|
|
|
|
|
|
|
|
|
|
|
|
rc, err := c.scanReposForURL(ref, rf)
|
|
|
|
rc, err := c.scanReposForURL(ref, rf)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
// If there is no special config, return the default HTTP client and
|
|
|
|
// If there is no special config, return the default HTTP client and
|
|
|
|
// swallow the error.
|
|
|
|
// swallow the error.
|
|
|
|
if err == ErrNoOwnerRepo {
|
|
|
|
if err == ErrNoOwnerRepo {
|
|
|
|
getterConstructor, err := c.Getters.ByScheme(u.Scheme)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return u, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &repo.ChartRepository{}
|
|
|
|
r := &repo.ChartRepository{}
|
|
|
|
r.Client, err = getterConstructor(ref, "", "", "")
|
|
|
|
r.Client = g
|
|
|
|
return u, r, err
|
|
|
|
g.SetCredentials(c.getRepoCredentials(r))
|
|
|
|
|
|
|
|
return u, r, g, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return u, nil, err
|
|
|
|
return u, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r, err := repo.NewChartRepository(rc, c.Getters)
|
|
|
|
r, err := repo.NewChartRepository(rc, c.Getters)
|
|
|
|
// If we get here, we don't need to go through the next phase of looking
|
|
|
|
// If we get here, we don't need to go through the next phase of looking
|
|
|
|
// up the URL. We have it already. So we just return.
|
|
|
|
// up the URL. We have it already. So we just return.
|
|
|
|
return u, r, err
|
|
|
|
return u, r, g, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// See if it's of the form: repo/path_to_chart
|
|
|
|
// See if it's of the form: repo/path_to_chart
|
|
|
|
p := strings.SplitN(u.Path, "/", 2)
|
|
|
|
p := strings.SplitN(u.Path, "/", 2)
|
|
|
|
if len(p) < 2 {
|
|
|
|
if len(p) < 2 {
|
|
|
|
return u, nil, fmt.Errorf("Non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
|
|
|
|
return u, nil, nil, fmt.Errorf("Non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
repoName := p[0]
|
|
|
|
repoName := p[0]
|
|
|
|
chartName := p[1]
|
|
|
|
chartName := p[1]
|
|
|
|
rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories)
|
|
|
|
rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, nil, err
|
|
|
|
return u, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
r, err := repo.NewChartRepository(rc, c.Getters)
|
|
|
|
r, err := repo.NewChartRepository(rc, c.Getters)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, nil, err
|
|
|
|
return u, nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Next, we need to load the index, and actually look up the chart.
|
|
|
|
// Next, we need to load the index, and actually look up the chart.
|
|
|
|
i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name))
|
|
|
|
i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name))
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, r, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err)
|
|
|
|
return u, r, g, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cv, err := i.Get(chartName, version)
|
|
|
|
cv, err := i.Get(chartName, version)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, r, fmt.Errorf("chart %q matching %s not found in %s index. (try 'helm repo update'). %s", chartName, version, r.Config.Name, err)
|
|
|
|
return u, r, g, fmt.Errorf("chart %q matching %s not found in %s index. (try 'helm repo update'). %s", chartName, version, r.Config.Name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(cv.URLs) == 0 {
|
|
|
|
if len(cv.URLs) == 0 {
|
|
|
|
return u, r, fmt.Errorf("chart %q has no downloadable URLs", ref)
|
|
|
|
return u, r, g, fmt.Errorf("chart %q has no downloadable URLs", ref)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Seems that picking first URL is not fully correct
|
|
|
|
// TODO: Seems that picking first URL is not fully correct
|
|
|
|
u, err = url.Parse(cv.URLs[0])
|
|
|
|
u, err = url.Parse(cv.URLs[0])
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return u, r, fmt.Errorf("invalid chart URL format: %s", ref)
|
|
|
|
return u, r, g, fmt.Errorf("invalid chart URL format: %s", ref)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the URL is relative (no scheme), prepend the chart repo's base URL
|
|
|
|
// If the URL is relative (no scheme), prepend the chart repo's base URL
|
|
|
|
if !u.IsAbs() {
|
|
|
|
if !u.IsAbs() {
|
|
|
|
repoURL, err := url.Parse(rc.URL)
|
|
|
|
repoURL, err := url.Parse(rc.URL)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return repoURL, r, err
|
|
|
|
return repoURL, r, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
q := repoURL.Query()
|
|
|
|
q := repoURL.Query()
|
|
|
|
// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
|
|
|
|
// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
|
|
|
|
repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
|
|
|
|
repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
|
|
|
|
u = repoURL.ResolveReference(u)
|
|
|
|
u = repoURL.ResolveReference(u)
|
|
|
|
u.RawQuery = q.Encode()
|
|
|
|
u.RawQuery = q.Encode()
|
|
|
|
return u, r, err
|
|
|
|
g, err := getter.NewHTTPGetter(rc.URL, "", "", "")
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return repoURL, r, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
g.SetCredentials(c.getRepoCredentials(r))
|
|
|
|
|
|
|
|
return u, r, g, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return u, r, nil
|
|
|
|
return u, r, g, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If this ChartDownloader is not configured to use credentials, and the chart repository sent as an argument is,
|
|
|
|
|
|
|
|
// then the repository's configured credentials are returned.
|
|
|
|
|
|
|
|
// Else, this ChartDownloader's credentials are returned.
|
|
|
|
|
|
|
|
func (c *ChartDownloader) getRepoCredentials(r *repo.ChartRepository) (username, password string) {
|
|
|
|
|
|
|
|
username = c.Username
|
|
|
|
|
|
|
|
password = c.Password
|
|
|
|
|
|
|
|
if r != nil && r.Config != nil {
|
|
|
|
|
|
|
|
if username == "" {
|
|
|
|
|
|
|
|
username = r.Config.Username
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if password == "" {
|
|
|
|
|
|
|
|
password = r.Config.Password
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
|
|
|
|
// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
|
|
|
|