client.go 2.2 KB

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