client.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. func NewEnvClient() (*Client, error) {
  38. var transport *http.Transport
  39. if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
  40. tlsc := &tls.Config{}
  41. cert, err := tls.LoadX509KeyPair(filepath.Join(dockerCertPath, "cert.pem"), filepath.Join(dockerCertPath, "key.pem"))
  42. if err != nil {
  43. return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
  44. }
  45. tlsc.Certificates = append(tlsc.Certificates, cert)
  46. tlsc.InsecureSkipVerify = os.Getenv("DOCKER_TLS_VERIFY") == ""
  47. transport = &http.Transport{
  48. TLSClientConfig: tlsc,
  49. }
  50. }
  51. return NewClient(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_API_VERSION"), transport, nil)
  52. }
  53. // NewClient initializes a new API client for the given host and API version.
  54. // It won't send any version information if the version number is empty.
  55. // It uses the transport to create a new http client.
  56. // It also initializes the custom http headers to add to each request.
  57. func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
  58. var (
  59. basePath string
  60. tlsConfig *tls.Config
  61. scheme = "http"
  62. protoAddrParts = strings.SplitN(host, "://", 2)
  63. proto, addr = protoAddrParts[0], protoAddrParts[1]
  64. )
  65. if proto == "tcp" {
  66. parsed, err := url.Parse("tcp://" + addr)
  67. if err != nil {
  68. return nil, err
  69. }
  70. addr = parsed.Host
  71. basePath = parsed.Path
  72. }
  73. transport = configureTransport(transport, proto, addr)
  74. if transport.TLSClientConfig != nil {
  75. scheme = "https"
  76. }
  77. return &Client{
  78. proto: proto,
  79. addr: addr,
  80. basePath: basePath,
  81. scheme: scheme,
  82. tlsConfig: tlsConfig,
  83. httpClient: &http.Client{Transport: transport},
  84. version: version,
  85. customHTTPHeaders: httpHeaders,
  86. }, nil
  87. }
  88. // getAPIPath returns the versioned request path to call the api.
  89. // It appends the query parameters to the path if they are not empty.
  90. func (cli *Client) getAPIPath(p string, query url.Values) string {
  91. var apiPath string
  92. if cli.version != "" {
  93. v := strings.TrimPrefix(cli.version, "v")
  94. apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p)
  95. } else {
  96. apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
  97. }
  98. if len(query) > 0 {
  99. apiPath += "?" + query.Encode()
  100. }
  101. return apiPath
  102. }
  103. // ClientVersion returns the version string associated with this
  104. // instance of the Client. Note that this value can be changed
  105. // via the DOCKER_API_VERSION env var.
  106. func (cli *Client) ClientVersion() string {
  107. return cli.version
  108. }
  109. func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
  110. if tr == nil {
  111. tr = &http.Transport{}
  112. }
  113. // Why 32? See https://github.com/docker/docker/pull/8035.
  114. timeout := 32 * time.Second
  115. if proto == "unix" {
  116. // No need for compression in local communications.
  117. tr.DisableCompression = true
  118. tr.Dial = func(_, _ string) (net.Conn, error) {
  119. return net.DialTimeout(proto, addr, timeout)
  120. }
  121. } else {
  122. tr.Proxy = http.ProxyFromEnvironment
  123. tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
  124. }
  125. return tr
  126. }