|
@@ -2,6 +2,7 @@ package plugins
|
|
|
|
|
|
import (
|
|
import (
|
|
"bytes"
|
|
"bytes"
|
|
|
|
+ "context"
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"io"
|
|
"io"
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
@@ -9,6 +10,7 @@ import (
|
|
"net/url"
|
|
"net/url"
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
+ "github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/plugins/transport"
|
|
"github.com/docker/docker/pkg/plugins/transport"
|
|
"github.com/docker/go-connections/sockets"
|
|
"github.com/docker/go-connections/sockets"
|
|
"github.com/docker/go-connections/tlsconfig"
|
|
"github.com/docker/go-connections/tlsconfig"
|
|
@@ -82,16 +84,33 @@ type Client struct {
|
|
requestFactory transport.RequestFactory
|
|
requestFactory transport.RequestFactory
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// RequestOpts is the set of options that can be passed into a request
|
|
|
|
+type RequestOpts struct {
|
|
|
|
+ Timeout time.Duration
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// WithRequestTimeout sets a timeout duration for plugin requests
|
|
|
|
+func WithRequestTimeout(t time.Duration) func(*RequestOpts) {
|
|
|
|
+ return func(o *RequestOpts) {
|
|
|
|
+ o.Timeout = t
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
// Call calls the specified method with the specified arguments for the plugin.
|
|
// Call calls the specified method with the specified arguments for the plugin.
|
|
// It will retry for 30 seconds if a failure occurs when calling.
|
|
// It will retry for 30 seconds if a failure occurs when calling.
|
|
-func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
|
|
|
|
|
|
+func (c *Client) Call(serviceMethod string, args, ret interface{}) error {
|
|
|
|
+ return c.CallWithOptions(serviceMethod, args, ret)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// CallWithOptions is just like call except it takes options
|
|
|
|
+func (c *Client) CallWithOptions(serviceMethod string, args interface{}, ret interface{}, opts ...func(*RequestOpts)) error {
|
|
var buf bytes.Buffer
|
|
var buf bytes.Buffer
|
|
if args != nil {
|
|
if args != nil {
|
|
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
|
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- body, err := c.callWithRetry(serviceMethod, &buf, true)
|
|
|
|
|
|
+ body, err := c.callWithRetry(serviceMethod, &buf, true, opts...)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -128,18 +147,31 @@ func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{})
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
|
|
|
|
|
|
+func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool, reqOpts ...func(*RequestOpts)) (io.ReadCloser, error) {
|
|
var retries int
|
|
var retries int
|
|
start := time.Now()
|
|
start := time.Now()
|
|
|
|
|
|
|
|
+ var opts RequestOpts
|
|
|
|
+ for _, o := range reqOpts {
|
|
|
|
+ o(&opts)
|
|
|
|
+ }
|
|
|
|
+
|
|
for {
|
|
for {
|
|
req, err := c.requestFactory.NewRequest(serviceMethod, data)
|
|
req, err := c.requestFactory.NewRequest(serviceMethod, data)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ cancelRequest := func() {}
|
|
|
|
+ if opts.Timeout > 0 {
|
|
|
|
+ var ctx context.Context
|
|
|
|
+ ctx, cancelRequest = context.WithTimeout(req.Context(), opts.Timeout)
|
|
|
|
+ req = req.WithContext(ctx)
|
|
|
|
+ }
|
|
|
|
+
|
|
resp, err := c.http.Do(req)
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
+ cancelRequest()
|
|
if !retry {
|
|
if !retry {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
@@ -157,6 +189,7 @@ func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool)
|
|
if resp.StatusCode != http.StatusOK {
|
|
if resp.StatusCode != http.StatusOK {
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
resp.Body.Close()
|
|
|
|
+ cancelRequest()
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
|
|
return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
|
|
}
|
|
}
|
|
@@ -176,7 +209,11 @@ func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool)
|
|
// old way...
|
|
// old way...
|
|
return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
|
|
return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
|
|
}
|
|
}
|
|
- return resp.Body, nil
|
|
|
|
|
|
+ return ioutils.NewReadCloserWrapper(resp.Body, func() error {
|
|
|
|
+ err := resp.Body.Close()
|
|
|
|
+ cancelRequest()
|
|
|
|
+ return err
|
|
|
|
+ }), nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|