ソースを参照

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 9 年 前
コミット
defd1519f7
2 ファイル変更74 行追加18 行削除
  1. 21 1
      api/client/cli.go
  2. 53 17
      api/client/lib/client.go

+ 21 - 1
api/client/cli.go

@@ -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
+}

+ 53 - 17
api/client/lib/client.go

@@ -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,20 +79,10 @@ 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,
@@ -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
+}