Merge pull request #5917 from bacongobbler/ref-getter-options

ref(getter): introduce Options for passing in getter parameters
pull/5970/head
Matthew Fisher 5 years ago committed by GitHub
commit e55a25bfeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -751,7 +751,7 @@ func readFile(filePath string, settings cli.EnvSettings) ([]byte, error) {
return ioutil.ReadFile(filePath) return ioutil.ReadFile(filePath)
} }
getter, err := getterConstructor(filePath, "", "", "") getter, err := getterConstructor(getter.WithURL(filePath))
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }

@ -156,7 +156,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
} }
// TODO add user-agent // TODO add user-agent
g, err := getter.NewHTTPGetter(ref, "", "", "") g, err := getter.NewHTTPGetter(getter.WithURL(ref))
if err != nil { if err != nil {
return u, nil, err return u, nil, err
} }
@ -240,7 +240,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
u = repoURL.ResolveReference(u) u = repoURL.ResolveReference(u)
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
// TODO add user-agent // TODO add user-agent
g, err := getter.NewHTTPGetter(rc.URL, "", "", "") g, err := getter.NewHTTPGetter(getter.WithURL(rc.URL))
if err != nil { if err != nil {
return repoURL, nil, err return repoURL, nil, err
} }

@ -99,7 +99,7 @@ func TestDownload(t *testing.T) {
t.Fatal("No http provider found") t.Fatal("No http provider found")
} }
g, err := provider.New(srv.URL, "", "", "") g, err := provider.New(getter.WithURL(srv.URL))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -124,11 +124,13 @@ func TestDownload(t *testing.T) {
defer basicAuthSrv.Close() defer basicAuthSrv.Close()
u, _ := url.ParseRequestURI(basicAuthSrv.URL) u, _ := url.ParseRequestURI(basicAuthSrv.URL)
httpgetter, err := getter.NewHTTPGetter(u.String(), "", "", "") httpgetter, err := getter.NewHTTPGetter(
getter.WithURL(u.String()),
getter.WithBasicAuth("username", "password"),
)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
httpgetter.SetBasicAuth("username", "password")
got, err = httpgetter.Get(u.String()) got, err = httpgetter.Get(u.String())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

@ -24,6 +24,55 @@ import (
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
) )
// options are generic parameters to be provided to the getter during instantiation.
//
// Getters may or may not ignore these parameters as they are passed in.
type options struct {
url string
certFile string
keyFile string
caFile string
username string
password string
userAgent string
}
// Option allows specifying various settings configurable by the user for overriding the defaults
// used when performing Get operations with the Getter.
type Option func(*options)
// WithURL informs the getter the server name that will be used when fetching objects. Used in conjunction with
// WithTLSClientConfig to set the TLSClientConfig's server name.
func WithURL(url string) Option {
return func(opts *options) {
opts.url = url
}
}
// WithBasicAuth sets the request's Authorization header to use the provided credentials
func WithBasicAuth(username, password string) Option {
return func(opts *options) {
opts.username = username
opts.password = password
}
}
// WithUserAgent sets the request's User-Agent header to use the provided agent name.
func WithUserAgent(userAgent string) Option {
return func(opts *options) {
opts.userAgent = userAgent
}
}
// WithTLSClientConfig sets the client client auth with the provided credentials.
func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
return func(opts *options) {
opts.certFile = certFile
opts.keyFile = keyFile
opts.caFile = caFile
}
}
// Getter is an interface to support GET to the specified URL. // Getter is an interface to support GET to the specified URL.
type Getter interface { type Getter interface {
//Get file content by url string //Get file content by url string
@ -32,7 +81,7 @@ type Getter interface {
// Constructor is the function for every getter which creates a specific instance // Constructor is the function for every getter which creates a specific instance
// according to the configuration // according to the configuration
type Constructor func(URL, CertFile, KeyFile, CAFile string) (Getter, error) type Constructor func(options ...Option) (Getter, error)
// Provider represents any getter and the schemes that it supports. // Provider represents any getter and the schemes that it supports.
// //
@ -69,8 +118,8 @@ func (p Providers) ByScheme(scheme string) (Constructor, error) {
} }
// All finds all of the registered getters as a list of Provider instances. // All finds all of the registered getters as a list of Provider instances.
// Currently the build-in http/https getter and the discovered // Currently, the built-in getters and the discovered plugins with downloader
// plugins with downloader notations are collected. // notations are collected.
func All(settings cli.EnvSettings) Providers { func All(settings cli.EnvSettings) Providers {
result := Providers{ result := Providers{
{ {

@ -23,7 +23,7 @@ import (
func TestProvider(t *testing.T) { func TestProvider(t *testing.T) {
p := Provider{ p := Provider{
[]string{"one", "three"}, []string{"one", "three"},
func(h, e, l, m string) (Getter, error) { return nil, nil }, func(_ ...Option) (Getter, error) { return nil, nil },
} }
if !p.Provides("three") { if !p.Provides("three") {
@ -33,8 +33,8 @@ func TestProvider(t *testing.T) {
func TestProviders(t *testing.T) { func TestProviders(t *testing.T) {
ps := Providers{ ps := Providers{
{[]string{"one", "three"}, func(h, e, l, m string) (Getter, error) { return nil, nil }}, {[]string{"one", "three"}, func(_ ...Option) (Getter, error) { return nil, nil }},
{[]string{"two", "four"}, func(h, e, l, m string) (Getter, error) { return nil, nil }}, {[]string{"two", "four"}, func(_ ...Option) (Getter, error) { return nil, nil }},
} }
if _, err := ps.ByScheme("one"); err != nil { if _, err := ps.ByScheme("one"); err != nil {

@ -28,21 +28,19 @@ 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 opts options
password string
userAgent string
} }
// SetBasicAuth sets the credentials for the getter // SetBasicAuth sets the request's Authorization header to use the provided credentials.
func (g *HTTPGetter) SetBasicAuth(username, password string) { func (g *HTTPGetter) SetBasicAuth(username, password string) {
g.username = username g.opts.username = username
g.password = password g.opts.password = password
} }
// SetUserAgent sets the HTTP User-Agent for the getter // SetUserAgent sets the request's User-Agent header to use the provided agent name.
func (g *HTTPGetter) SetUserAgent(userAgent string) { func (g *HTTPGetter) SetUserAgent(userAgent string) {
g.userAgent = userAgent g.opts.userAgent = userAgent
} }
//Get performs a Get from repo.Getter and returns the body. //Get performs a Get from repo.Getter and returns the body.
@ -60,12 +58,12 @@ func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) {
return buf, err return buf, err
} }
// req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v")) // req.Header.Set("User-Agent", "Helm/"+strings.TrimPrefix(version.GetVersion(), "v"))
if g.userAgent != "" { if g.opts.userAgent != "" {
req.Header.Set("User-Agent", g.userAgent) req.Header.Set("User-Agent", g.opts.userAgent)
} }
if g.username != "" && g.password != "" { if g.opts.username != "" && g.opts.password != "" {
req.SetBasicAuth(g.username, g.password) req.SetBasicAuth(g.opts.username, g.opts.password)
} }
resp, err := g.client.Do(req) resp, err := g.client.Do(req)
@ -82,21 +80,26 @@ 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(options ...Option) (Getter, error) {
return NewHTTPGetter(url, certFile, keyFile, caFile) return NewHTTPGetter(options...)
} }
// NewHTTPGetter constructs a valid http/https client as HTTPGetter // NewHTTPGetter constructs a valid http/https client as HTTPGetter
func NewHTTPGetter(url, certFile, keyFile, caFile string) (*HTTPGetter, error) { func NewHTTPGetter(options ...Option) (*HTTPGetter, error) {
var client HTTPGetter var client HTTPGetter
if certFile != "" && keyFile != "" {
tlsConf, err := tlsutil.NewClientTLS(certFile, keyFile, caFile) for _, opt := range options {
opt(&client.opts)
}
if client.opts.certFile != "" && client.opts.keyFile != "" {
tlsConf, err := tlsutil.NewClientTLS(client.opts.certFile, client.opts.keyFile, client.opts.caFile)
if err != nil { if err != nil {
return &client, errors.Wrap(err, "can't create TLS config for client") return &client, errors.Wrap(err, "can't create TLS config for client")
} }
tlsConf.BuildNameToCertificate() tlsConf.BuildNameToCertificate()
sni, err := urlutil.ExtractHostname(url) sni, err := urlutil.ExtractHostname(client.opts.url)
if err != nil { if err != nil {
return &client, err return &client, err
} }
@ -111,5 +114,6 @@ func NewHTTPGetter(url, certFile, keyFile, caFile string) (*HTTPGetter, error) {
} else { } else {
client.client = http.DefaultClient client.client = http.DefaultClient
} }
return &client, nil return &client, nil
} }

@ -19,10 +19,12 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"testing" "testing"
"helm.sh/helm/internal/test"
) )
func TestHTTPGetter(t *testing.T) { func TestHTTPGetter(t *testing.T) {
g, err := newHTTPGetter("http://example.com", "", "", "") g, err := newHTTPGetter(WithURL("http://example.com"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -37,12 +39,44 @@ func TestHTTPGetter(t *testing.T) {
cd := "../../testdata" cd := "../../testdata"
join := filepath.Join join := filepath.Join
ca, pub, priv := join(cd, "ca.pem"), join(cd, "crt.pem"), join(cd, "key.pem") ca, pub, priv := join(cd, "ca.pem"), join(cd, "crt.pem"), join(cd, "key.pem")
g, err = newHTTPGetter("http://example.com/", pub, priv, ca) g, err = newHTTPGetter(
WithURL("http://example.com"),
WithTLSClientConfig(pub, priv, ca),
)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, ok := g.(*HTTPGetter); !ok { hg, ok := g.(*HTTPGetter)
if !ok {
t.Fatal("Expected newHTTPGetter to produce an httpGetter") t.Fatal("Expected newHTTPGetter to produce an httpGetter")
} }
transport, ok := hg.client.Transport.(*http.Transport)
if !ok {
t.Errorf("Expected newHTTPGetter to set up an HTTP transport")
}
test.AssertGoldenString(t, transport.TLSClientConfig.ServerName, "output/httpgetter-servername.txt")
// Test other options
hg, err = NewHTTPGetter(
WithBasicAuth("I", "Am"),
WithUserAgent("Groot"),
)
if err != nil {
t.Fatal(err)
}
if hg.opts.username != "I" {
t.Errorf("Expected NewHTTPGetter to contain %q as the username, got %q", "I", hg.opts.username)
}
if hg.opts.password != "Am" {
t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password)
}
if hg.opts.userAgent != "Groot" {
t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent)
}
} }

@ -54,16 +54,16 @@ func collectPlugins(settings cli.EnvSettings) (Providers, error) {
// pluginGetter is a generic type to invoke custom downloaders, // pluginGetter is a generic type to invoke custom downloaders,
// implemented in plugins. // implemented in plugins.
type pluginGetter struct { type pluginGetter struct {
command string command string
certFile, keyFile, cAFile string settings cli.EnvSettings
settings cli.EnvSettings name string
name string base string
base string opts options
} }
// Get runs downloader plugin command // Get runs downloader plugin command
func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) {
argv := []string{p.certFile, p.keyFile, p.cAFile, href} argv := []string{p.opts.certFile, p.opts.keyFile, p.opts.caFile, href}
prog := exec.Command(filepath.Join(p.base, p.command), argv...) prog := exec.Command(filepath.Join(p.base, p.command), argv...)
plugin.SetupPluginEnv(p.settings, p.name, p.base) plugin.SetupPluginEnv(p.settings, p.name, p.base)
prog.Env = os.Environ() prog.Env = os.Environ()
@ -82,16 +82,16 @@ func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) {
// newPluginGetter constructs a valid plugin getter // newPluginGetter constructs a valid plugin getter
func newPluginGetter(command string, settings cli.EnvSettings, name, base string) Constructor { func newPluginGetter(command string, settings cli.EnvSettings, name, base string) Constructor {
return func(URL, CertFile, KeyFile, CAFile string) (Getter, error) { return func(options ...Option) (Getter, error) {
result := &pluginGetter{ result := &pluginGetter{
command: command, command: command,
certFile: CertFile,
keyFile: KeyFile,
cAFile: CAFile,
settings: settings, settings: settings,
name: name, name: name,
base: base, base: base,
} }
for _, opt := range options {
opt(&result.opts)
}
return result, nil return result, nil
} }
} }

@ -78,7 +78,7 @@ func TestPluginGetter(t *testing.T) {
env := hh(false) env := hh(false)
pg := newPluginGetter("echo", env, "test", ".") pg := newPluginGetter("echo", env, "test", ".")
g, err := pg("test://foo/bar", "", "", "") g, err := pg()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -84,7 +84,7 @@ func NewHTTPInstaller(source string, home helmpath.Home) (*HTTPInstaller, error)
return nil, err return nil, err
} }
get, err := getConstructor.New(source, "", "", "") get, err := getConstructor.New(getter.WithURL(source))
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -63,7 +63,10 @@ func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository,
if err != nil { if err != nil {
return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme) return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme)
} }
client, err := getterConstructor(cfg.URL, cfg.CertFile, cfg.KeyFile, cfg.CAFile) client, err := getterConstructor(
getter.WithURL(cfg.URL),
getter.WithTLSClientConfig(cfg.CertFile, cfg.KeyFile, cfg.CAFile),
)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "could not construct protocol handler for: %s", u.Scheme) return nil, errors.Wrapf(err, "could not construct protocol handler for: %s", u.Scheme)
} }
@ -121,11 +124,14 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error {
indexURL = parsedURL.String() indexURL = parsedURL.String()
// TODO add user-agent // TODO add user-agent
g, err := getter.NewHTTPGetter(indexURL, r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile) g, err := getter.NewHTTPGetter(
getter.WithURL(indexURL),
getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile),
getter.WithBasicAuth(r.Config.Username, r.Config.Password),
)
if err != nil { if err != nil {
return err return err
} }
g.SetBasicAuth(r.Config.Username, r.Config.Password)
resp, err := g.Get(indexURL) resp, err := g.Get(indexURL)
if err != nil { if err != nil {
return err return err

Loading…
Cancel
Save