client.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package plugins
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "net"
  8. "net/http"
  9. "strings"
  10. "time"
  11. "github.com/Sirupsen/logrus"
  12. )
  13. const (
  14. versionMimetype = "application/vnd.docker.plugins.v1+json"
  15. defaultTimeOut = 30
  16. )
  17. func NewClient(addr string) *Client {
  18. tr := &http.Transport{}
  19. protoAndAddr := strings.Split(addr, "://")
  20. configureTCPTransport(tr, protoAndAddr[0], protoAndAddr[1])
  21. return &Client{&http.Client{Transport: tr}, protoAndAddr[1]}
  22. }
  23. type Client struct {
  24. http *http.Client
  25. addr string
  26. }
  27. func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
  28. var buf bytes.Buffer
  29. if err := json.NewEncoder(&buf).Encode(args); err != nil {
  30. return err
  31. }
  32. req, err := http.NewRequest("POST", "/"+serviceMethod, &buf)
  33. if err != nil {
  34. return err
  35. }
  36. req.Header.Add("Accept", versionMimetype)
  37. req.URL.Scheme = "http"
  38. req.URL.Host = c.addr
  39. var retries int
  40. start := time.Now()
  41. for {
  42. resp, err := c.http.Do(req)
  43. if err != nil {
  44. timeOff := backoff(retries)
  45. if timeOff+time.Since(start) > defaultTimeOut {
  46. return err
  47. }
  48. retries++
  49. logrus.Warn("Unable to connect to plugin: %s, retrying in %ds\n", c.addr, timeOff)
  50. time.Sleep(timeOff)
  51. continue
  52. }
  53. if resp.StatusCode != http.StatusOK {
  54. remoteErr, err := ioutil.ReadAll(resp.Body)
  55. if err != nil {
  56. return nil
  57. }
  58. return fmt.Errorf("Plugin Error: %s", remoteErr)
  59. }
  60. return json.NewDecoder(resp.Body).Decode(&ret)
  61. }
  62. }
  63. func backoff(retries int) time.Duration {
  64. b, max := float64(1), float64(defaultTimeOut)
  65. for b < max && retries > 0 {
  66. b *= 2
  67. retries--
  68. }
  69. if b > max {
  70. b = max
  71. }
  72. return time.Duration(b)
  73. }
  74. func configureTCPTransport(tr *http.Transport, proto, addr string) {
  75. // Why 32? See https://github.com/docker/docker/pull/8035.
  76. timeout := 32 * time.Second
  77. if proto == "unix" {
  78. // No need for compression in local communications.
  79. tr.DisableCompression = true
  80. tr.Dial = func(_, _ string) (net.Conn, error) {
  81. return net.DialTimeout(proto, addr, timeout)
  82. }
  83. } else {
  84. tr.Proxy = http.ProxyFromEnvironment
  85. tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
  86. }
  87. }