pull/1670/merge
Anton Galitsyn 9 years ago committed by GitHub
commit 0680a861b1

@ -17,11 +17,13 @@ package main
import (
"io"
"net/http"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/downloader"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/httputil"
)
const dependencyBuildDesc = `
@ -36,11 +38,16 @@ of 'helm dependency update'.
`
type dependencyBuildCmd struct {
out io.Writer
chartpath string
verify bool
keyring string
helmhome helmpath.Home
certFile string
keyFile string
caFile string
out io.Writer
}
func newDependencyBuildCmd(out io.Writer) *cobra.Command {
@ -66,16 +73,30 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.BoolVar(&dbc.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVar(&dbc.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&dbc.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&dbc.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}
func (d *dependencyBuildCmd) run() error {
var client *http.Client
var err error
if d.certFile != "" && d.keyFile != "" && d.caFile != "" {
client, err = httputil.NewHTTPClientTLS(d.certFile, d.keyFile, d.caFile)
if err != nil {
return err
}
} else {
client = http.DefaultClient
}
man := &downloader.Manager{
Out: d.out,
ChartPath: d.chartpath,
HelmHome: d.helmhome,
Keyring: d.keyring,
Client: client,
}
if d.verify {
man.Verify = downloader.VerifyIfPossible

@ -17,11 +17,13 @@ package main
import (
"io"
"net/http"
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/downloader"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/httputil"
)
const dependencyUpDesc = `
@ -36,11 +38,16 @@ rebuild the requirements to an exact version.
// dependencyUpdateCmd describes a 'helm dependency update'
type dependencyUpdateCmd struct {
out io.Writer
chartpath string
helmhome helmpath.Home
verify bool
keyring string
certFile string
keyFile string
caFile string
out io.Writer
}
// newDependencyUpdateCmd creates a new dependency update command.
@ -75,17 +82,31 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.BoolVar(&duc.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&duc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVar(&duc.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&duc.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&duc.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}
// run runs the full dependency update process.
func (d *dependencyUpdateCmd) run() error {
var client *http.Client
var err error
if d.certFile != "" && d.keyFile != "" && d.caFile != "" {
client, err = httputil.NewHTTPClientTLS(d.certFile, d.keyFile, d.caFile)
if err != nil {
return err
}
} else {
client = http.DefaultClient
}
man := &downloader.Manager{
Out: d.out,
ChartPath: d.chartpath,
HelmHome: d.helmhome,
Keyring: d.keyring,
Client: client,
}
if d.verify {
man.Verify = downloader.VerifyIfPossible

@ -62,6 +62,8 @@ type ChartDownloader struct {
Keyring string
// HelmHome is the $HELM_HOME.
HelmHome helmpath.Home
// A Client is an HTTP client.
Client *http.Client
}
// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
@ -81,7 +83,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
if err != nil {
return "", nil, err
}
data, err := download(u.String())
data, err := download(u.String(), c.Client)
if err != nil {
return "", nil, err
}
@ -96,7 +98,7 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
ver := &provenance.Verification{}
if c.Verify > VerifyNever {
body, err := download(u.String() + ".prov")
body, err := download(u.String()+".prov", c.Client)
if err != nil {
if c.Verify == VerifyAlways {
return destfile, ver, fmt.Errorf("Failed to fetch provenance %q", u.String()+".prov")
@ -218,10 +220,10 @@ func VerifyChart(path string, keyring string) (*provenance.Verification, error)
}
// download performs a simple HTTP Get and returns the body.
func download(href string) (*bytes.Buffer, error) {
func download(href string, client *http.Client) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
resp, err := http.Get(href)
resp, err := client.Get(href)
if err != nil {
return buf, err
}

@ -82,7 +82,7 @@ func TestDownload(t *testing.T) {
}))
defer srv.Close()
got, err := download(srv.URL)
got, err := download(srv.URL, http.DefaultClient)
if err != nil {
t.Fatal(err)
}
@ -132,6 +132,7 @@ func TestDownloadTo(t *testing.T) {
Out: os.Stderr,
Verify: VerifyAlways,
Keyring: "testdata/helm-test-key.pub",
Client: http.DefaultClient,
}
cname := "/signtest-0.1.0.tgz"
where, v, err := c.DownloadTo(srv.URL()+cname, "", dest)

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
@ -49,6 +50,8 @@ type Manager struct {
Verify VerificationStrategy
// Keyring is the key ring file.
Keyring string
// A Client is an HTTP client.
Client *http.Client
}
// Build rebuilds a local charts directory from a lockfile.
@ -179,6 +182,7 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
Verify: m.Verify,
Keyring: m.Keyring,
HelmHome: m.HelmHome,
Client: m.Client,
}
destPath := filepath.Join(m.ChartPath, "charts")
@ -295,7 +299,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) {
for _, re := range repos {
wg.Add(1)
go func(n, u string) {
if err := repo.DownloadIndexFile(n, u, m.HelmHome.CacheIndex(n)); err != nil {
if err := repo.DownloadIndexFile(n, u, m.HelmHome.CacheIndex(n), http.DefaultClient); err != nil {
fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", n, u, err)
} else {
fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", n)

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@ -27,6 +28,7 @@ import (
"k8s.io/helm/cmd/helm/downloader"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/httputil"
)
const fetchDesc = `
@ -55,6 +57,10 @@ type fetchCmd struct {
verifyLater bool
keyring string
certFile string
keyFile string
caFile string
out io.Writer
}
@ -87,17 +93,30 @@ func newFetchCmd(out io.Writer) *cobra.Command {
f.StringVar(&fch.version, "version", "", "specific version of a chart. Without this, the latest version is fetched")
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVarP(&fch.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
f.StringVar(&fch.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate 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")
return cmd
}
func (f *fetchCmd) run() error {
pname := f.chartRef
var client *http.Client
var err error
if f.certFile != "" && f.keyFile != "" && f.caFile != "" {
client, err = httputil.NewHTTPClientTLS(f.certFile, f.keyFile, f.caFile)
if err != nil {
return err
}
} else {
client = http.DefaultClient
}
c := downloader.ChartDownloader{
HelmHome: helmpath.Home(homePath()),
Out: f.out,
Keyring: f.keyring,
Verify: downloader.VerifyNever,
Client: client,
}
if f.verify {
@ -118,7 +137,7 @@ func (f *fetchCmd) run() error {
defer os.RemoveAll(dest)
}
saved, v, err := c.DownloadTo(pname, f.version, dest)
saved, v, err := c.DownloadTo(f.chartRef, f.version, dest)
if err != nil {
return err
}

@ -37,14 +37,23 @@ import (
const (
localRepoIndexFilePath = "index.yaml"
homeEnvVar = "HELM_HOME"
hostEnvVar = "HELM_HOST"
tillerNamespace = "kube-system"
)
const (
homeEnvVar = "HELM_HOME"
hostEnvVar = "HELM_HOST"
certFileEnvVar = "HELM_CERT_FILE"
keyFileEnvVar = "HELM_KEY_FILE"
caFileEnvVar = "HELM_CA_FILE"
)
var (
helmHome string
tillerHost string
certFile string
keyFile string
caFile string
kubeContext string
)
@ -68,9 +77,12 @@ Common actions from this point include:
- helm list: list releases of charts
Environment:
$HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm
$HELM_HOST set an alternative Tiller host. The format is host:port
$KUBECONFIG set an alternate Kubernetes configuration file (default "~/.kube/config")
$HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm
$HELM_HOST set an alternative Tiller host. The format is host:port
$HELM_CERT_FILE identify HTTPS client using this SSL certificate file.
$HELM_KEY_FILE identify HTTPS client using this SSL key file.
$HELM_CA_FILE verify certificates of HTTPS-enabled servers using this CA bundle.
$KUBECONFIG set an alternate Kubernetes configuration file (default "~/.kube/config")
`
func newRootCmd(out io.Writer) *cobra.Command {
@ -91,6 +103,9 @@ func newRootCmd(out io.Writer) *cobra.Command {
p := cmd.PersistentFlags()
p.StringVar(&helmHome, "home", home, "location of your Helm config. Overrides $HELM_HOME")
p.StringVar(&tillerHost, "host", thost, "address of tiller. Overrides $HELM_HOST")
p.StringVar(&certFile, "cert-file", os.Getenv(certFileEnvVar), "identify HTTPS client using this SSL certificate file. Overrides $HELM_CERT_FILE")
p.StringVar(&keyFile, "key-file", os.Getenv(keyFileEnvVar), "identify HTTPS client using this SSL key file. Overrides $HELM_KEY_FILE")
p.StringVar(&caFile, "ca-file", os.Getenv(caFileEnvVar), "verify certificates of HTTPS-enabled servers using this CA bundle. Overrides $HELM_CA_FILE")
p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use")
p.BoolVar(&flagDebug, "debug", false, "enable verbose output")

@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"io"
"net/http"
"os"
"github.com/spf13/cobra"
@ -172,7 +173,7 @@ func ensureHome(home helmpath.Home, out io.Writer) error {
return err
}
cif := home.CacheIndex(stableRepository)
if err := repo.DownloadIndexFile(stableRepository, stableRepositoryURL, cif); err != nil {
if err := repo.DownloadIndexFile(stableRepository, stableRepositoryURL, cif, http.DefaultClient); err != nil {
fmt.Fprintf(out, "WARNING: Failed to download %s: %s (run 'helm repo update')\n", stableRepository, err)
}
} else if fi.IsDir() {

@ -22,6 +22,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
@ -277,6 +278,8 @@ func locateChartPath(name, version string, verify bool, keyring string) (string,
HelmHome: helmpath.Home(homePath()),
Out: os.Stdout,
Keyring: keyring,
// TODO: this should be configurable
Client: http.DefaultClient,
}
if verify {
dl.Verify = downloader.VerifyAlways

@ -19,12 +19,14 @@ package main
import (
"fmt"
"io"
"net/http"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/httputil"
"k8s.io/helm/pkg/repo"
)
@ -32,8 +34,13 @@ type repoAddCmd struct {
name string
url string
home helmpath.Home
out io.Writer
noupdate bool
certFile string
keyFile string
caFile string
out io.Writer
}
func newRepoAddCmd(out io.Writer) *cobra.Command {
@ -56,17 +63,32 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
return add.run()
},
}
f := cmd.Flags()
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")
f.StringVar(&add.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}
func (a *repoAddCmd) run() error {
var client *http.Client
var err error
if a.certFile != "" && a.keyFile != "" && a.caFile != "" {
client, err = httputil.NewHTTPClientTLS(a.certFile, a.keyFile, a.caFile)
if err != nil {
return err
}
} else {
client = http.DefaultClient
}
if a.noupdate {
err = addRepository(a.name, a.url, a.home)
err = addRepository(a.name, a.url, a.home, client)
} else {
err = updateRepository(a.name, a.url, a.home)
err = updateRepository(a.name, a.url, a.home, client)
}
if err != nil {
return err
@ -75,9 +97,9 @@ func (a *repoAddCmd) run() error {
return nil
}
func addRepository(name, url string, home helmpath.Home) error {
func addRepository(name, url string, home helmpath.Home, client *http.Client) error {
cif := home.CacheIndex(name)
if err := repo.DownloadIndexFile(name, url, cif); err != nil {
if err := repo.DownloadIndexFile(name, url, cif, client); err != nil {
return fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error())
}
@ -102,9 +124,9 @@ func insertRepoLine(name, url string, home helmpath.Home) error {
return f.WriteFile(home.RepositoryFile(), 0644)
}
func updateRepository(name, url string, home helmpath.Home) error {
func updateRepository(name, url string, home helmpath.Home, client *http.Client) error {
cif := home.CacheIndex(name)
if err := repo.DownloadIndexFile(name, url, cif); err != nil {
if err := repo.DownloadIndexFile(name, url, cif, client); err != nil {
return err
}

@ -18,6 +18,7 @@ package main
import (
"bytes"
"net/http"
"os"
"testing"
@ -80,7 +81,7 @@ func TestRepoAdd(t *testing.T) {
t.Fatal(err)
}
if err := addRepository(testName, ts.URL(), hh); err != nil {
if err := addRepository(testName, ts.URL(), hh, http.DefaultClient); err != nil {
t.Error(err)
}
@ -93,11 +94,11 @@ func TestRepoAdd(t *testing.T) {
t.Errorf("%s was not successfully inserted into %s", testName, hh.RepositoryFile())
}
if err := updateRepository(testName, ts.URL(), hh); err != nil {
if err := updateRepository(testName, ts.URL(), hh, http.DefaultClient); err != nil {
t.Errorf("Repository was not updated: %s", err)
}
if err := addRepository(testName, ts.URL(), hh); err == nil {
if err := addRepository(testName, ts.URL(), hh, http.DefaultClient); err == nil {
t.Errorf("Duplicate repository name was added")
}
}

@ -20,11 +20,13 @@ import (
"errors"
"fmt"
"io"
"net/http"
"sync"
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/helmpath"
"k8s.io/helm/pkg/httputil"
"k8s.io/helm/pkg/repo"
)
@ -37,9 +39,14 @@ future releases.
`
type repoUpdateCmd struct {
update func([]*repo.Entry, bool, io.Writer, helmpath.Home)
out io.Writer
update func([]*repo.Entry, bool, io.Writer, helmpath.Home, *http.Client)
home helmpath.Home
certFile string
keyFile string
caFile string
out io.Writer
}
func newRepoUpdateCmd(out io.Writer) *cobra.Command {
@ -57,10 +64,27 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
return u.run()
},
}
f := cmd.Flags()
f.StringVar(&u.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&u.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&u.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}
func (u *repoUpdateCmd) run() error {
var client *http.Client
var err error
if u.certFile != "" && u.keyFile != "" && u.caFile != "" {
client, err = httputil.NewHTTPClientTLS(u.certFile, u.keyFile, u.caFile)
if err != nil {
return err
}
} else {
client = http.DefaultClient
}
f, err := repo.LoadRepositoriesFile(u.home.RepositoryFile())
if err != nil {
return err
@ -70,11 +94,11 @@ func (u *repoUpdateCmd) run() error {
return errors.New("no repositories found. You must add one before updating")
}
u.update(f.Repositories, flagDebug, u.out, u.home)
u.update(f.Repositories, flagDebug, u.out, u.home, client)
return nil
}
func updateCharts(repos []*repo.Entry, verbose bool, out io.Writer, home helmpath.Home) {
func updateCharts(repos []*repo.Entry, verbose bool, out io.Writer, home helmpath.Home, client *http.Client) {
fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...")
var wg sync.WaitGroup
for _, re := range repos {
@ -85,7 +109,7 @@ func updateCharts(repos []*repo.Entry, verbose bool, out io.Writer, home helmpat
// We skip local because the indices are symlinked.
return
}
err := repo.DownloadIndexFile(n, u, home.CacheIndex(n))
err := repo.DownloadIndexFile(n, u, home.CacheIndex(n), client)
if err != nil {
fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", n, u, err)
} else {

@ -19,6 +19,7 @@ import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strings"
"testing"
@ -43,7 +44,7 @@ func TestUpdateCmd(t *testing.T) {
out := bytes.NewBuffer(nil)
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.Entry, verbose bool, out io.Writer, home helmpath.Home) {
updater := func(repos []*repo.Entry, verbose bool, out io.Writer, home helmpath.Home, client *http.Client) {
for _, re := range repos {
fmt.Fprintln(out, re.Name)
}
@ -83,7 +84,7 @@ func TestUpdateCharts(t *testing.T) {
repos := []*repo.Entry{
{Name: "charts", URL: srv.URL()},
}
updateCharts(repos, false, buf, helmpath.Home(thome))
updateCharts(repos, false, buf, helmpath.Home(thome), http.DefaultClient)
got := buf.String()
if strings.Contains(got, "Unable to get an update") {

@ -0,0 +1,39 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package httputil
import (
"fmt"
"net/http"
"k8s.io/helm/pkg/tlsutil"
)
// NewHTTPClientTLS constructs http.Client with configured TLS for http.Transport
func NewHTTPClientTLS(certFile, keyFile, caFile string) (*http.Client, error) {
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile)
if err != nil {
return nil, fmt.Errorf("can't create TLS config for client: %s", err.Error())
}
tlsConf.BuildNameToCertificate()
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
}
return client, nil
}

@ -229,11 +229,11 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
}
// DownloadIndexFile fetches the index from a repository.
func DownloadIndexFile(repoName, url, indexFilePath string) error {
func DownloadIndexFile(repoName, url, indexFilePath string, client *http.Client) error {
var indexURL string
indexURL = strings.TrimSuffix(url, "/") + "/index.yaml"
resp, err := http.Get(indexURL)
resp, err := client.Get(indexURL)
if err != nil {
return err
}

@ -133,7 +133,7 @@ func TestDownloadIndexFile(t *testing.T) {
defer os.RemoveAll(dirName)
path := filepath.Join(dirName, testRepo+"-index.yaml")
if err := DownloadIndexFile(testRepo, srv.URL, path); err != nil {
if err := DownloadIndexFile(testRepo, srv.URL, path, http.DefaultClient); err != nil {
t.Errorf("%#v", err)
}

@ -0,0 +1,68 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tlsutil
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
)
// NewClientTLS returns tls.Config appropriate for client auth.
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
cert, err := CertFromFilePair(certFile, keyFile)
if err != nil {
return nil, err
}
cp, err := CertPoolFromFile(caFile)
if err != nil {
return nil, err
}
return &tls.Config{
Certificates: []tls.Certificate{*cert},
RootCAs: cp,
}, nil
}
// CertPoolFromFile returns an x509.CertPool containing the certificates
// in the given PEM-encoded file.
// Returns an error if the file could not be read, a certificate could not
// be parsed, or if the file does not contain any certificates
func CertPoolFromFile(filename string) (*x509.CertPool, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("can't read CA file: %v", filename)
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("failed to append certificates from file: %s", filename)
}
return cp, nil
}
// CertFromFilePair returns an tls.Certificate containing the
// certificates public/private key pair from a pair of given PEM-encoded files.
// Returns an error if the file could not be read, a certificate could not
// be parsed, or if the file does not contain any certificates
func CertFromFilePair(certFile, keyFile string) (*tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("can't load key pair from cert %s and key %s", certFile, keyFile)
}
return &cert, err
}
Loading…
Cancel
Save