diff --git a/registry/registry.go b/registry/registry.go index 17c480a5..47bc9963 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -8,96 +8,138 @@ import ( "strings" ) +// Registry is the main Docker Registry API client type +type Registry struct { + URL string + Client *http.Client + Logf LogfCallback +} + +// LogfCallback is the prototype of the custom logging function used by Registry type LogfCallback func(format string, args ...interface{}) -/* - * Discard log messages silently. - */ +// Quiet discards log messages silently. func Quiet(format string, args ...interface{}) { /* discard logs */ } -/* - * Pass log messages along to Go's "log" module. - */ +// Log passes log messages along to Go's "log" module. func Log(format string, args ...interface{}) { log.Printf(format, args...) } -type Registry struct { - URL string - Client *http.Client - Logf LogfCallback +// Options stores optional parameters for constructing a new Registry +// See details in the docs of NewCustom() +type Options struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Insecure bool `json:"insecure,omitempty"` + Logf LogfCallback `json:"-"` + DoInitialPing bool `json:"do_initial_ping,omitempty"` + DisableBasicAuth bool `json:"disable_basicauth,omitempty"` } -/* - * Create a new Registry with the given URL and credentials, then Ping()s it - * before returning it to verify that the registry is available. - * - * You can, alternately, construct a Registry manually by populating the fields. - * This passes http.DefaultTransport to WrapTransport when creating the - * http.Client. - */ -func New(registryUrl, username, password string) (*Registry, error) { - transport := http.DefaultTransport - - return newFromTransport(registryUrl, username, password, transport, Log) -} +// NewCustom creates a new Registry with the given URL and optional parameters. +// The interpretation of the optional parameters: +// Username, Password: credentials for the Docker Registry (default: anonymous access) +// Insecure: disables TLS certificate verification (default: TLS certificate verification is enabled) +// Logf: all log messages will be passed to this function (default: registry.Log) +// DoInitialPing: if true, the registry will be Ping()ed during construction (defualt: false) +// (note that some registries, e.g. quay.io, don't support anonymous Ping()) +// DisableBasicAuth: disable basicauth authentication (default: basicauth is enabled) +// (note that some registries, e.g. older versions of Artifactory, don't play well +// with both token and basic auth enabled) +func NewCustom(url string, opts Options) (*Registry, error) { + url = strings.TrimSuffix(url, "/") + var transport http.RoundTripper + if opts.Insecure { + transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + } else { + transport = http.DefaultTransport + } + transport = WrapTransport(transport, url, opts) -/* - * Create a new Registry, as with New, using an http.Transport that disables - * SSL certificate verification. - */ -func NewInsecure(registryUrl, username, password string) (*Registry, error) { - transport := &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, + logf := opts.Logf + if logf == nil { + logf = Log + } + registry := &Registry{ + URL: url, + Client: &http.Client{ + Transport: transport, }, + Logf: logf, } + if opts.DoInitialPing { + if err := registry.Ping(); err != nil { + return nil, err + } + } + return registry, nil + +} + +// New creates a new Registry with the given URL and credentials, then Ping()s it +// before returning it to verify that the registry is available. +// Be aware that this will print out log messages for the initial Ping() +// no matter what. +// +// This constructor is left here for backward compatitibiliy, +// use NewCustom() if you need more control over constructor parameters. +func New(url, username, password string) (*Registry, error) { + return NewCustom(url, Options{ + Username: username, + Password: password, + Logf: Log, + DoInitialPing: true, + }) +} - return newFromTransport(registryUrl, username, password, transport, Log) +// NewInsecure creates a new Registry, as with New, but using an http.Transport that disables +// SSL certificate verification. +// Be aware that this will print out log messages for the initial Ping() +// no matter what. +// +// This constructor is left here for backward compatitibiliy, +// use NewCustom() if you need more control over constructor parameters. +func NewInsecure(url, username, password string) (*Registry, error) { + return NewCustom(url, Options{ + Username: username, + Password: password, + Insecure: true, + Logf: Log, + DoInitialPing: true, + }) } /* - * Given an existing http.RoundTripper such as http.DefaultTransport, build the - * transport stack necessary to authenticate to the Docker registry API. This - * adds in support for OAuth bearer tokens and HTTP Basic auth, and sets up + * WrapTransport takes an existing http.RoundTripper such as http.DefaultTransport, + * and builds the transport stack necessary to authenticate to the Docker registry API. + * This adds in support for OAuth bearer tokens and HTTP Basic auth, and sets up * error handling this library relies on. */ -func WrapTransport(transport http.RoundTripper, url, username, password string) http.RoundTripper { - tokenTransport := &TokenTransport{ +func WrapTransport(transport http.RoundTripper, url string, opts Options) http.RoundTripper { + transport = &TokenTransport{ Transport: transport, - Username: username, - Password: password, + Username: opts.Username, + Password: opts.Password, } - basicAuthTransport := &BasicTransport{ - Transport: tokenTransport, - URL: url, - Username: username, - Password: password, - } - errorTransport := &ErrorTransport{ - Transport: basicAuthTransport, - } - return errorTransport -} - -func newFromTransport(registryUrl, username, password string, transport http.RoundTripper, logf LogfCallback) (*Registry, error) { - url := strings.TrimSuffix(registryUrl, "/") - transport = WrapTransport(transport, url, username, password) - registry := &Registry{ - URL: url, - Client: &http.Client{ + if !opts.DisableBasicAuth { + transport = &BasicTransport{ Transport: transport, - }, - Logf: logf, + URL: url, + Username: opts.Username, + Password: opts.Password, + } } - - if err := registry.Ping(); err != nil { - return nil, err + transport = &ErrorTransport{ + Transport: transport, } - - return registry, nil + return transport } func (r *Registry) url(pathTemplate string, args ...interface{}) string { @@ -112,9 +154,6 @@ func (r *Registry) Ping() error { resp, err := r.Client.Get(url) if resp != nil { defer resp.Body.Close() - } - if err != nil { - } return err }