Remove api client lib dependency on tlsconfig and sockets packages.

- Let consumers to configure the http transport initially and apply or
  defaults on top.
- Add function to initialize a new client based on environment
  variables, useful for integrators.

Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
David Calavera 2015-12-16 20:32:17 -05:00
parent 905f3336b2
commit defd1519f7
2 changed files with 74 additions and 18 deletions

View File

@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io"
"net/http"
"os"
"runtime"
@ -108,7 +109,12 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
verStr = tmpStr
}
client, err := lib.NewClient(host, verStr, clientFlags.Common.TLSOptions, customHeaders)
clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions)
if err != nil {
return err
}
client, err := lib.NewClient(host, verStr, clientTransport, customHeaders)
if err != nil {
return err
}
@ -145,3 +151,17 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
host, err = opts.ParseHost(defaultHost, host)
return
}
func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) {
if tlsOptions == nil {
return &http.Transport{}, nil
}
config, err := tlsconfig.Client(*tlsOptions)
if err != nil {
return nil, err
}
return &http.Transport{
TLSClientConfig: config,
}, nil
}

View File

@ -3,12 +3,13 @@ package lib
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/sockets"
"github.com/docker/docker/pkg/tlsconfig"
"time"
)
// Client is the API client that performs all operations
@ -32,11 +33,35 @@ type Client struct {
customHTTPHeaders map[string]string
}
// NewEnvClient initializes a new API client based on environment variables.
// Use DOCKER_HOST to set the url to the docker server.
// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
// Use DOCKER_CERT_PATH to load the tls certificates from.
func NewEnvClient() (*Client, error) {
var transport *http.Transport
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
tlsc := &tls.Config{}
cert, err := tls.LoadX509KeyPair(filepath.Join(dockerCertPath, "cert.pem"), filepath.Join(dockerCertPath, "key.pem"))
if err != nil {
return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
}
tlsc.Certificates = append(tlsc.Certificates, cert)
tlsc.InsecureSkipVerify = os.Getenv("DOCKER_TLS_VERIFY") == ""
transport = &http.Transport{
TLSClientConfig: tlsc,
}
}
return NewClient(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_API_VERSION"), transport, nil)
}
// NewClient initializes a new API client for the given host and API version.
// It won't send any version information if the version number is empty.
// It uses the tlsOptions to decide whether to use a secure connection or not.
// It uses the transport to create a new http client.
// It also initializes the custom http headers to add to each request.
func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
var (
basePath string
tlsConfig *tls.Config
@ -54,21 +79,11 @@ func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpH
basePath = parsed.Path
}
if tlsOptions != nil {
transport = configureTransport(transport, proto, addr)
if transport.TLSClientConfig != nil {
scheme = "https"
var err error
tlsConfig, err = tlsconfig.Client(*tlsOptions)
if err != nil {
return nil, err
}
}
// The transport is created here for reuse during the client session.
transport := &http.Transport{
TLSClientConfig: tlsConfig,
}
sockets.ConfigureTCPTransport(transport, proto, addr)
return &Client{
proto: proto,
addr: addr,
@ -103,3 +118,24 @@ func (cli *Client) getAPIPath(p string, query url.Values) string {
func (cli *Client) ClientVersion() string {
return cli.version
}
func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
if tr == nil {
tr = &http.Transport{}
}
// Why 32? See https://github.com/docker/docker/pull/8035.
timeout := 32 * time.Second
if proto == "unix" {
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, timeout)
}
} else {
tr.Proxy = http.ProxyFromEnvironment
tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
}
return tr
}