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 }