Browse code

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>

David Calavera authored on 2015/12/17 10:32:17
Showing 2 changed files
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"errors"
5 5
 	"fmt"
6 6
 	"io"
7
+	"net/http"
7 8
 	"os"
8 9
 	"runtime"
9 10
 
... ...
@@ -108,7 +109,12 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
108 108
 			verStr = tmpStr
109 109
 		}
110 110
 
111
-		client, err := lib.NewClient(host, verStr, clientFlags.Common.TLSOptions, customHeaders)
111
+		clientTransport, err := newClientTransport(clientFlags.Common.TLSOptions)
112
+		if err != nil {
113
+			return err
114
+		}
115
+
116
+		client, err := lib.NewClient(host, verStr, clientTransport, customHeaders)
112 117
 		if err != nil {
113 118
 			return err
114 119
 		}
... ...
@@ -145,3 +151,17 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
145 145
 	host, err = opts.ParseHost(defaultHost, host)
146 146
 	return
147 147
 }
148
+
149
+func newClientTransport(tlsOptions *tlsconfig.Options) (*http.Transport, error) {
150
+	if tlsOptions == nil {
151
+		return &http.Transport{}, nil
152
+	}
153
+
154
+	config, err := tlsconfig.Client(*tlsOptions)
155
+	if err != nil {
156
+		return nil, err
157
+	}
158
+	return &http.Transport{
159
+		TLSClientConfig: config,
160
+	}, nil
161
+}
... ...
@@ -3,12 +3,13 @@ package lib
3 3
 import (
4 4
 	"crypto/tls"
5 5
 	"fmt"
6
+	"net"
6 7
 	"net/http"
7 8
 	"net/url"
9
+	"os"
10
+	"path/filepath"
8 11
 	"strings"
9
-
10
-	"github.com/docker/docker/pkg/sockets"
11
-	"github.com/docker/docker/pkg/tlsconfig"
12
+	"time"
12 13
 )
13 14
 
14 15
 // Client is the API client that performs all operations
... ...
@@ -32,11 +33,35 @@ type Client struct {
32 32
 	customHTTPHeaders map[string]string
33 33
 }
34 34
 
35
+// NewEnvClient initializes a new API client based on environment variables.
36
+// Use DOCKER_HOST to set the url to the docker server.
37
+// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
38
+// Use DOCKER_CERT_PATH to load the tls certificates from.
39
+func NewEnvClient() (*Client, error) {
40
+	var transport *http.Transport
41
+	if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
42
+		tlsc := &tls.Config{}
43
+
44
+		cert, err := tls.LoadX509KeyPair(filepath.Join(dockerCertPath, "cert.pem"), filepath.Join(dockerCertPath, "key.pem"))
45
+		if err != nil {
46
+			return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
47
+		}
48
+
49
+		tlsc.Certificates = append(tlsc.Certificates, cert)
50
+		tlsc.InsecureSkipVerify = os.Getenv("DOCKER_TLS_VERIFY") == ""
51
+		transport = &http.Transport{
52
+			TLSClientConfig: tlsc,
53
+		}
54
+	}
55
+
56
+	return NewClient(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_API_VERSION"), transport, nil)
57
+}
58
+
35 59
 // NewClient initializes a new API client for the given host and API version.
36 60
 // It won't send any version information if the version number is empty.
37
-// It uses the tlsOptions to decide whether to use a secure connection or not.
61
+// It uses the transport to create a new http client.
38 62
 // It also initializes the custom http headers to add to each request.
39
-func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpHeaders map[string]string) (*Client, error) {
63
+func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
40 64
 	var (
41 65
 		basePath       string
42 66
 		tlsConfig      *tls.Config
... ...
@@ -54,20 +79,10 @@ func NewClient(host string, version string, tlsOptions *tlsconfig.Options, httpH
54 54
 		basePath = parsed.Path
55 55
 	}
56 56
 
57
-	if tlsOptions != nil {
57
+	transport = configureTransport(transport, proto, addr)
58
+	if transport.TLSClientConfig != nil {
58 59
 		scheme = "https"
59
-		var err error
60
-		tlsConfig, err = tlsconfig.Client(*tlsOptions)
61
-		if err != nil {
62
-			return nil, err
63
-		}
64
-	}
65
-
66
-	// The transport is created here for reuse during the client session.
67
-	transport := &http.Transport{
68
-		TLSClientConfig: tlsConfig,
69 60
 	}
70
-	sockets.ConfigureTCPTransport(transport, proto, addr)
71 61
 
72 62
 	return &Client{
73 63
 		proto:             proto,
... ...
@@ -103,3 +118,24 @@ func (cli *Client) getAPIPath(p string, query url.Values) string {
103 103
 func (cli *Client) ClientVersion() string {
104 104
 	return cli.version
105 105
 }
106
+
107
+func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
108
+	if tr == nil {
109
+		tr = &http.Transport{}
110
+	}
111
+
112
+	// Why 32? See https://github.com/docker/docker/pull/8035.
113
+	timeout := 32 * time.Second
114
+	if proto == "unix" {
115
+		// No need for compression in local communications.
116
+		tr.DisableCompression = true
117
+		tr.Dial = func(_, _ string) (net.Conn, error) {
118
+			return net.DialTimeout(proto, addr, timeout)
119
+		}
120
+	} else {
121
+		tr.Proxy = http.ProxyFromEnvironment
122
+		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
123
+	}
124
+
125
+	return tr
126
+}