github.com/codemac/docker@v1.2.1-0.20150518222241-6a18412d5b9c/pkg/plugins/client.go (about)

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