client.go 4.2 KB

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