github.com/rentongzhang/docker@v1.8.2-rc1/pkg/plugins/client.go (about)

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