Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Introduce extendable constructor options and the NewCustom() constructor #63

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 106 additions & 67 deletions registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}