diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 5b0288632..d1bc52c4e 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -56,6 +56,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download") f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") + f.StringVar(&c.Token, "token", "", "chart repository bearer token where to locate the requested chart") } // bindOutputFlag will add the output flag to the given command and bind the diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 1b09ece83..5a3af456b 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -61,6 +61,8 @@ type repoAddOptions struct { // Deprecated, but cannot be removed until Helm 4 deprecatedNoUpdate bool + + tokenFromStdin bool } func newRepoAddCmd(out io.Writer) *cobra.Command { @@ -76,7 +78,6 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { o.url = args[1] o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache - return o.run(out) }, } @@ -91,6 +92,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the repository") f.BoolVar(&o.allowDeprecatedRepos, "allow-deprecated-repos", false, "by default, this command will not allow adding official repos that have been permanently deleted. This disables that behavior") + f.BoolVar(&o.tokenFromStdin, "token-stdin", false, "read chart repository bearer token from stdin") return cmd } @@ -144,6 +146,16 @@ func (o *repoAddOptions) run(out io.Writer) error { o.password = string(password) } + var token string + if o.tokenFromStdin { + tokenStdin, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return err + } + token = strings.TrimSuffix(string(tokenStdin), "\n") + token = strings.TrimSuffix(token, "\r") + } + c := repo.Entry{ Name: o.name, URL: o.url, @@ -153,6 +165,7 @@ func (o *repoAddOptions) run(out io.Writer) error { KeyFile: o.keyFile, CAFile: o.caFile, InsecureSkipTLSverify: o.insecureSkipTLSverify, + Token: token, } // If the repo exists do one of two things: diff --git a/pkg/action/install.go b/pkg/action/install.go index 4de0b64e6..fb3a3a0a4 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -115,6 +115,7 @@ type ChartPathOptions struct { Username string // --username Verify bool // --verify Version string // --version + Token string // --token } // NewInstall creates a new Install object with the given configuration. @@ -652,6 +653,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( getter.WithBasicAuth(c.Username, c.Password), getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), + getter.WithBearerToken(c.Token), }, RepositoryConfig: settings.RepositoryConfig, RepositoryCache: settings.RepositoryCache, @@ -660,8 +662,15 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( dl.Verify = downloader.VerifyAlways } if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, getter.All(settings)) + chartURL, err := repo.FindChartInRepoURLWithAuth( + getter.All(settings), + repo.WithRepoURL(c.RepoURL), + repo.WithChartInfo(name, version), + repo.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), + repo.WithBasicAuth(c.Username, c.Password), + repo.WithBearerToken(c.Token), + repo.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), + ) if err != nil { return "", err } diff --git a/pkg/action/pull.go b/pkg/action/pull.go index 220ca11b2..41a5af8a0 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -65,6 +65,7 @@ func (p *Pull) Run(chartRef string) (string, error) { getter.WithBasicAuth(p.Username, p.Password), getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), + getter.WithBearerToken(p.Token), }, RepositoryConfig: p.Settings.RepositoryConfig, RepositoryCache: p.Settings.RepositoryCache, @@ -89,7 +90,15 @@ func (p *Pull) Run(chartRef string) (string, error) { } if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, getter.All(p.Settings)) + chartURL, err := repo.FindChartInRepoURLWithAuth( + getter.All(p.Settings), + repo.WithRepoURL(p.RepoURL), + repo.WithChartInfo(chartRef, p.Version), + repo.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), + repo.WithBasicAuth(p.Username, p.Password), + repo.WithBearerToken(p.Token), + repo.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), + ) if err != nil { return out.String(), err } diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index ef26f3348..099da42ce 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -191,6 +191,13 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er getter.WithBasicAuth(rc.Username, rc.Password), ) } + if rc.Token != "" { + c.Options = append( + c.Options, + getter.WithBearerToken(rc.Token), + ) + } + return u, nil } @@ -220,6 +227,9 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er if r.Config.Username != "" && r.Config.Password != "" { c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) } + if r.Config.Token != "" { + c.Options = append(c.Options, getter.WithBearerToken(r.Config.Token)) + } } // Next, we need to load the index, and actually look up the chart. diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 334d7aaa1..9b2ce50c4 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -19,6 +19,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "testing" "helm.sh/helm/v3/internal/test/ensure" @@ -323,6 +324,63 @@ func TestDownloadTo_VerifyLater(t *testing.T) { } } +func TestDownloadTo_Token(t *testing.T) { + // Set up a fake repo with bearer auth enabled + srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") + srv.Stop() + if err != nil { + t.Fatal(err) + } + srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := strings.Split(r.Header.Get("Authorization"), "Bearer ") + if len(token) != 2 || strings.TrimSpace(token[1]) != "JWT" { + t.Errorf("Expected request to use bearer token and for token == 'JWT' got '%s'", token) + } + })) + srv.Start() + defer srv.Stop() + if err := srv.CreateIndex(); err != nil { + t.Fatal(err) + } + + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + + c := ChartDownloader{ + Out: os.Stderr, + Verify: VerifyAlways, + Keyring: "testdata/helm-test-key.pub", + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), + Options: []getter.Option{ + getter.WithBearerToken("JWT"), + }, + } + cname := "/signtest-0.1.0.tgz" + dest := srv.Root() + where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) + if err != nil { + t.Fatal(err) + } + + if expect := filepath.Join(dest, cname); where != expect { + t.Errorf("Expected download to %s, got %s", expect, where) + } + + if v.FileHash == "" { + t.Error("File hash was empty, but verification is required.") + } + + if _, err := os.Stat(filepath.Join(dest, cname)); err != nil { + t.Error(err) + } +} + func TestScanReposForURL(t *testing.T) { c := ChartDownloader{ Out: os.Stderr, diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 145244082..3821d5253 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -307,7 +307,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { // Any failure to resolve/download a chart should fail: // https://github.com/helm/helm/issues/1439 - churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) + churl, username, password, token, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) if err != nil { saveError = errors.Wrapf(err, "could not find %s", churl) break @@ -329,6 +329,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { Getters: m.Getters, Options: []getter.Option{ getter.WithBasicAuth(username, password), + getter.WithBearerToken(token), }, } @@ -647,7 +648,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { // repoURL is the repository to search // // 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, err error) { +func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password, token string, err error) { for _, cr := range repos { if urlutil.Equal(repoURL, cr.Config.URL) { var entry repo.ChartVersions @@ -666,6 +667,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* } username = cr.Config.Username password = cr.Config.Password + token = cr.Config.Token return } } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index fc8d9abb2..f72524154 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -81,7 +81,7 @@ func TestFindChartURL(t *testing.T) { version := "0.1.0" repoURL := "http://example.com/charts" - churl, username, password, err := m.findChartURL(name, version, repoURL, repos) + churl, username, password, token, err := m.findChartURL(name, version, repoURL, repos) if err != nil { t.Fatal(err) } @@ -94,6 +94,9 @@ func TestFindChartURL(t *testing.T) { if password != "" { t.Errorf("Unexpected password %q", password) } + if token != "" { + t.Errorf("Unexpected token %q", token) + } } func TestGetRepoNames(t *testing.T) { diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml index 430865269..ebc0e3b3b 100644 --- a/pkg/downloader/testdata/repositories.yaml +++ b/pkg/downloader/testdata/repositories.yaml @@ -21,3 +21,6 @@ repositories: certFile: "cert" keyFile: "key" caFile: "ca" + - name: testing-token + url: "http://example.com" + token: "JWT" diff --git a/pkg/downloader/testdata/repository/testing-token-index.yaml b/pkg/downloader/testdata/repository/testing-token-index.yaml new file mode 100644 index 000000000..dd936ab36 --- /dev/null +++ b/pkg/downloader/testdata/repository/testing-token-index.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +entries: + foo: + - name: foo + description: Foo Chart + home: https://helm.sh/helm + keywords: [] + maintainers: [] + sources: + - https://github.com/helm/charts + urls: + - http://example.com/foo-1.2.3.tgz + version: 1.2.3 + checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index 8ee08cb7f..3d38ba3fb 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -38,6 +38,7 @@ type options struct { password string userAgent string timeout time.Duration + token string } // Option allows specifying various settings configurable by the user for overriding the defaults @@ -83,6 +84,13 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option { } } +// WithBearerToken sets the request's Authorization header to use the provided token +func WithBearerToken(token string) Option { + return func(opts *options) { + opts.token = token + } +} + // WithTimeout sets the timeout for requests func WithTimeout(timeout time.Duration) Option { return func(opts *options) { diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index bd60629ae..6369434f5 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -18,6 +18,7 @@ package getter import ( "bytes" "crypto/tls" + "fmt" "io" "net/http" @@ -60,6 +61,10 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) { req.SetBasicAuth(g.opts.username, g.opts.password) } + if g.opts.token != "" { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", g.opts.token)) + } + client, err := g.httpClient() if err != nil { return nil, err diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 3aab22abe..9af45d412 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -58,6 +58,7 @@ func TestHTTPGetter(t *testing.T) { WithTLSClientConfig(pub, priv, ca), WithInsecureSkipVerifyTLS(insecure), WithTimeout(timeout), + WithBearerToken("JWT"), ) if err != nil { t.Fatal(err) @@ -100,6 +101,10 @@ func TestHTTPGetter(t *testing.T) { t.Errorf("Expected NewHTTPGetter to contain %s as Timeout flag, got %s", timeout, hg.opts.timeout) } + if hg.opts.token != "JWT" { + t.Errorf("Expected NewHTTPGetter to contain %s as the token, got %s", "JWT", hg.opts.token) + } + // Test if setting insecureSkipVerifyTLS is being passed to the ops insecure = true @@ -259,6 +264,64 @@ func TestDownloadInsecureSkipTLSVerify(t *testing.T) { } +func TestDownloadToken(t *testing.T) { + expect := "Call me Ishmael" + expectedUserAgent := "I am Groot" + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defaultUserAgent := "Helm/" + strings.TrimPrefix(version.GetVersion(), "v") + if r.UserAgent() != defaultUserAgent { + t.Errorf("Expected '%s', got '%s'", defaultUserAgent, r.UserAgent()) + } + fmt.Fprint(w, expect) + })) + defer srv.Close() + + g, err := All(cli.New()).ByScheme("http") + if err != nil { + t.Fatal(err) + } + got, err := g.Get(srv.URL, WithURL(srv.URL)) + if err != nil { + t.Fatal(err) + } + + if got.String() != expect { + t.Errorf("Expected %q, got %q", expect, got.String()) + } + + // test with http server + tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := strings.Split(r.Header.Get("Authorization"), "Bearer ") + if len(token) != 2 || strings.TrimSpace(token[1]) != "JWT" { + t.Errorf("Expected request to use bearer token and for token == 'JWT' got '%s'", token) + } + if r.UserAgent() != expectedUserAgent { + t.Errorf("Expected '%s', got '%s'", expectedUserAgent, r.UserAgent()) + } + fmt.Fprint(w, expect) + })) + + defer tokenSrv.Close() + + u, _ := url.ParseRequestURI(tokenSrv.URL) + httpgetter, err := NewHTTPGetter( + WithURL(u.String()), + WithBearerToken("JWT"), + WithUserAgent(expectedUserAgent), + ) + if err != nil { + t.Fatal(err) + } + got, err = httpgetter.Get(u.String()) + if err != nil { + t.Fatal(err) + } + + if got.String() != expect { + t.Errorf("Expected %q, got %q", expect, got.String()) + } +} + func TestHTTPGetterTarDownload(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { f, _ := os.Open("testdata/empty-0.0.1.tgz") diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 92892bb85..9f9830962 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -48,6 +48,7 @@ type Entry struct { KeyFile string `json:"keyFile"` CAFile string `json:"caFile"` InsecureSkipTLSverify bool `json:"insecure_skip_tls_verify"` + Token string `json:"token"` } // ChartRepository represents a chart repository @@ -59,6 +60,70 @@ type ChartRepository struct { CachePath string } +// options are generic parameters to be provided to find chart. +type options struct { + chartName string + chartVersion string + repoURL string + certFile string + keyFile string + caFile string + username string + password string + token string + insecureSkipTLSverify bool +} + +// Option allows specifying various settings configurable by the user for overriding the defaults +// used when performing find chart. +type Option func(*options) + +// WithChartInfo informs the chart info. +func WithChartInfo(chartName, chartVersion string) Option { + return func(opts *options) { + opts.chartName = chartName + opts.chartVersion = chartVersion + } +} + +// WithRepoURL informs the repo url that will be used when find chart. +func WithRepoURL(url string) Option { + return func(opts *options) { + opts.repoURL = url + } +} + +// WithBasicAuth sets the request's Authorization header to use the provided credentials +func WithBasicAuth(username, password string) Option { + return func(opts *options) { + opts.username = username + opts.password = password + } +} + +// WithTLSClientConfig sets the client auth with the provided credentials. +func WithTLSClientConfig(certFile, keyFile, caFile string) Option { + return func(opts *options) { + opts.certFile = certFile + opts.keyFile = keyFile + opts.caFile = caFile + } +} + +// WithBearerToken sets the request's Authorization header to use the provided token +func WithBearerToken(token string) Option { + return func(opts *options) { + opts.token = token + } +} + +// WithInsecureSkipVerifyTLS determines if a TLS Certificate will be checked +func WithInsecureSkipVerifyTLS(insecureSkipTLSverify bool) Option { + return func(opts *options) { + opts.insecureSkipTLSverify = insecureSkipTLSverify + } +} + // NewChartRepository constructs ChartRepository func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, error) { u, err := url.Parse(cfg.URL) @@ -127,6 +192,7 @@ func (r *ChartRepository) DownloadIndexFile() (string, error) { getter.WithInsecureSkipVerifyTLS(r.Config.InsecureSkipTLSverify), getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile), getter.WithBasicAuth(r.Config.Username, r.Config.Password), + getter.WithBearerToken(r.Config.Token), ) if err != nil { return "", err @@ -198,14 +264,33 @@ func (r *ChartRepository) generateIndex() error { // FindChartInRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { - return FindChartInAuthRepoURL(repoURL, "", "", chartName, chartVersion, certFile, keyFile, caFile, getters) + return FindChartInRepoURLWithAuth( + getters, + WithRepoURL(repoURL), + WithChartInfo(chartName, chartVersion), + WithTLSClientConfig(certFile, keyFile, caFile), + ) } // FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories, like FindChartInRepoURL, // but it also receives credentials for the chart repository. +// Deprecated: this function is deprecated and will be removed in Helm 4, please use FindChartInRepoURLWithAuth instead. func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { - return FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, getters) + return findChartInAuthRepoURL( + getters, + WithRepoURL(repoURL), + WithChartInfo(chartName, chartVersion), + WithBasicAuth(username, password), + WithTLSClientConfig(certFile, keyFile, caFile), + ) +} + +// FindChartInRepoURLWithAuth finds chart in chart repository pointed by repoURL +// without adding repo to repositories, like FindChartInRepoURL, +// but it also receives credentials for the chart repository. +func FindChartInRepoURLWithAuth(getters getter.Providers, opts ...Option) (string, error) { + return findChartInAuthRepoURL(getters, opts...) } // FindChartInAuthAndTLSRepoURL finds chart in chart repository pointed by repoURL @@ -213,21 +298,37 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion // but it also receives credentials and TLS verify flag for the chart repository. // TODO Helm 4, FindChartInAuthAndTLSRepoURL should be integrated into FindChartInAuthRepoURL. func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify bool, getters getter.Providers) (string, error) { + return findChartInAuthRepoURL( + getters, + WithRepoURL(repoURL), + WithChartInfo(chartName, chartVersion), + WithBasicAuth(username, password), + WithTLSClientConfig(certFile, keyFile, caFile), + WithInsecureSkipVerifyTLS(insecureSkipTLSverify), + ) +} + +func findChartInAuthRepoURL(getters getter.Providers, opts ...Option) (string, error) { // Download and write the index file to a temporary location buf := make([]byte, 20) rand.Read(buf) name := strings.ReplaceAll(base64.StdEncoding.EncodeToString(buf), "/", "-") + repoOpts := options{} + for _, opt := range opts { + opt(&repoOpts) + } c := Entry{ - URL: repoURL, - Username: username, - Password: password, - CertFile: certFile, - KeyFile: keyFile, - CAFile: caFile, Name: name, - InsecureSkipTLSverify: insecureSkipTLSverify, + URL: repoOpts.repoURL, + Username: repoOpts.username, + Password: repoOpts.password, + CertFile: repoOpts.certFile, + KeyFile: repoOpts.keyFile, + CAFile: repoOpts.caFile, + Token: repoOpts.token, + InsecureSkipTLSverify: repoOpts.insecureSkipTLSverify, } r, err := NewChartRepository(&c, getters) if err != nil { @@ -235,7 +336,7 @@ func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartV } idx, err := r.DownloadIndexFile() if err != nil { - return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL) + return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoOpts.repoURL) } // Read the index file for the repository to get chart information and return chart URL @@ -244,13 +345,13 @@ func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartV return "", err } - errMsg := fmt.Sprintf("chart %q", chartName) - if chartVersion != "" { - errMsg = fmt.Sprintf("%s version %q", errMsg, chartVersion) + errMsg := fmt.Sprintf("chart %q", repoOpts.chartName) + if repoOpts.chartVersion != "" { + errMsg = fmt.Sprintf("%s version %q", errMsg, repoOpts.chartVersion) } - cv, err := repoIndex.Get(chartName, chartVersion) + cv, err := repoIndex.Get(repoOpts.chartName, repoOpts.chartVersion) if err != nil { - return "", errors.Errorf("%s not found in %s repository", errMsg, repoURL) + return "", errors.Errorf("%s not found in %s repository", errMsg, repoOpts.repoURL) } if len(cv.URLs) == 0 { @@ -259,7 +360,7 @@ func FindChartInAuthAndTLSRepoURL(repoURL, username, password, chartName, chartV chartURL := cv.URLs[0] - absoluteChartURL, err := ResolveReferenceURL(repoURL, chartURL) + absoluteChartURL, err := ResolveReferenceURL(repoOpts.repoURL, chartURL) if err != nil { return "", errors.Wrap(err, "failed to make chart URL absolute") }