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,7 +385,7 @@ 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,
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)
@ -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