From b3b5366d55b70e2d5d5dba062d418a3ea99eb25d Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Mon, 27 Nov 2017 19:01:01 +0200 Subject: [PATCH 01/14] Authentication support for remote charts repositories. --- cmd/helm/fetch.go | 6 +++-- cmd/helm/inspect.go | 20 ++++++++++++--- cmd/helm/install.go | 14 +++++++---- cmd/helm/repo_add.go | 28 ++++++++++++++++++--- cmd/helm/upgrade.go | 6 ++++- pkg/downloader/chart_downloader.go | 4 +-- pkg/downloader/chart_downloader_test.go | 4 +-- pkg/downloader/manager.go | 28 +++++++++++++-------- pkg/downloader/manager_test.go | 9 +++++-- pkg/getter/getter.go | 2 ++ pkg/getter/httpgetter.go | 13 ++++++++++ pkg/getter/plugingetter.go | 4 +++ pkg/plugin/installer/http_installer_test.go | 2 ++ pkg/repo/chartrepo.go | 15 +++++++++-- 14 files changed, 122 insertions(+), 33 deletions(-) diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index 9c1f25614..79c526cf7 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -52,6 +52,8 @@ type fetchCmd struct { destdir string version string repoURL string + username string + password string verify bool verifyLater bool @@ -138,14 +140,14 @@ func (f *fetchCmd) run() error { } if f.repoURL != "" { - chartURL, err := repo.FindChartInRepoURL(f.repoURL, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) + chartURL, err := repo.FindChartInRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) if err != nil { return err } f.chartRef = chartURL } - saved, v, err := c.DownloadTo(f.chartRef, f.version, dest) + saved, v, err := c.DownloadTo(f.chartRef, f.username, f.password, f.version, dest) if err != nil { return err } diff --git a/cmd/helm/inspect.go b/cmd/helm/inspect.go index 736f14fce..02eaf9b87 100644 --- a/cmd/helm/inspect.go +++ b/cmd/helm/inspect.go @@ -59,6 +59,8 @@ type inspectCmd struct { out io.Writer version string repoURL string + username string + password string certFile string keyFile string @@ -88,7 +90,7 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err @@ -107,7 +109,7 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err @@ -126,7 +128,7 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err @@ -181,6 +183,18 @@ func newInspectCmd(out io.Writer) *cobra.Command { subCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) } + username := "username" + usernamedesc := "chart repository username where to locate the requested chart" + inspectCommand.Flags().StringVar(&insp.username, username, "", usernamedesc) + valuesSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc) + chartSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc) + + password := "password" + passworddesc := "chart repository password where to locate the requested chart" + inspectCommand.Flags().StringVar(&insp.password, password, "", passworddesc) + valuesSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc) + chartSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc) + certFile := "cert-file" certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle" for _, subCmd := range cmds { diff --git a/cmd/helm/install.go b/cmd/helm/install.go index da1ee52d7..03abfa3e1 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -118,6 +118,8 @@ type installCmd struct { timeout int64 wait bool repoURL string + username string + password string devel bool depUp bool @@ -165,7 +167,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { inst.version = ">0.0.0-0" } - cp, err := locateChartPath(inst.repoURL, args[0], inst.version, inst.verify, inst.keyring, + cp, err := locateChartPath(inst.repoURL, inst.username, inst.password, args[0], inst.version, inst.verify, inst.keyring, inst.certFile, inst.keyFile, inst.caFile) if err != nil { return err @@ -191,6 +193,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&inst.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.StringVar(&inst.repoURL, "repo", "", "chart repository url where to locate the requested chart") + f.StringVar(&inst.username, "username", "", "chart repository username where to locate the requested chart") + f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&inst.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&inst.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&inst.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") @@ -381,8 +385,8 @@ func (i *installCmd) printRelease(rel *release.Release) { // - URL // // If 'verify' is true, this will attempt to also verify the chart. -func locateChartPath(repoURL, name, version string, verify bool, keyring, - certFile, keyFile, caFile string) (string, error) { +func locateChartPath(repoURL, username, password, name, version string, verify bool, keyring, +certFile, keyFile, caFile string) (string, error) { name = strings.TrimSpace(name) version = strings.TrimSpace(version) if fi, err := os.Stat(name); err == nil { @@ -419,7 +423,7 @@ func locateChartPath(repoURL, name, version string, verify bool, keyring, dl.Verify = downloader.VerifyAlways } if repoURL != "" { - chartURL, err := repo.FindChartInRepoURL(repoURL, name, version, + chartURL, err := repo.FindChartInRepoURL(repoURL, username, password, name, version, certFile, keyFile, caFile, getter.All(settings)) if err != nil { return "", err @@ -431,7 +435,7 @@ func locateChartPath(repoURL, name, version string, verify bool, keyring, os.MkdirAll(settings.Home.Archive(), 0744) } - filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive()) + filename, _, err := dl.DownloadTo(name, username, password, version, settings.Home.Archive()) if err == nil { lname, err := filepath.Abs(filename) if err != nil { diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index b172d016c..fcaf32000 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -30,6 +30,8 @@ import ( type repoAddCmd struct { name string url string + username string + password string home helmpath.Home noupdate bool @@ -44,15 +46,31 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { add := &repoAddCmd{out: out} cmd := &cobra.Command{ - Use: "add [flags] [NAME] [URL]", + Use: "add [flags] [NAME] [URL] [USERNAME] [PASSWORD]", Short: "add a chart repository", RunE: func(cmd *cobra.Command, args []string) error { - if err := checkArgsLength(len(args), "name for the chart repository", "the url of the chart repository"); err != nil { + argsDesc := []string { + "name for the chart repository", + "the url of the chart repository", + "the username for the chart repository", + "the password of the chart repository", + } + + if len(args) <= 2 { + if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1]); err != nil { + return err + } + } else + if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1], argsDesc[2], argsDesc[3]); err != nil { return err } add.name = args[0] add.url = args[1] + if len(args) == 4 { + add.username = args[2] + add.password = args[3] + } add.home = settings.Home return add.run() @@ -69,14 +87,14 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { } func (a *repoAddCmd) run() error { - if err := addRepository(a.name, a.url, a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil { + if err := addRepository(a.name, a.url, a.username, a.password , a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil { return err } fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name) return nil } -func addRepository(name, url string, home helmpath.Home, certFile, keyFile, caFile string, noUpdate bool) error { +func addRepository(name, url, username, password string, home helmpath.Home, certFile, keyFile, caFile string, noUpdate bool) error { f, err := repo.LoadRepositoriesFile(home.RepositoryFile()) if err != nil { return err @@ -91,6 +109,8 @@ func addRepository(name, url string, home helmpath.Home, certFile, keyFile, caFi Name: name, Cache: cif, URL: url, + Username: username, + Password: password, CertFile: certFile, KeyFile: keyFile, CAFile: caFile, diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index d08dd62ef..aadb59ac2 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -73,6 +73,8 @@ type upgradeCmd struct { reuseValues bool wait bool repoURL string + username string + password string devel bool certFile string @@ -128,6 +130,8 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored.") f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart") + f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart") + f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart") f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") @@ -139,7 +143,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { } func (u *upgradeCmd) run() error { - chartPath, err := locateChartPath(u.repoURL, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) + chartPath, err := locateChartPath(u.repoURL, u.username, u.password, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) if err != nil { return err } diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index c7610aaf9..38de99010 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -80,13 +80,13 @@ type ChartDownloader struct { // // 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. -func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) { +func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest string) (string, *provenance.Verification, error) { u, g, err := c.ResolveChartVersion(ref, version) if err != nil { return "", nil, err } - data, err := g.Get(u.String()) + data, err := g.GetWithCredentials(u.String(), username, password) if err != nil { return "", nil, err } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 0100772e9..682ceaa2e 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -195,7 +195,7 @@ func TestDownloadTo(t *testing.T) { Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" - where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) + where, v, err := c.DownloadTo(srv.URL()+cname, "", "", "", dest) if err != nil { t.Error(err) return @@ -258,7 +258,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" - where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) + where, _, err := c.DownloadTo(srv.URL()+cname,"", "", "", dest) if err != nil { t.Error(err) return diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 6d4103bae..bac5c4823 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -245,13 +245,13 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { // Any failure to resolve/download a chart should fail: // https://github.com/kubernetes/helm/issues/1439 - churl, err := findChartURL(dep.Name, dep.Version, dep.Repository, repos) + churl, username, password, err := findChartURL(dep.Name, dep.Version, dep.Repository, repos) if err != nil { saveError = fmt.Errorf("could not find %s: %s", churl, err) break } - if _, _, err := dl.DownloadTo(churl, "", destPath); err != nil { + if _, _, err := dl.DownloadTo(churl, username, password, "", destPath); err != nil { saveError = fmt.Errorf("could not download %s: %s", churl, err) break } @@ -476,22 +476,30 @@ 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 findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (string, error) { +func findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) { for _, cr := range repos { if urlutil.Equal(repoURL, cr.Config.URL) { - entry, err := findEntryByName(name, cr) + var entry repo.ChartVersions + entry, err = findEntryByName(name, cr) if err != nil { - return "", err + return } - ve, err := findVersionedEntry(version, entry) + var ve *repo.ChartVersion + ve, err = findVersionedEntry(version, entry) if err != nil { - return "", err + return } - - return normalizeURL(repoURL, ve.URLs[0]) + url, err = normalizeURL(repoURL, ve.URLs[0]) + if err != nil { + return + } + username = cr.Config.Username + password = cr.Config.Password + return } } - return "", fmt.Errorf("chart %s not found in %s", name, repoURL) + err = fmt.Errorf("chart %s not found in %s", name, repoURL) + return } // findEntryByName finds an entry in the chart repository whose name matches the given name. diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 091277689..1ff2a9c17 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -77,14 +77,19 @@ func TestFindChartURL(t *testing.T) { version := "0.1.0" repoURL := "http://example.com/charts" - churl, err := findChartURL(name, version, repoURL, repos) + churl, username, password, err := findChartURL(name, version, repoURL, repos) if err != nil { t.Fatal(err) } if churl != "https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz" { t.Errorf("Unexpected URL %q", churl) } - + if username != "" { + t.Errorf("Unexpected username %q", username) + } + if password != "" { + t.Errorf("Unexpected password %q", password) + } } func TestGetRepoNames(t *testing.T) { diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index ca018884a..de9d59578 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -27,6 +27,8 @@ import ( type Getter interface { //Get file content by url string Get(url string) (*bytes.Buffer, error) + //Get file content by url, username and password strings + GetWithCredentials(href, username, password string) (*bytes.Buffer, error) } // Constructor is the function for every getter which creates a specific instance diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index c86b96458..5748ae9f7 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -34,6 +34,15 @@ type httpGetter struct { //Get performs a Get from repo.Getter and returns the body. func (g *httpGetter) Get(href string) (*bytes.Buffer, error) { + return g.get(href, "", "") +} + +//Get performs a Get from repo.Getter using credentials and returns the body. +func (g *httpGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { + return g.get(href, username, password) +} + +func (g *httpGetter) get(href, username, password string) (*bytes.Buffer, error) { buf := bytes.NewBuffer(nil) // Set a helm specific user agent so that a repo server and metrics can @@ -44,6 +53,10 @@ func (g *httpGetter) Get(href string) (*bytes.Buffer, error) { } req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v")) + if username != "" && password != "" { + req.SetBasicAuth(username, password) + } + resp, err := g.client.Do(req) if err != nil { return buf, err diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go index c747eef7f..edbce131b 100644 --- a/pkg/getter/plugingetter.go +++ b/pkg/getter/plugingetter.go @@ -79,6 +79,10 @@ func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { return buf, nil } +func (p *pluginGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { + return p.Get(href) +} + // newPluginGetter constructs a valid plugin getter func newPluginGetter(command string, settings environment.EnvSettings, name, base string) Constructor { return func(URL, CertFile, KeyFile, CAFile string) (Getter, error) { diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index ca1a71e3e..ca409fdad 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -35,6 +35,8 @@ type TestHTTPGetter struct { func (t *TestHTTPGetter) Get(href string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } +func (t *TestHTTPGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } + // Fake plugin tarball data var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA=" diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index b95a7ae07..a4853ba22 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -29,6 +29,7 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/provenance" + "bytes" ) // Entry represents a collection of parameters for chart repository @@ -36,6 +37,8 @@ type Entry struct { Name string `json:"name"` Cache string `json:"cache"` URL string `json:"url"` + Username string `json:"username"` + Password string `json:"password"` CertFile string `json:"certFile"` KeyFile string `json:"keyFile"` CAFile string `json:"caFile"` @@ -117,7 +120,13 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error { parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" indexURL = parsedURL.String() - resp, err := r.Client.Get(indexURL) + var resp *bytes.Buffer + if r.Config.Username == "" || r.Config.Password == "" { + resp, err = r.Client.Get(indexURL) + } else { + resp, err = r.Client.GetWithCredentials(indexURL, r.Config.Username, r.Config.Password) + } + if err != nil { return err } @@ -185,7 +194,7 @@ 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) { +func FindChartInRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file") @@ -196,6 +205,8 @@ func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caF c := Entry{ URL: repoURL, + Username: username, + Password: password, CertFile: certFile, KeyFile: keyFile, CAFile: caFile, From b3d62f1e5b2eaad878604ec74c765c35ec3bd66c Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Mon, 27 Nov 2017 19:07:07 +0200 Subject: [PATCH 02/14] Authentication support for remote charts repositories. --- pkg/repo/chartrepo.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index a4853ba22..eb0aaaf9e 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -120,12 +120,7 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error { parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" indexURL = parsedURL.String() - var resp *bytes.Buffer - if r.Config.Username == "" || r.Config.Password == "" { - resp, err = r.Client.Get(indexURL) - } else { - resp, err = r.Client.GetWithCredentials(indexURL, r.Config.Username, r.Config.Password) - } + resp, err := r.Client.GetWithCredentials(indexURL, r.Config.Username, r.Config.Password) if err != nil { return err From bfd728030d47a570b169dfc81d942305c1f5997d Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Mon, 27 Nov 2017 19:29:30 +0200 Subject: [PATCH 03/14] Authentication support for remote charts repositories. --- pkg/repo/chartrepo.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index eb0aaaf9e..0c004d1bc 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -29,7 +29,6 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/provenance" - "bytes" ) // Entry represents a collection of parameters for chart repository From 8283dbd15b23ff283b31a85c41d562cafedf4134 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Mon, 27 Nov 2017 22:10:45 +0200 Subject: [PATCH 04/14] Tests fixes. --- cmd/helm/repo_add_test.go | 6 +++--- cmd/helm/repo_remove_test.go | 2 +- pkg/repo/chartrepo_test.go | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 79d0d3c53..157b1cc5b 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -80,7 +80,7 @@ func TestRepoAdd(t *testing.T) { settings.Home = thome - if err := addRepository(testName, ts.URL(), hh, "", "", "", true); err != nil { + if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", true); err != nil { t.Error(err) } @@ -93,11 +93,11 @@ func TestRepoAdd(t *testing.T) { t.Errorf("%s was not successfully inserted into %s", testName, hh.RepositoryFile()) } - if err := addRepository(testName, ts.URL(), hh, "", "", "", false); err != nil { + if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", false); err != nil { t.Errorf("Repository was not updated: %s", err) } - if err := addRepository(testName, ts.URL(), hh, "", "", "", false); err != nil { + if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", false); err != nil { t.Errorf("Duplicate repository name was added") } } diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 63082b42e..174a44495 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -51,7 +51,7 @@ func TestRepoRemove(t *testing.T) { if err := removeRepoLine(b, testName, hh); err == nil { t.Errorf("Expected error removing %s, but did not get one.", testName) } - if err := addRepository(testName, ts.URL(), hh, "", "", "", true); err != nil { + if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", true); err != nil { t.Error(err) } diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index 948ee12d3..fc12fb009 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -221,7 +221,7 @@ func TestFindChartInRepoURL(t *testing.T) { } defer srv.Close() - chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + chartURL, err := FindChartInRepoURL(srv.URL, "", "", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("%s", err) } @@ -229,7 +229,7 @@ func TestFindChartInRepoURL(t *testing.T) { t.Errorf("%s is not the valid URL", chartURL) } - chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + chartURL, err = FindChartInRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("%s", err) } @@ -239,7 +239,7 @@ func TestFindChartInRepoURL(t *testing.T) { } func TestErrorFindChartInRepoURL(t *testing.T) { - _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err := FindChartInRepoURL("http://someserver/something", "", "", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for bad chart URL, but did not get any errors") } @@ -253,7 +253,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { } defer srv.Close() - _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "", "", "nginx1", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") } @@ -261,7 +261,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "", "", "nginx1", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") } @@ -269,7 +269,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "", "", "chartWithNoURL", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for no chart URLs available, but did not get any errors") } From 9697dd54e42491792e4d13e6e905b7456b4344d8 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Mon, 27 Nov 2017 22:49:06 +0200 Subject: [PATCH 05/14] Ran 'go fmt' and 'make docs'. --- cmd/helm/install.go | 2 +- cmd/helm/repo_add.go | 7 +++---- docs/helm/helm_inspect.md | 2 ++ docs/helm/helm_inspect_chart.md | 2 ++ docs/helm/helm_inspect_values.md | 2 ++ docs/helm/helm_install.md | 2 ++ docs/helm/helm_repo_add.md | 2 ++ docs/helm/helm_upgrade.md | 2 ++ pkg/downloader/chart_downloader_test.go | 2 +- pkg/plugin/installer/http_installer_test.go | 4 +++- 10 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 03abfa3e1..74a104514 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -386,7 +386,7 @@ func (i *installCmd) printRelease(rel *release.Release) { // // If 'verify' is true, this will attempt to also verify the chart. func locateChartPath(repoURL, username, password, name, version string, verify bool, keyring, -certFile, keyFile, caFile string) (string, error) { + certFile, keyFile, caFile string) (string, error) { name = strings.TrimSpace(name) version = strings.TrimSpace(version) if fi, err := os.Stat(name); err == nil { diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index fcaf32000..bbf0de9f8 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -49,7 +49,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { Use: "add [flags] [NAME] [URL] [USERNAME] [PASSWORD]", Short: "add a chart repository", RunE: func(cmd *cobra.Command, args []string) error { - argsDesc := []string { + argsDesc := []string{ "name for the chart repository", "the url of the chart repository", "the username for the chart repository", @@ -60,8 +60,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1]); err != nil { return err } - } else - if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1], argsDesc[2], argsDesc[3]); err != nil { + } else if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1], argsDesc[2], argsDesc[3]); err != nil { return err } @@ -87,7 +86,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { } func (a *repoAddCmd) run() error { - if err := addRepository(a.name, a.url, a.username, a.password , a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil { + if err := addRepository(a.name, a.url, a.username, a.password, a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil { return err } fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name) diff --git a/docs/helm/helm_inspect.md b/docs/helm/helm_inspect.md index 9928ed847..e46b3dbf4 100644 --- a/docs/helm/helm_inspect.md +++ b/docs/helm/helm_inspect.md @@ -23,7 +23,9 @@ helm inspect [CHART] --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --key-file string identify HTTPS client using this SSL key file --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --password string chart repository password where to locate the requested chart --repo string chart repository url where to locate the requested chart + --username string chart repository username where to locate the requested chart --verify verify the provenance data for this chart --version string version of the chart. By default, the newest chart is shown ``` diff --git a/docs/helm/helm_inspect_chart.md b/docs/helm/helm_inspect_chart.md index bfa6061c8..cd1328b59 100644 --- a/docs/helm/helm_inspect_chart.md +++ b/docs/helm/helm_inspect_chart.md @@ -21,7 +21,9 @@ helm inspect chart [CHART] --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --key-file string identify HTTPS client using this SSL key file --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --password string chart repository password where to locate the requested chart --repo string chart repository url where to locate the requested chart + --username string chart repository username where to locate the requested chart --verify verify the provenance data for this chart --version string version of the chart. By default, the newest chart is shown ``` diff --git a/docs/helm/helm_inspect_values.md b/docs/helm/helm_inspect_values.md index fbf8660c2..6a907cc7d 100644 --- a/docs/helm/helm_inspect_values.md +++ b/docs/helm/helm_inspect_values.md @@ -21,7 +21,9 @@ helm inspect values [CHART] --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --key-file string identify HTTPS client using this SSL key file --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --password string chart repository password where to locate the requested chart --repo string chart repository url where to locate the requested chart + --username string chart repository username where to locate the requested chart --verify verify the provenance data for this chart --version string version of the chart. By default, the newest chart is shown ``` diff --git a/docs/helm/helm_install.md b/docs/helm/helm_install.md index 406416dc8..0ae9097ba 100644 --- a/docs/helm/helm_install.md +++ b/docs/helm/helm_install.md @@ -80,6 +80,7 @@ helm install [CHART] --name-template string specify template used to name the release --namespace string namespace to install the release into. Defaults to the current kube config namespace. --no-hooks prevent hooks from running during install + --password string chart repository password where to locate the requested chart --replace re-use the given name, even if that name is already used. This is unsafe in production --repo string chart repository url where to locate the requested chart --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) @@ -89,6 +90,7 @@ helm install [CHART] --tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem") --tls-key string path to TLS key file (default "$HELM_HOME/key.pem") --tls-verify enable TLS for request and verify remote + --username string chart repository username where to locate the requested chart -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default []) --verify verify the package before installing it --version string specify the exact chart version to install. If this is not specified, the latest version is installed diff --git a/docs/helm/helm_repo_add.md b/docs/helm/helm_repo_add.md index f0dfcbd5d..456ffa27e 100644 --- a/docs/helm/helm_repo_add.md +++ b/docs/helm/helm_repo_add.md @@ -18,6 +18,8 @@ helm repo add [flags] [NAME] [URL] --cert-file string identify HTTPS client using this SSL certificate file --key-file string identify HTTPS client using this SSL key file --no-update raise error if repo is already registered + --password string chart repository password + --username string chart repository username ``` ### Options inherited from parent commands diff --git a/docs/helm/helm_upgrade.md b/docs/helm/helm_upgrade.md index 5ed128958..5cbb9a110 100644 --- a/docs/helm/helm_upgrade.md +++ b/docs/helm/helm_upgrade.md @@ -46,6 +46,7 @@ helm upgrade [RELEASE] [CHART] --keyring string path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg") --namespace string namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace --no-hooks disable pre/post upgrade hooks + --password string chart repository password where to locate the requested chart --recreate-pods performs pods restart for the resource if applicable --repo string chart repository url where to locate the requested chart --reset-values when upgrading, reset the values to the ones built into the chart @@ -57,6 +58,7 @@ helm upgrade [RELEASE] [CHART] --tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem") --tls-key string path to TLS key file (default "$HELM_HOME/key.pem") --tls-verify enable TLS for request and verify remote + --username string chart repository username where to locate the requested chart -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default []) --verify verify the provenance of the chart before upgrading --version string specify the exact chart version to use. If this is not specified, the latest version is used diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 682ceaa2e..b043ef761 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -258,7 +258,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" - where, _, err := c.DownloadTo(srv.URL()+cname,"", "", "", dest) + where, _, err := c.DownloadTo(srv.URL()+cname, "", "", "", dest) if err != nil { t.Error(err) return diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index ca409fdad..648b9b0c0 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -35,7 +35,9 @@ type TestHTTPGetter struct { func (t *TestHTTPGetter) Get(href string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } -func (t *TestHTTPGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } +func (t *TestHTTPGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { + return t.MockResponse, t.MockError +} // Fake plugin tarball data var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA=" From 871286d60e4bd7a38dadb657f89dbbccefb46bbb Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Tue, 28 Nov 2017 18:37:17 +0200 Subject: [PATCH 06/14] Bug fix - if --username and --password are not passed, use the configured repo credentials --- pkg/downloader/chart_downloader.go | 47 +++++++++++++++---------- pkg/downloader/chart_downloader_test.go | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 38de99010..ff52fb3f0 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -81,11 +81,22 @@ type ChartDownloader struct { // 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. func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest string) (string, *provenance.Verification, error) { - u, g, err := c.ResolveChartVersion(ref, version) + u, g, e, err := c.ResolveChartVersion(ref, version) if err != nil { return "", nil, err } + // If this method did not receive credentials, and the chart repository is + // configured with credentials, we should use the configured credentials. + if e != nil { + if username == "" { + username = e.Username + } + if password == "" { + password = e.Password + } + } + data, err := g.GetWithCredentials(u.String(), username, password) if err != nil { return "", nil, err @@ -139,15 +150,15 @@ func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest stri // * If version is non-empty, this will return the URL for that version // * If version is empty, this will return the URL for the latest version // * 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, *repo.Entry, error) { u, err := url.Parse(ref) 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()) if err != nil { - return u, nil, err + return u, nil, nil, err } if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { @@ -163,73 +174,73 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge if err == ErrNoOwnerRepo { getterConstructor, err := c.Getters.ByScheme(u.Scheme) if err != nil { - return u, nil, err + return u, nil, nil, err } getter, err := getterConstructor(ref, "", "", "") - return u, getter, err + return u, getter, nil, err } - return u, nil, err + return u, nil, nil, err } r, err := repo.NewChartRepository(rc, c.Getters) // 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. - return u, r.Client, err + return u, r.Client, r.Config, err } // See if it's of the form: repo/path_to_chart p := strings.SplitN(u.Path, "/", 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] chartName := p[1] rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories) if err != nil { - return u, nil, err + return u, nil, nil, err } r, err := repo.NewChartRepository(rc, c.Getters) 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. i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name)) if err != nil { - return u, r.Client, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) + return u, r.Client, r.Config, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) } cv, err := i.Get(chartName, version) if err != nil { - return u, r.Client, 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.Client, r.Config, 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 { - return u, r.Client, fmt.Errorf("chart %q has no downloadable URLs", ref) + return u, r.Client, r.Config, fmt.Errorf("chart %q has no downloadable URLs", ref) } // TODO: Seems that picking first URL is not fully correct u, err = url.Parse(cv.URLs[0]) if err != nil { - return u, r.Client, fmt.Errorf("invalid chart URL format: %s", ref) + return u, r.Client, r.Config, fmt.Errorf("invalid chart URL format: %s", ref) } // If the URL is relative (no scheme), prepend the chart repo's base URL if !u.IsAbs() { repoURL, err := url.Parse(rc.URL) if err != nil { - return repoURL, r.Client, err + return repoURL, r.Client, r.Config, err } q := repoURL.Query() // We need a trailing slash for ResolveReference to work, but make sure there isn't already one repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/" u = repoURL.ResolveReference(u) u.RawQuery = q.Encode() - return u, r.Client, err + return u, r.Client, r.Config, err } - return u, r.Client, nil + return u, r.Client, r.Config, nil } // VerifyChart takes a path to a chart archive and a keyring, and verifies the chart. diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index b043ef761..60028eb25 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -61,7 +61,7 @@ func TestResolveChartRef(t *testing.T) { } for _, tt := range tests { - u, _, err := c.ResolveChartVersion(tt.ref, tt.version) + u, _, _, err := c.ResolveChartVersion(tt.ref, tt.version) if err != nil { if tt.fail { continue From 3d29942edcf18587ca4e38530dd30f23e7540ac7 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Sun, 31 Dec 2017 16:38:37 +0200 Subject: [PATCH 07/14] Basic auth support - Refactor. --- cmd/helm/fetch.go | 4 +- cmd/helm/install.go | 4 +- pkg/downloader/chart_downloader.go | 61 ++++++++++++++----------- pkg/downloader/chart_downloader_test.go | 6 +-- pkg/downloader/manager.go | 20 ++++---- 5 files changed, 54 insertions(+), 41 deletions(-) diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index 79c526cf7..612ad973a 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -119,6 +119,8 @@ func (f *fetchCmd) run() error { Keyring: f.keyring, Verify: downloader.VerifyNever, Getters: getter.All(settings), + Username: f.username, + Password: f.password, } if f.verify { @@ -147,7 +149,7 @@ func (f *fetchCmd) run() error { f.chartRef = chartURL } - saved, v, err := c.DownloadTo(f.chartRef, f.username, f.password, f.version, dest) + saved, v, err := c.DownloadTo(f.chartRef, f.version, dest) if err != nil { return err } diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 74a104514..a4f33c448 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -418,6 +418,8 @@ func locateChartPath(repoURL, username, password, name, version string, verify b Out: os.Stdout, Keyring: keyring, Getters: getter.All(settings), + Username: username, + Password: password, } if verify { dl.Verify = downloader.VerifyAlways @@ -435,7 +437,7 @@ func locateChartPath(repoURL, username, password, name, version string, verify b os.MkdirAll(settings.Home.Archive(), 0744) } - filename, _, err := dl.DownloadTo(name, username, password, version, settings.Home.Archive()) + filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive()) if err == nil { lname, err := filepath.Abs(filename) if err != nil { diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index ff52fb3f0..65dbe7bcf 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -67,6 +67,10 @@ type ChartDownloader struct { HelmHome helmpath.Home // Getter collection for the operation Getters getter.Providers + // Chart repository username + Username string + // Chart repository password + Password string } // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. @@ -80,24 +84,26 @@ type ChartDownloader struct { // // 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. -func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest string) (string, *provenance.Verification, error) { - u, g, e, err := c.ResolveChartVersion(ref, version) +func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) { + u, r, err := c.ResolveChartVersion(ref, version) if err != nil { return "", nil, err } - // If this method did not receive credentials, and the chart repository is - // configured with credentials, we should use the configured credentials. - if e != nil { + // If this ChartDownloader is not configured to use credentials, and the chart repository is, + // then we should use the repository's configured credentials. + username := c.Username + password := c.Password + if r.Config != nil { if username == "" { - username = e.Username + username = r.Config.Username } if password == "" { - password = e.Password + password = r.Config.Password } } - data, err := g.GetWithCredentials(u.String(), username, password) + data, err := r.Client.GetWithCredentials(u.String(), username, password) if err != nil { return "", nil, err } @@ -111,7 +117,7 @@ func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest stri // If provenance is requested, verify it. ver := &provenance.Verification{} if c.Verify > VerifyNever { - body, err := g.Get(u.String() + ".prov") + body, err := r.Client.Get(u.String() + ".prov") if err != nil { if c.Verify == VerifyAlways { return destfile, ver, fmt.Errorf("Failed to fetch provenance %q", u.String()+".prov") @@ -150,15 +156,15 @@ func (c *ChartDownloader) DownloadTo(ref, username, password, version, dest stri // * If version is non-empty, this will return the URL for that version // * If version is empty, this will return the URL for the latest version // * If no version can be found, an error is returned -func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, getter.Getter, *repo.Entry, error) { +func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, *repo.ChartRepository, error) { u, err := url.Parse(ref) if err != nil { - return nil, nil, nil, fmt.Errorf("invalid chart URL format: %s", ref) + return nil, nil, fmt.Errorf("invalid chart URL format: %s", ref) } rf, err := repo.LoadRepositoriesFile(c.HelmHome.RepositoryFile()) if err != nil { - return u, nil, nil, err + return u, nil, err } if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { @@ -174,73 +180,74 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge if err == ErrNoOwnerRepo { getterConstructor, err := c.Getters.ByScheme(u.Scheme) if err != nil { - return u, nil, nil, err + return u, nil, err } - getter, err := getterConstructor(ref, "", "", "") - return u, getter, nil, err + r := &repo.ChartRepository{} + r.Client, err = getterConstructor(ref, "", "", "") + return u, r, err } - return u, nil, nil, err + return u, nil, err } r, err := repo.NewChartRepository(rc, c.Getters) // 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. - return u, r.Client, r.Config, err + return u, r, err } // See if it's of the form: repo/path_to_chart p := strings.SplitN(u.Path, "/", 2) if len(p) < 2 { - return u, nil, nil, fmt.Errorf("Non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u) + return u, nil, fmt.Errorf("Non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u) } repoName := p[0] chartName := p[1] rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories) if err != nil { - return u, nil, nil, err + return u, nil, err } r, err := repo.NewChartRepository(rc, c.Getters) if err != nil { - return u, nil, nil, err + return u, nil, err } // Next, we need to load the index, and actually look up the chart. i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name)) if err != nil { - return u, r.Client, r.Config, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) + return u, r, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) } cv, err := i.Get(chartName, version) if err != nil { - return u, r.Client, r.Config, 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, 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 { - return u, r.Client, r.Config, fmt.Errorf("chart %q has no downloadable URLs", ref) + return u, r, fmt.Errorf("chart %q has no downloadable URLs", ref) } // TODO: Seems that picking first URL is not fully correct u, err = url.Parse(cv.URLs[0]) if err != nil { - return u, r.Client, r.Config, fmt.Errorf("invalid chart URL format: %s", ref) + return u, r, fmt.Errorf("invalid chart URL format: %s", ref) } // If the URL is relative (no scheme), prepend the chart repo's base URL if !u.IsAbs() { repoURL, err := url.Parse(rc.URL) if err != nil { - return repoURL, r.Client, r.Config, err + return repoURL, r, err } q := repoURL.Query() // We need a trailing slash for ResolveReference to work, but make sure there isn't already one repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/" u = repoURL.ResolveReference(u) u.RawQuery = q.Encode() - return u, r.Client, r.Config, err + return u, r, err } - return u, r.Client, r.Config, nil + return u, r, nil } // VerifyChart takes a path to a chart archive and a keyring, and verifies the chart. diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 60028eb25..0100772e9 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -61,7 +61,7 @@ func TestResolveChartRef(t *testing.T) { } for _, tt := range tests { - u, _, _, err := c.ResolveChartVersion(tt.ref, tt.version) + u, _, err := c.ResolveChartVersion(tt.ref, tt.version) if err != nil { if tt.fail { continue @@ -195,7 +195,7 @@ func TestDownloadTo(t *testing.T) { Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" - where, v, err := c.DownloadTo(srv.URL()+cname, "", "", "", dest) + where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) if err != nil { t.Error(err) return @@ -258,7 +258,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" - where, _, err := c.DownloadTo(srv.URL()+cname, "", "", "", dest) + where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) if err != nil { t.Error(err) return diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index bac5c4823..89a839b54 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -197,14 +197,6 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { return err } - dl := ChartDownloader{ - Out: m.Out, - Verify: m.Verify, - Keyring: m.Keyring, - HelmHome: m.HelmHome, - Getters: m.Getters, - } - destPath := filepath.Join(m.ChartPath, "charts") tmpPath := filepath.Join(m.ChartPath, "tmpcharts") @@ -251,7 +243,17 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { break } - if _, _, err := dl.DownloadTo(churl, username, password, "", destPath); err != nil { + dl := ChartDownloader{ + Out: m.Out, + Verify: m.Verify, + Keyring: m.Keyring, + HelmHome: m.HelmHome, + Getters: m.Getters, + Username: username, + Password: password, + } + + if _, _, err := dl.DownloadTo(churl, "", destPath); err != nil { saveError = fmt.Errorf("could not download %s: %s", churl, err) break } From 333c821ec208c862bbd1d20f6fa59546716d7fa6 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Sun, 21 Jan 2018 19:54:09 +0200 Subject: [PATCH 08/14] Documentation and tests. --- docs/chart_repository.md | 7 ++++++- pkg/downloader/chart_downloader_test.go | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/chart_repository.md b/docs/chart_repository.md index 1379573fc..21043dd12 100644 --- a/docs/chart_repository.md +++ b/docs/chart_repository.md @@ -148,6 +148,11 @@ Charts repository hosts its charts, so you may want to take a **Note:** A public GCS bucket can be accessed via simple HTTPS at this address `https://bucket-name.storage.googleapis.com/`. +### JFrog Artifactory + +You can also set up chart repositories using JFrog Artifactory. +Read more about chart repositories with JFrog Artifactory [here](https://www.jfrog.com/confluence/display/RTF/Helm+Chart+Repositories) + ### Github Pages example In a similar way you can create charts repository using GitHub Pages. @@ -271,7 +276,7 @@ If the charts are backed by HTTP basic authentication, you can also supply the username and password here: ```console -$ helm repo add fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com +$ helm repo add fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com my-username my-password $ helm repo list fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com ``` diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 0100772e9..ca691cd66 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -115,16 +115,16 @@ func TestDownload(t *testing.T) { // test with server backed by basic auth basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() - if !ok || username != "username" && password != "password" { + if !ok || username != "username" || password != "password" { t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) } fmt.Fprint(w, expect) })) + defer basicAuthSrv.Close() u, _ := url.ParseRequestURI(basicAuthSrv.URL) - u.User = url.UserPassword("username", "password") - got, err = getter.Get(u.String()) + got, err = getter.GetWithCredentials(u.String(), "username", "password") if err != nil { t.Fatal(err) } From 20b1aa3a665360cbdd8b0efcf46860e7e8c035d5 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Wed, 7 Feb 2018 15:59:31 +0200 Subject: [PATCH 09/14] From args to flags, fetch command fix and backward compatibility fix. --- cmd/helm/fetch.go | 4 +++- cmd/helm/install.go | 2 +- cmd/helm/repo_add.go | 21 ++++----------------- docs/chart_repository.md | 4 ++-- pkg/repo/chartrepo.go | 9 ++++++++- pkg/repo/chartrepo_test.go | 12 ++++++------ 6 files changed, 24 insertions(+), 28 deletions(-) diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index 612ad973a..8ccfd8793 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -96,6 +96,8 @@ func newFetchCmd(out io.Writer) *cobra.Command { } f := cmd.Flags() + f.StringVar(&fch.username, "username", "", "chart repository username") + f.StringVar(&fch.password, "password", "", "chart repository password") f.BoolVar(&fch.untar, "untar", false, "if set to true, will untar the chart after downloading it") f.StringVar(&fch.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded") f.BoolVar(&fch.verify, "verify", false, "verify the package against its signature") @@ -142,7 +144,7 @@ func (f *fetchCmd) run() error { } if f.repoURL != "" { - chartURL, err := repo.FindChartInRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) + chartURL, err := repo.FindChartInAuthRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) if err != nil { return err } diff --git a/cmd/helm/install.go b/cmd/helm/install.go index a4f33c448..6354dbb9e 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -425,7 +425,7 @@ func locateChartPath(repoURL, username, password, name, version string, verify b dl.Verify = downloader.VerifyAlways } if repoURL != "" { - chartURL, err := repo.FindChartInRepoURL(repoURL, username, password, name, version, + chartURL, err := repo.FindChartInAuthRepoURL(repoURL, username, password, name, version, certFile, keyFile, caFile, getter.All(settings)) if err != nil { return "", err diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index bbf0de9f8..77a64cc89 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -46,30 +46,15 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { add := &repoAddCmd{out: out} cmd := &cobra.Command{ - Use: "add [flags] [NAME] [URL] [USERNAME] [PASSWORD]", + Use: "add [flags] [NAME] [URL]", Short: "add a chart repository", RunE: func(cmd *cobra.Command, args []string) error { - argsDesc := []string{ - "name for the chart repository", - "the url of the chart repository", - "the username for the chart repository", - "the password of the chart repository", - } - - if len(args) <= 2 { - if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1]); err != nil { - return err - } - } else if err := checkArgsLength(len(args), argsDesc[0], argsDesc[1], argsDesc[2], argsDesc[3]); err != nil { + if err := checkArgsLength(len(args), "name for the chart repository", "the url of the chart repository"); err != nil { return err } add.name = args[0] add.url = args[1] - if len(args) == 4 { - add.username = args[2] - add.password = args[3] - } add.home = settings.Home return add.run() @@ -77,6 +62,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { } f := cmd.Flags() + f.StringVar(&add.username, "username", "", "chart repository username") + f.StringVar(&add.password, "password", "", "chart repository password") f.BoolVar(&add.noupdate, "no-update", false, "raise error if repo is already registered") f.StringVar(&add.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&add.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") diff --git a/docs/chart_repository.md b/docs/chart_repository.md index 21043dd12..9d3837bed 100644 --- a/docs/chart_repository.md +++ b/docs/chart_repository.md @@ -275,8 +275,8 @@ fantastic-charts https://fantastic-charts.storage.googleapis.com If the charts are backed by HTTP basic authentication, you can also supply the username and password here: -```console -$ helm repo add fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com my-username my-password +``console +$ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username--password my-password $ helm repo list fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com ``` diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 0c004d1bc..b73dda52f 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -188,7 +188,14 @@ func (r *ChartRepository) generateIndex() error { // FindChartInRepoURL finds chart in chart repository pointed by repoURL // without adding repo to repositories -func FindChartInRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { +func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { + return FindChartInAuthRepoURL(repoURL, "", "", chartName, chartVersion, certFile, keyFile, caFile, getters) +} + +// FindChartInRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repositories. +// Unlike the FindChartInRepoURL function, this function also receives credentials for the chart repository. +func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { // Download and write the index file to a temporary location tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file") diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index fc12fb009..948ee12d3 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -221,7 +221,7 @@ func TestFindChartInRepoURL(t *testing.T) { } defer srv.Close() - chartURL, err := FindChartInRepoURL(srv.URL, "", "", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("%s", err) } @@ -229,7 +229,7 @@ func TestFindChartInRepoURL(t *testing.T) { t.Errorf("%s is not the valid URL", chartURL) } - chartURL, err = FindChartInRepoURL(srv.URL, "", "", "nginx", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("%s", err) } @@ -239,7 +239,7 @@ func TestFindChartInRepoURL(t *testing.T) { } func TestErrorFindChartInRepoURL(t *testing.T) { - _, err := FindChartInRepoURL("http://someserver/something", "", "", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for bad chart URL, but did not get any errors") } @@ -253,7 +253,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { } defer srv.Close() - _, err = FindChartInRepoURL(srv.URL, "", "", "nginx1", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") } @@ -261,7 +261,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "", "", "nginx1", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") } @@ -269,7 +269,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "", "", "chartWithNoURL", "", "", "", "", getter.All(environment.EnvSettings{})) + _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(environment.EnvSettings{})) if err == nil { t.Errorf("Expected error for no chart URLs available, but did not get any errors") } From b41b54928d1d61270466edc1b540b77f0309b351 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Wed, 7 Feb 2018 16:07:27 +0200 Subject: [PATCH 10/14] helm docs update. --- docs/helm/helm_fetch.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/helm/helm_fetch.md b/docs/helm/helm_fetch.md index 3bc3334a0..3e60724f4 100644 --- a/docs/helm/helm_fetch.md +++ b/docs/helm/helm_fetch.md @@ -33,10 +33,12 @@ helm fetch [flags] [chart URL | repo/chartname] [...] --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. --key-file string identify HTTPS client using this SSL key file --keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg") + --password string chart repository password --prov fetch the provenance file, but don't perform verification --repo string chart repository url where to locate the requested chart --untar if set to true, will untar the chart after downloading it --untardir string if untar is specified, this flag specifies the name of the directory into which the chart is expanded (default ".") + --username string chart repository username --verify verify the package against its signature --version string specific version of a chart. Without this, the latest version is fetched ``` From e49bdb2a4ffcb772d330e134de573eeb4d1c55ed Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Sun, 11 Feb 2018 20:16:40 +0200 Subject: [PATCH 11/14] Backwards compatibility and doc fix. --- cmd/helm/fetch.go | 4 ++-- docs/chart_repository.md | 4 ++-- pkg/downloader/chart_downloader.go | 13 +++++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index 8ccfd8793..069f57eff 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -96,8 +96,6 @@ func newFetchCmd(out io.Writer) *cobra.Command { } f := cmd.Flags() - f.StringVar(&fch.username, "username", "", "chart repository username") - f.StringVar(&fch.password, "password", "", "chart repository password") f.BoolVar(&fch.untar, "untar", false, "if set to true, will untar the chart after downloading it") f.StringVar(&fch.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded") f.BoolVar(&fch.verify, "verify", false, "verify the package against its signature") @@ -110,6 +108,8 @@ func newFetchCmd(out io.Writer) *cobra.Command { f.StringVar(&fch.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&fch.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") f.BoolVar(&fch.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") + f.StringVar(&fch.username, "username", "", "chart repository username") + f.StringVar(&fch.password, "password", "", "chart repository password") return cmd } diff --git a/docs/chart_repository.md b/docs/chart_repository.md index 9d3837bed..5a24249b8 100644 --- a/docs/chart_repository.md +++ b/docs/chart_repository.md @@ -276,9 +276,9 @@ If the charts are backed by HTTP basic authentication, you can also supply the username and password here: ``console -$ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username--password my-password +$ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username --password my-password $ helm repo list -fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com +fantastic-charts https://fantastic-charts.storage.googleapis.com ``` **Note:** A repository will not be added if it does not contain a valid diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 65dbe7bcf..87c80db0c 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -85,7 +85,7 @@ type ChartDownloader struct { // 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. func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) { - u, r, err := c.ResolveChartVersion(ref, version) + u, r, err := c.ResolveChartVersionAndGetRepo(ref, version) if err != nil { return "", nil, err } @@ -156,7 +156,16 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven // * If version is non-empty, this will return the URL for that version // * If version is empty, this will return the URL for the latest version // * If no version can be found, an error is returned -func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, *repo.ChartRepository, error) { +func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, getter.Getter, error) { + u, r, err := c.ResolveChartVersionAndGetRepo(ref, version) + if r != nil { + return u, r.Client, err + } + return u, nil, err +} + +// Same as the ResolveChartVersion method, but returns the chart repositoryy. +func (c *ChartDownloader) ResolveChartVersionAndGetRepo(ref, version string) (*url.URL, *repo.ChartRepository, error) { u, err := url.Parse(ref) if err != nil { return nil, nil, fmt.Errorf("invalid chart URL format: %s", ref) From c2437d846dace8cb57ae7a1bd42176d5a4bcdb67 Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Sun, 11 Feb 2018 20:26:15 +0200 Subject: [PATCH 12/14] helm-fetch.md update. --- docs/helm/helm_fetch.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/helm/helm_fetch.md b/docs/helm/helm_fetch.md index 3e60724f4..1ddef65fa 100644 --- a/docs/helm/helm_fetch.md +++ b/docs/helm/helm_fetch.md @@ -56,5 +56,3 @@ helm fetch [flags] [chart URL | repo/chartname] [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. - -###### Auto generated by spf13/cobra on 8-Mar-2018 From 25142a83decce7bdefd4005882a87f5d1b14294a Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Thu, 1 Mar 2018 16:37:24 +0200 Subject: [PATCH 13/14] Removed GetWithCredentials API. --- pkg/downloader/chart_downloader.go | 87 ++++++++++++--------- pkg/downloader/chart_downloader_test.go | 11 ++- pkg/getter/getter.go | 2 - pkg/getter/httpgetter.go | 35 +++++---- pkg/getter/httpgetter_test.go | 4 +- pkg/getter/plugingetter.go | 4 - pkg/plugin/installer/http_installer_test.go | 4 - pkg/repo/chartrepo.go | 8 +- 8 files changed, 87 insertions(+), 68 deletions(-) diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 87c80db0c..9fe89820e 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -85,25 +85,12 @@ type ChartDownloader struct { // 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. 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 { return "", nil, err } - // If this ChartDownloader is not configured to use credentials, and the chart repository is, - // 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) + data, err := g.Get(u.String()) if err != nil { 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 no version can be found, an error is returned 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 { 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. -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) 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()) 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 { // 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 // we want to find the repo in case we have special SSL cert config // for that repo. + rc, err := c.scanReposForURL(ref, rf) if err != nil { // If there is no special config, return the default HTTP client and // swallow the error. if err == ErrNoOwnerRepo { - getterConstructor, err := c.Getters.ByScheme(u.Scheme) - if err != nil { - return u, nil, err - } r := &repo.ChartRepository{} - r.Client, err = getterConstructor(ref, "", "", "") - return u, r, err + r.Client = g + 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) // 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. - return u, r, err + return u, r, g, err } // See if it's of the form: repo/path_to_chart p := strings.SplitN(u.Path, "/", 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] chartName := p[1] rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories) if err != nil { - return u, nil, err + return u, nil, nil, err } r, err := repo.NewChartRepository(rc, c.Getters) 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. i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name)) 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) 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 { - 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 u, err = url.Parse(cv.URLs[0]) 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 !u.IsAbs() { repoURL, err := url.Parse(rc.URL) if err != nil { - return repoURL, r, err + return repoURL, r, nil, err } q := repoURL.Query() // We need a trailing slash for ResolveReference to work, but make sure there isn't already one repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/" u = repoURL.ResolveReference(u) 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. diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index ca691cd66..80efa77e8 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -99,11 +99,11 @@ func TestDownload(t *testing.T) { t.Fatal("No http provider found") } - getter, err := provider.New(srv.URL, "", "", "") + g, err := provider.New(srv.URL, "", "", "") if err != nil { t.Fatal(err) } - got, err := getter.Get(srv.URL) + got, err := g.Get(srv.URL) if err != nil { t.Fatal(err) } @@ -124,7 +124,12 @@ func TestDownload(t *testing.T) { defer basicAuthSrv.Close() u, _ := url.ParseRequestURI(basicAuthSrv.URL) - got, err = getter.GetWithCredentials(u.String(), "username", "password") + httpgetter, err := getter.NewHTTPGetter(u.String(), "", "", "") + if err != nil { + t.Fatal(err) + } + httpgetter.SetCredentials("username", "password") + got, err = httpgetter.Get(u.String()) if err != nil { t.Fatal(err) } diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index de9d59578..ca018884a 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -27,8 +27,6 @@ import ( type Getter interface { //Get file content by url string Get(url string) (*bytes.Buffer, error) - //Get file content by url, username and password strings - GetWithCredentials(href, username, password string) (*bytes.Buffer, error) } // Constructor is the function for every getter which creates a specific instance diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 5748ae9f7..dd462ce5f 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -28,21 +28,23 @@ import ( ) //httpGetter is the efault HTTP(/S) backend handler -type httpGetter struct { - client *http.Client +type HttpGetter struct { + client *http.Client + username string + password string } -//Get performs a Get from repo.Getter and returns the body. -func (g *httpGetter) Get(href string) (*bytes.Buffer, error) { - return g.get(href, "", "") +func (g *HttpGetter) SetCredentials(username, password string) { + g.username = username + g.password = password } -//Get performs a Get from repo.Getter using credentials and returns the body. -func (g *httpGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { - return g.get(href, username, password) +//Get performs a Get from repo.Getter and returns the body. +func (g *HttpGetter) Get(href string) (*bytes.Buffer, error) { + return g.get(href) } -func (g *httpGetter) get(href, username, password string) (*bytes.Buffer, error) { +func (g *HttpGetter) get(href string) (*bytes.Buffer, error) { buf := bytes.NewBuffer(nil) // Set a helm specific user agent so that a repo server and metrics can @@ -53,8 +55,8 @@ func (g *httpGetter) get(href, username, password string) (*bytes.Buffer, error) } req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v")) - if username != "" && password != "" { - req.SetBasicAuth(username, password) + if g.username != "" && g.password != "" { + req.SetBasicAuth(g.username, g.password) } resp, err := g.client.Do(req) @@ -72,17 +74,22 @@ func (g *httpGetter) get(href, username, password string) (*bytes.Buffer, error) // newHTTPGetter constructs a valid http/https client as Getter func newHTTPGetter(URL, CertFile, KeyFile, CAFile string) (Getter, error) { - var client httpGetter + return NewHTTPGetter(URL, CertFile, KeyFile, CAFile) +} + +// NewHTTPGetter constructs a valid http/https client as HttpGetter +func NewHTTPGetter(URL, CertFile, KeyFile, CAFile string) (*HttpGetter, error) { + var client HttpGetter if CertFile != "" && KeyFile != "" { tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile) if err != nil { - return nil, fmt.Errorf("can't create TLS config for client: %s", err.Error()) + return &client, fmt.Errorf("can't create TLS config for client: %s", err.Error()) } tlsConf.BuildNameToCertificate() sni, err := urlutil.ExtractHostname(URL) if err != nil { - return nil, err + return &client, err } tlsConf.ServerName = sni diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index 477e0bc4f..fe3fde22a 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -27,7 +27,7 @@ func TestHTTPGetter(t *testing.T) { t.Fatal(err) } - if hg, ok := g.(*httpGetter); !ok { + if hg, ok := g.(*HttpGetter); !ok { t.Fatal("Expected newHTTPGetter to produce an httpGetter") } else if hg.client != http.DefaultClient { t.Fatal("Expected newHTTPGetter to return a default HTTP client.") @@ -42,7 +42,7 @@ func TestHTTPGetter(t *testing.T) { t.Fatal(err) } - if _, ok := g.(*httpGetter); !ok { + if _, ok := g.(*HttpGetter); !ok { t.Fatal("Expected newHTTPGetter to produce an httpGetter") } } diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go index edbce131b..c747eef7f 100644 --- a/pkg/getter/plugingetter.go +++ b/pkg/getter/plugingetter.go @@ -79,10 +79,6 @@ func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { return buf, nil } -func (p *pluginGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { - return p.Get(href) -} - // newPluginGetter constructs a valid plugin getter func newPluginGetter(command string, settings environment.EnvSettings, name, base string) Constructor { return func(URL, CertFile, KeyFile, CAFile string) (Getter, error) { diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 648b9b0c0..ca1a71e3e 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -35,10 +35,6 @@ type TestHTTPGetter struct { func (t *TestHTTPGetter) Get(href string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } -func (t *TestHTTPGetter) GetWithCredentials(href, username, password string) (*bytes.Buffer, error) { - return t.MockResponse, t.MockError -} - // Fake plugin tarball data var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA=" diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index b73dda52f..c76cc7913 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -119,8 +119,12 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error { parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" indexURL = parsedURL.String() - resp, err := r.Client.GetWithCredentials(indexURL, r.Config.Username, r.Config.Password) - + g, err := getter.NewHTTPGetter(indexURL, r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile) + if err != nil { + return err + } + g.SetCredentials(r.Config.Username, r.Config.Password) + resp, err := g.Get(indexURL) if err != nil { return err } From d0e3561ada592aa3a39a7a3bc255689ffc2c194a Mon Sep 17 00:00:00 2001 From: eyalbe4 Date: Tue, 20 Mar 2018 13:19:57 +0200 Subject: [PATCH 14/14] Fixed inspect.go followint rebase. --- cmd/helm/inspect.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/helm/inspect.go b/cmd/helm/inspect.go index 02eaf9b87..999856959 100644 --- a/cmd/helm/inspect.go +++ b/cmd/helm/inspect.go @@ -147,7 +147,7 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring, insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err