github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/api/client/lib/client.go (about)

     1  package lib
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"time"
    13  )
    14  
    15  // Client is the API client that performs all operations
    16  // against a docker server.
    17  type Client struct {
    18  	// proto holds the client protocol i.e. unix.
    19  	proto string
    20  	// addr holds the client address.
    21  	addr string
    22  	// basePath holds the path to prepend to the requests
    23  	basePath string
    24  	// scheme holds the scheme of the client i.e. https.
    25  	scheme string
    26  	// tlsConfig holds the tls configuration to use in hijacked requests.
    27  	tlsConfig *tls.Config
    28  	// httpClient holds the client transport instance. Exported to keep the old code running.
    29  	httpClient *http.Client
    30  	// version of the server to talk to.
    31  	version string
    32  	// custom http headers configured by users
    33  	customHTTPHeaders map[string]string
    34  }
    35  
    36  // NewEnvClient initializes a new API client based on environment variables.
    37  // Use DOCKER_HOST to set the url to the docker server.
    38  // Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
    39  // Use DOCKER_CERT_PATH to load the tls certificates from.
    40  func NewEnvClient() (*Client, error) {
    41  	var transport *http.Transport
    42  	if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
    43  		tlsc := &tls.Config{}
    44  
    45  		cert, err := tls.LoadX509KeyPair(filepath.Join(dockerCertPath, "cert.pem"), filepath.Join(dockerCertPath, "key.pem"))
    46  		if err != nil {
    47  			return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
    48  		}
    49  
    50  		tlsc.Certificates = append(tlsc.Certificates, cert)
    51  		tlsc.InsecureSkipVerify = os.Getenv("DOCKER_TLS_VERIFY") == ""
    52  		transport = &http.Transport{
    53  			TLSClientConfig: tlsc,
    54  		}
    55  	}
    56  
    57  	return NewClient(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_API_VERSION"), transport, nil)
    58  }
    59  
    60  // NewClient initializes a new API client for the given host and API version.
    61  // It won't send any version information if the version number is empty.
    62  // It uses the transport to create a new http client.
    63  // It also initializes the custom http headers to add to each request.
    64  func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
    65  	var (
    66  		basePath       string
    67  		tlsConfig      *tls.Config
    68  		scheme         = "http"
    69  		protoAddrParts = strings.SplitN(host, "://", 2)
    70  		proto, addr    = protoAddrParts[0], protoAddrParts[1]
    71  	)
    72  
    73  	if proto == "tcp" {
    74  		parsed, err := url.Parse("tcp://" + addr)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		addr = parsed.Host
    79  		basePath = parsed.Path
    80  	}
    81  
    82  	transport = configureTransport(transport, proto, addr)
    83  	if transport.TLSClientConfig != nil {
    84  		scheme = "https"
    85  	}
    86  
    87  	return &Client{
    88  		proto:             proto,
    89  		addr:              addr,
    90  		basePath:          basePath,
    91  		scheme:            scheme,
    92  		tlsConfig:         tlsConfig,
    93  		httpClient:        &http.Client{Transport: transport},
    94  		version:           version,
    95  		customHTTPHeaders: httpHeaders,
    96  	}, nil
    97  }
    98  
    99  // getAPIPath returns the versioned request path to call the api.
   100  // It appends the query parameters to the path if they are not empty.
   101  func (cli *Client) getAPIPath(p string, query url.Values) string {
   102  	var apiPath string
   103  	if cli.version != "" {
   104  		v := strings.TrimPrefix(cli.version, "v")
   105  		apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p)
   106  	} else {
   107  		apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
   108  	}
   109  	if len(query) > 0 {
   110  		apiPath += "?" + query.Encode()
   111  	}
   112  	return apiPath
   113  }
   114  
   115  // ClientVersion returns the version string associated with this
   116  // instance of the Client. Note that this value can be changed
   117  // via the DOCKER_API_VERSION env var.
   118  func (cli *Client) ClientVersion() string {
   119  	return cli.version
   120  }
   121  
   122  func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
   123  	if tr == nil {
   124  		tr = &http.Transport{}
   125  	}
   126  
   127  	// Why 32? See https://github.com/docker/docker/pull/8035.
   128  	timeout := 32 * time.Second
   129  	if proto == "unix" {
   130  		// No need for compression in local communications.
   131  		tr.DisableCompression = true
   132  		tr.Dial = func(_, _ string) (net.Conn, error) {
   133  			return net.DialTimeout(proto, addr, timeout)
   134  		}
   135  	} else {
   136  		tr.Proxy = http.ProxyFromEnvironment
   137  		tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
   138  	}
   139  
   140  	return tr
   141  }