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