Authentication support for remote charts repositories.

pull/3206/head^2
eyalbe4 8 years ago
parent 87f66af061
commit b3b5366d55

@ -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
}

@ -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 {

@ -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 {

@ -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,

@ -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
}

@ -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
}

@ -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

@ -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.

@ -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) {

@ -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

@ -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

@ -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) {

@ -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="

@ -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,

Loading…
Cancel
Save