From d78b81e2951554fcf9e10436f4001dac0efb92ae Mon Sep 17 00:00:00 2001 From: lemonli Date: Wed, 8 Jul 2020 16:18:01 +0800 Subject: [PATCH] feat(helm): Support Bearer token auth when get chart Signed-off-by: lemonli --- cmd/helm/flags.go | 2 +- cmd/helm/repo_add.go | 16 +- pkg/action/install.go | 17 ++- pkg/action/pull.go | 11 +- pkg/downloader/chart_downloader.go | 10 ++ pkg/downloader/chart_downloader_test.go | 58 +++++++ pkg/downloader/manager.go | 18 +-- pkg/downloader/manager_test.go | 34 +---- pkg/downloader/testdata/repositories.yaml | 6 +- .../repository/testing-token-index.yaml | 14 ++ pkg/getter/getter.go | 9 +- pkg/getter/httpgetter.go | 5 + pkg/getter/httpgetter_test.go | 64 +++++++- pkg/repo/chartrepo.go | 141 ++++++++++++++---- 14 files changed, 317 insertions(+), 88 deletions(-) create mode 100644 pkg/downloader/testdata/repository/testing-token-index.yaml diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index 0cc0564e2..001b9a32c 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -60,7 +60,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.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains") + 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 e13df7ad8..e5c7cd6da 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -63,6 +63,8 @@ type repoAddOptions struct { // Deprecated, but cannot be removed until Helm 4 deprecatedNoUpdate bool + + tokenFromStdin bool } func newRepoAddCmd(out io.Writer) *cobra.Command { @@ -78,7 +80,6 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { o.url = args[1] o.repoFile = settings.RepositoryConfig o.repoCache = settings.RepositoryCache - return o.run(out) }, } @@ -94,7 +95,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.passCredentialsAll, "pass-credentials", false, "pass credentials to all domains") + f.BoolVar(&o.tokenFromStdin, "token-stdin", false, "read chart repository bearer token from stdin") return cmd } @@ -165,6 +166,16 @@ func (o *repoAddOptions) run(out io.Writer) error { } } + 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, @@ -175,6 +186,7 @@ func (o *repoAddOptions) run(out io.Writer) error { KeyFile: o.keyFile, CAFile: o.caFile, InsecureSkipTLSverify: o.insecureSkipTLSverify, + Token: token, } // Check if the repo name is legal diff --git a/pkg/action/install.go b/pkg/action/install.go index 3872ed5c9..c98cf7bac 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -117,10 +117,7 @@ type ChartPathOptions struct { Username string // --username Verify bool // --verify Version string // --version - - // registryClient provides a registry client but is not added with - // options from a flag - registryClient *registry.Client + Token string // --token } // NewInstall creates a new Install object with the given configuration. @@ -706,6 +703,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( getter.WithPassCredentialsAll(c.PassCredentialsAll), getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile), getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify), + getter.WithBearerToken(c.Token), }, RepositoryConfig: settings.RepositoryConfig, RepositoryCache: settings.RepositoryCache, @@ -716,8 +714,15 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) ( dl.Verify = downloader.VerifyAlways } if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, 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 b4018869e..d42e6abe4 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -86,6 +86,7 @@ func (p *Pull) Run(chartRef string) (string, error) { getter.WithPassCredentialsAll(p.PassCredentialsAll), getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), getter.WithInsecureSkipVerifyTLS(p.InsecureSkipTLSverify), + getter.WithBearerToken(p.Token), }, RegistryClient: p.cfg.RegistryClient, RepositoryConfig: p.Settings.RepositoryConfig, @@ -116,7 +117,15 @@ func (p *Pull) Run(chartRef string) (string, error) { } if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, 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 3feb5b702..6fad54ae8 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -239,6 +239,13 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er getter.WithPassCredentialsAll(rc.PassCredentialsAll), ) } + if rc.Token != "" { + c.Options = append( + c.Options, + getter.WithBearerToken(rc.Token), + ) + } + return u, nil } @@ -275,6 +282,9 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er getter.WithPassCredentialsAll(r.Config.PassCredentialsAll), ) } + 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 f70a56422..630432fbc 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -18,6 +18,7 @@ package downloader import ( "os" "path/filepath" + "strings" "testing" "helm.sh/helm/v3/internal/test/ensure" @@ -311,6 +312,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 18b28dde1..29de737bc 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -313,7 +313,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, insecureskiptlsverify, passcredentialsall, caFile, certFile, keyFile, 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 @@ -336,9 +336,7 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { Getters: m.Getters, Options: []getter.Option{ getter.WithBasicAuth(username, password), - getter.WithPassCredentialsAll(passcredentialsall), - getter.WithInsecureSkipVerifyTLS(insecureskiptlsverify), - getter.WithTLSClientConfig(certFile, keyFile, caFile), + getter.WithBearerToken(token), }, } @@ -703,11 +701,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, insecureskiptlsverify, passcredentialsall bool, caFile, certFile, keyFile string, err error) { - if registry.IsOCI(repoURL) { - return fmt.Sprintf("%s/%s:%s", repoURL, name, version), "", "", false, false, "", "", "", nil - } - +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) { @@ -727,11 +721,7 @@ func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]* } username = cr.Config.Username password = cr.Config.Password - passcredentialsall = cr.Config.PassCredentialsAll - insecureskiptlsverify = cr.Config.InsecureSkipTLSverify - caFile = cr.Config.CAFile - certFile = cr.Config.CertFile - keyFile = cr.Config.KeyFile + token = cr.Config.Token return } } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index f7ab1a568..4dfe22198 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -84,7 +84,7 @@ func TestFindChartURL(t *testing.T) { version := "0.1.0" repoURL := "http://example.com/charts" - churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err := m.findChartURL(name, version, repoURL, repos) + churl, username, password, token, err := m.findChartURL(name, version, repoURL, repos) if err != nil { t.Fatal(err) } @@ -98,36 +98,8 @@ func TestFindChartURL(t *testing.T) { if password != "" { t.Errorf("Unexpected password %q", password) } - if passcredentialsall != false { - t.Errorf("Unexpected passcredentialsall %t", passcredentialsall) - } - if insecureSkipTLSVerify { - t.Errorf("Unexpected insecureSkipTLSVerify %t", insecureSkipTLSVerify) - } - - name = "tlsfoo" - version = "1.2.3" - repoURL = "https://example-https-insecureskiptlsverify.com" - - churl, username, password, insecureSkipTLSVerify, passcredentialsall, _, _, _, err = m.findChartURL(name, version, repoURL, repos) - if err != nil { - t.Fatal(err) - } - - if !insecureSkipTLSVerify { - t.Errorf("Unexpected insecureSkipTLSVerify %t", insecureSkipTLSVerify) - } - if churl != "https://example.com/tlsfoo-1.2.3.tgz" { - t.Errorf("Unexpected URL %q", churl) - } - if username != "" { - t.Errorf("Unexpected username %q", username) - } - if password != "" { - t.Errorf("Unexpected password %q", password) - } - if passcredentialsall != false { - t.Errorf("Unexpected passcredentialsall %t", passcredentialsall) + if token != "" { + t.Errorf("Unexpected token %q", token) } } diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml index cfd618745..ebc0e3b3b 100644 --- a/pkg/downloader/testdata/repositories.yaml +++ b/pkg/downloader/testdata/repositories.yaml @@ -21,6 +21,6 @@ repositories: certFile: "cert" keyFile: "key" caFile: "ca" - - name: testing-https-insecureskip-tls-verify - url: "https://example-https-insecureskiptlsverify.com" - insecure_skip_tls_verify: true + - 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 653b032fe..de5a8f643 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -44,7 +44,7 @@ type options struct { version string registryClient *registry.Client timeout time.Duration - transport *http.Transport + token string } // Option allows specifying various settings configurable by the user for overriding the defaults @@ -96,6 +96,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 6fe1aa71f..e72066000 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" "net/url" @@ -78,6 +79,10 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) { } } + 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 140b2c714..081a7f42b 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -60,7 +60,7 @@ func TestHTTPGetter(t *testing.T) { WithTLSClientConfig(pub, priv, ca), WithInsecureSkipVerifyTLS(insecure), WithTimeout(timeout), - WithTransport(transport), + WithBearerToken("JWT"), ) if err != nil { t.Fatal(err) @@ -107,8 +107,8 @@ func TestHTTPGetter(t *testing.T) { t.Errorf("Expected NewHTTPGetter to contain %s as Timeout flag, got %s", timeout, hg.opts.timeout) } - if hg.opts.transport != transport { - t.Errorf("Expected NewHTTPGetter to contain %p as Transport, got %p", transport, hg.opts.transport) + 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 @@ -363,6 +363,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 956997cc9..6b4c75ee1 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -48,7 +48,7 @@ type Entry struct { KeyFile string `json:"keyFile"` CAFile string `json:"caFile"` InsecureSkipTLSverify bool `json:"insecure_skip_tls_verify"` - PassCredentialsAll bool `json:"pass_credentials_all"` + Token string `json:"token"` } // ChartRepository represents a chart repository @@ -60,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) @@ -130,7 +194,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.WithPassCredentialsAll(r.Config.PassCredentialsAll), + getter.WithBearerToken(r.Config.Token), ) if err != nil { return "", err @@ -204,14 +268,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 @@ -219,31 +302,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 FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile, false, false, getters) + return findChartInAuthRepoURL( + getters, + WithRepoURL(repoURL), + WithChartInfo(chartName, chartVersion), + WithBasicAuth(username, password), + WithTLSClientConfig(certFile, keyFile, caFile), + WithInsecureSkipVerifyTLS(insecureSkipTLSverify), + ) } -// FindChartInAuthAndTLSAndPassRepoURL finds chart in chart repository pointed by repoURL -// without adding repo to repositories, like FindChartInRepoURL, -// but it also receives credentials, TLS verify flag, and if credentials should -// be passed on to other domains. -// TODO Helm 4, FindChartInAuthAndTLSAndPassRepoURL should be integrated into FindChartInAuthRepoURL. -func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, insecureSkipTLSverify, passCredentialsAll bool, getters getter.Providers) (string, error) { +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, - PassCredentialsAll: passCredentialsAll, - 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 { @@ -251,7 +340,7 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, } 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 @@ -260,13 +349,13 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, 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 { @@ -275,7 +364,7 @@ func FindChartInAuthAndTLSAndPassRepoURL(repoURL, username, password, chartName, 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") }