Authentication support for remote charts repositories (#3206)

Authentication support for remote charts repositories.
pull/3599/head
Eyal Ben Moshe 7 years ago committed by Matthew Fisher
parent 19c73207b2
commit b6335b7dfe

@ -52,6 +52,8 @@ type fetchCmd struct {
destdir string destdir string
version string version string
repoURL string repoURL string
username string
password string
verify bool verify bool
verifyLater bool verifyLater bool
@ -106,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.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.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.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 return cmd
} }
@ -117,6 +121,8 @@ func (f *fetchCmd) run() error {
Keyring: f.keyring, Keyring: f.keyring,
Verify: downloader.VerifyNever, Verify: downloader.VerifyNever,
Getters: getter.All(settings), Getters: getter.All(settings),
Username: f.username,
Password: f.password,
} }
if f.verify { if f.verify {
@ -138,7 +144,7 @@ func (f *fetchCmd) run() error {
} }
if f.repoURL != "" { 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.FindChartInAuthRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings))
if err != nil { if err != nil {
return err return err
} }

@ -59,6 +59,8 @@ type inspectCmd struct {
out io.Writer out io.Writer
version string version string
repoURL string repoURL string
username string
password string
certFile string certFile string
keyFile string keyFile string
@ -88,7 +90,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err 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) insp.certFile, insp.keyFile, insp.caFile)
if err != nil { if err != nil {
return err return err
@ -107,7 +109,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err 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) insp.certFile, insp.keyFile, insp.caFile)
if err != nil { if err != nil {
return err return err
@ -126,7 +128,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err 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) insp.certFile, insp.keyFile, insp.caFile)
if err != nil { if err != nil {
return err return err
@ -145,7 +147,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
if err := checkArgsLength(len(args), "chart name"); err != nil { if err := checkArgsLength(len(args), "chart name"); err != nil {
return err 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) insp.certFile, insp.keyFile, insp.caFile)
if err != nil { if err != nil {
return err return err
@ -181,6 +183,18 @@ func newInspectCmd(out io.Writer) *cobra.Command {
subCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) 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" certFile := "cert-file"
certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle" certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle"
for _, subCmd := range cmds { for _, subCmd := range cmds {

@ -118,6 +118,8 @@ type installCmd struct {
timeout int64 timeout int64
wait bool wait bool
repoURL string repoURL string
username string
password string
devel bool devel bool
depUp bool depUp bool
@ -165,7 +167,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
inst.version = ">0.0.0-0" 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) inst.certFile, inst.keyFile, inst.caFile)
if err != nil { if err != nil {
return err 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.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.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.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.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.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") f.StringVar(&inst.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
@ -381,7 +385,7 @@ func (i *installCmd) printRelease(rel *release.Release) {
// - URL // - URL
// //
// If 'verify' is true, this will attempt to also verify the chart. // If 'verify' is true, this will attempt to also verify the chart.
func locateChartPath(repoURL, name, version string, verify bool, keyring, 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) name = strings.TrimSpace(name)
version = strings.TrimSpace(version) version = strings.TrimSpace(version)
@ -414,12 +418,14 @@ func locateChartPath(repoURL, name, version string, verify bool, keyring,
Out: os.Stdout, Out: os.Stdout,
Keyring: keyring, Keyring: keyring,
Getters: getter.All(settings), Getters: getter.All(settings),
Username: username,
Password: password,
} }
if verify { if verify {
dl.Verify = downloader.VerifyAlways dl.Verify = downloader.VerifyAlways
} }
if repoURL != "" { if repoURL != "" {
chartURL, err := repo.FindChartInRepoURL(repoURL, name, version, chartURL, err := repo.FindChartInAuthRepoURL(repoURL, username, password, name, version,
certFile, keyFile, caFile, getter.All(settings)) certFile, keyFile, caFile, getter.All(settings))
if err != nil { if err != nil {
return "", err return "", err

@ -30,6 +30,8 @@ import (
type repoAddCmd struct { type repoAddCmd struct {
name string name string
url string url string
username string
password string
home helmpath.Home home helmpath.Home
noupdate bool noupdate bool
@ -60,6 +62,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
} }
f := cmd.Flags() 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.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.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") f.StringVar(&add.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
@ -69,14 +73,14 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
} }
func (a *repoAddCmd) run() error { 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 return err
} }
fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name) fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name)
return nil 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()) f, err := repo.LoadRepositoriesFile(home.RepositoryFile())
if err != nil { if err != nil {
return err return err
@ -91,6 +95,8 @@ func addRepository(name, url string, home helmpath.Home, certFile, keyFile, caFi
Name: name, Name: name,
Cache: cif, Cache: cif,
URL: url, URL: url,
Username: username,
Password: password,
CertFile: certFile, CertFile: certFile,
KeyFile: keyFile, KeyFile: keyFile,
CAFile: caFile, CAFile: caFile,

@ -80,7 +80,7 @@ func TestRepoAdd(t *testing.T) {
settings.Home = thome 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) t.Error(err)
} }
@ -93,11 +93,11 @@ func TestRepoAdd(t *testing.T) {
t.Errorf("%s was not successfully inserted into %s", testName, hh.RepositoryFile()) 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) 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") t.Errorf("Duplicate repository name was added")
} }
} }

@ -51,7 +51,7 @@ func TestRepoRemove(t *testing.T) {
if err := removeRepoLine(b, testName, hh); err == nil { if err := removeRepoLine(b, testName, hh); err == nil {
t.Errorf("Expected error removing %s, but did not get one.", testName) 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) t.Error(err)
} }

@ -73,6 +73,8 @@ type upgradeCmd struct {
reuseValues bool reuseValues bool
wait bool wait bool
repoURL string repoURL string
username string
password string
devel bool devel bool
certFile string 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.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.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.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.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.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") 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 { 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 { if err != nil {
return err return err
} }

@ -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 **Note:** A public GCS bucket can be accessed via simple HTTPS at this address
`https://bucket-name.storage.googleapis.com/`. `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 ### Github Pages example
In a similar way you can create charts repository using GitHub Pages. In a similar way you can create charts repository using GitHub Pages.
@ -270,10 +275,10 @@ fantastic-charts https://fantastic-charts.storage.googleapis.com
If the charts are backed by HTTP basic authentication, you can also supply the If the charts are backed by HTTP basic authentication, you can also supply the
username and password here: username and password here:
```console ``console
$ helm repo add fantastic-charts https://username:password@fantastic-charts.storage.googleapis.com $ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username --password my-password
$ helm repo list $ 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 **Note:** A repository will not be added if it does not contain a valid

@ -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. --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 --key-file string identify HTTPS client using this SSL key file
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg") --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 --prov fetch the provenance file, but don't perform verification
--repo string chart repository url where to locate the requested chart --repo string chart repository url where to locate the requested chart
--untar if set to true, will untar the chart after downloading it --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 ".") --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 --verify verify the package against its signature
--version string specific version of a chart. Without this, the latest version is fetched --version string specific version of a chart. Without this, the latest version is fetched
``` ```
@ -54,5 +56,3 @@ helm fetch [flags] [chart URL | repo/chartname] [...]
### SEE ALSO ### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 8-Mar-2018

@ -23,7 +23,9 @@ helm inspect [CHART]
--cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle
--key-file string identify HTTPS client using this SSL key file --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") --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 --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 --verify verify the provenance data for this chart
--version string version of the chart. By default, the newest chart is shown --version string version of the chart. By default, the newest chart is shown
``` ```

@ -21,7 +21,9 @@ helm inspect chart [CHART]
--cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle
--key-file string identify HTTPS client using this SSL key file --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") --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 --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 --verify verify the provenance data for this chart
--version string version of the chart. By default, the newest chart is shown --version string version of the chart. By default, the newest chart is shown
``` ```

@ -21,7 +21,9 @@ helm inspect values [CHART]
--cert-file string verify certificates of HTTPS-enabled servers using this CA bundle --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle
--key-file string identify HTTPS client using this SSL key file --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") --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 --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 --verify verify the provenance data for this chart
--version string version of the chart. By default, the newest chart is shown --version string version of the chart. By default, the newest chart is shown
``` ```

@ -80,6 +80,7 @@ helm install [CHART]
--name-template string specify template used to name the release --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. --namespace string namespace to install the release into. Defaults to the current kube config namespace.
--no-hooks prevent hooks from running during install --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 --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 --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) --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-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-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote --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 []) -f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default [])
--verify verify the package before installing it --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 --version string specify the exact chart version to install. If this is not specified, the latest version is installed

@ -18,6 +18,8 @@ helm repo add [flags] [NAME] [URL]
--cert-file string identify HTTPS client using this SSL certificate file --cert-file string identify HTTPS client using this SSL certificate file
--key-file string identify HTTPS client using this SSL key file --key-file string identify HTTPS client using this SSL key file
--no-update raise error if repo is already registered --no-update raise error if repo is already registered
--password string chart repository password
--username string chart repository username
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

@ -46,6 +46,7 @@ helm upgrade [RELEASE] [CHART]
--keyring string path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg") --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 --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 --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 --recreate-pods performs pods restart for the resource if applicable
--repo string chart repository url where to locate the requested chart --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 --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-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-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote --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 []) -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 --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 --version string specify the exact chart version to use. If this is not specified, the latest version is used

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

@ -99,11 +99,11 @@ func TestDownload(t *testing.T) {
t.Fatal("No http provider found") t.Fatal("No http provider found")
} }
getter, err := provider.New(srv.URL, "", "", "") g, err := provider.New(srv.URL, "", "", "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
got, err := getter.Get(srv.URL) got, err := g.Get(srv.URL)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -115,16 +115,21 @@ func TestDownload(t *testing.T) {
// test with server backed by basic auth // test with server backed by basic auth
basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth() 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) 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) fmt.Fprint(w, expect)
})) }))
defer basicAuthSrv.Close() defer basicAuthSrv.Close()
u, _ := url.ParseRequestURI(basicAuthSrv.URL) u, _ := url.ParseRequestURI(basicAuthSrv.URL)
u.User = url.UserPassword("username", "password") httpgetter, err := getter.NewHTTPGetter(u.String(), "", "", "")
got, err = getter.Get(u.String()) if err != nil {
t.Fatal(err)
}
httpgetter.SetCredentials("username", "password")
got, err = httpgetter.Get(u.String())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -197,14 +197,6 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
return err 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") destPath := filepath.Join(m.ChartPath, "charts")
tmpPath := filepath.Join(m.ChartPath, "tmpcharts") tmpPath := filepath.Join(m.ChartPath, "tmpcharts")
@ -245,12 +237,22 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
// Any failure to resolve/download a chart should fail: // Any failure to resolve/download a chart should fail:
// https://github.com/kubernetes/helm/issues/1439 // 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 { if err != nil {
saveError = fmt.Errorf("could not find %s: %s", churl, err) saveError = fmt.Errorf("could not find %s: %s", churl, err)
break break
} }
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 { if _, _, err := dl.DownloadTo(churl, "", destPath); err != nil {
saveError = fmt.Errorf("could not download %s: %s", churl, err) saveError = fmt.Errorf("could not download %s: %s", churl, err)
break break
@ -476,22 +478,30 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
// repoURL is the repository to search // repoURL is the repository to search
// //
// If it finds a URL that is "relative", it will prepend the repoURL. // 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 { for _, cr := range repos {
if urlutil.Equal(repoURL, cr.Config.URL) { if urlutil.Equal(repoURL, cr.Config.URL) {
entry, err := findEntryByName(name, cr) var entry repo.ChartVersions
entry, err = findEntryByName(name, cr)
if err != nil { if err != nil {
return "", err return
} }
ve, err := findVersionedEntry(version, entry) var ve *repo.ChartVersion
ve, err = findVersionedEntry(version, entry)
if err != nil { if err != nil {
return "", err return
} }
url, err = normalizeURL(repoURL, ve.URLs[0])
return 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. // findEntryByName finds an entry in the chart repository whose name matches the given name.

@ -77,14 +77,19 @@ func TestFindChartURL(t *testing.T) {
version := "0.1.0" version := "0.1.0"
repoURL := "http://example.com/charts" repoURL := "http://example.com/charts"
churl, err := findChartURL(name, version, repoURL, repos) churl, username, password, err := findChartURL(name, version, repoURL, repos)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if churl != "https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz" { if churl != "https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz" {
t.Errorf("Unexpected URL %q", churl) 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) { func TestGetRepoNames(t *testing.T) {

@ -28,12 +28,23 @@ import (
) )
//httpGetter is the efault HTTP(/S) backend handler //httpGetter is the efault HTTP(/S) backend handler
type httpGetter struct { type HttpGetter struct {
client *http.Client client *http.Client
username string
password string
}
func (g *HttpGetter) SetCredentials(username, password string) {
g.username = username
g.password = password
} }
//Get performs a Get from repo.Getter and returns the body. //Get performs a Get from repo.Getter and returns the body.
func (g *httpGetter) Get(href string) (*bytes.Buffer, error) { func (g *HttpGetter) Get(href string) (*bytes.Buffer, error) {
return g.get(href)
}
func (g *HttpGetter) get(href string) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
// Set a helm specific user agent so that a repo server and metrics can // Set a helm specific user agent so that a repo server and metrics can
@ -44,6 +55,10 @@ func (g *httpGetter) Get(href string) (*bytes.Buffer, error) {
} }
req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v")) req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v"))
if g.username != "" && g.password != "" {
req.SetBasicAuth(g.username, g.password)
}
resp, err := g.client.Do(req) resp, err := g.client.Do(req)
if err != nil { if err != nil {
return buf, err return buf, err
@ -59,17 +74,22 @@ func (g *httpGetter) Get(href string) (*bytes.Buffer, error) {
// newHTTPGetter constructs a valid http/https client as Getter // newHTTPGetter constructs a valid http/https client as Getter
func newHTTPGetter(URL, CertFile, KeyFile, CAFile string) (Getter, error) { 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 != "" { if CertFile != "" && KeyFile != "" {
tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile) tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile)
if err != nil { 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() tlsConf.BuildNameToCertificate()
sni, err := urlutil.ExtractHostname(URL) sni, err := urlutil.ExtractHostname(URL)
if err != nil { if err != nil {
return nil, err return &client, err
} }
tlsConf.ServerName = sni tlsConf.ServerName = sni

@ -27,7 +27,7 @@ func TestHTTPGetter(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if hg, ok := g.(*httpGetter); !ok { if hg, ok := g.(*HttpGetter); !ok {
t.Fatal("Expected newHTTPGetter to produce an httpGetter") t.Fatal("Expected newHTTPGetter to produce an httpGetter")
} else if hg.client != http.DefaultClient { } else if hg.client != http.DefaultClient {
t.Fatal("Expected newHTTPGetter to return a default HTTP client.") t.Fatal("Expected newHTTPGetter to return a default HTTP client.")
@ -42,7 +42,7 @@ func TestHTTPGetter(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if _, ok := g.(*httpGetter); !ok { if _, ok := g.(*HttpGetter); !ok {
t.Fatal("Expected newHTTPGetter to produce an httpGetter") t.Fatal("Expected newHTTPGetter to produce an httpGetter")
} }
} }

@ -36,6 +36,8 @@ type Entry struct {
Name string `json:"name"` Name string `json:"name"`
Cache string `json:"cache"` Cache string `json:"cache"`
URL string `json:"url"` URL string `json:"url"`
Username string `json:"username"`
Password string `json:"password"`
CertFile string `json:"certFile"` CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"` KeyFile string `json:"keyFile"`
CAFile string `json:"caFile"` CAFile string `json:"caFile"`
@ -117,7 +119,12 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error {
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml"
indexURL = parsedURL.String() indexURL = parsedURL.String()
resp, err := r.Client.Get(indexURL) 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 { if err != nil {
return err return err
} }
@ -186,6 +193,13 @@ func (r *ChartRepository) generateIndex() error {
// FindChartInRepoURL finds chart in chart repository pointed by repoURL // FindChartInRepoURL finds chart in chart repository pointed by repoURL
// without adding repo to repositories // without adding repo to repositories
func FindChartInRepoURL(repoURL, 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 // Download and write the index file to a temporary location
tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file") tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file")
@ -196,6 +210,8 @@ func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caF
c := Entry{ c := Entry{
URL: repoURL, URL: repoURL,
Username: username,
Password: password,
CertFile: certFile, CertFile: certFile,
KeyFile: keyFile, KeyFile: keyFile,
CAFile: caFile, CAFile: caFile,

Loading…
Cancel
Save