浏览代码

Add HTTP client timeout.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 83ca993c154d56e03d6f95a3f8351c48b3ed3e29)
Signed-off-by: Victor Vieux <victorvieux@gmail.com>
Anusha Ragunathan 8 年之前
父节点
当前提交
0403addc5f

+ 1 - 1
api/server/router/plugin/backend.go

@@ -11,7 +11,7 @@ import (
 // Backend for Plugin
 // Backend for Plugin
 type Backend interface {
 type Backend interface {
 	Disable(name string) error
 	Disable(name string) error
-	Enable(name string) error
+	Enable(name string, config *enginetypes.PluginEnableConfig) error
 	List() ([]enginetypes.Plugin, error)
 	List() ([]enginetypes.Plugin, error)
 	Inspect(name string) (enginetypes.Plugin, error)
 	Inspect(name string) (enginetypes.Plugin, error)
 	Remove(name string, config *enginetypes.PluginRmConfig) error
 	Remove(name string, config *enginetypes.PluginRmConfig) error

+ 13 - 1
api/server/router/plugin/plugin_routes.go

@@ -4,6 +4,7 @@ import (
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
 	"net/http"
 	"net/http"
+	"strconv"
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/server/httputils"
@@ -56,7 +57,18 @@ func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter,
 }
 }
 
 
 func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
-	return pr.backend.Enable(vars["name"])
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	timeout, err := strconv.Atoi(r.Form.Get("timeout"))
+	if err != nil {
+		return err
+	}
+	config := &types.PluginEnableConfig{Timeout: timeout}
+
+	return pr.backend.Enable(name, config)
 }
 }
 
 
 func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 6 - 1
api/swagger.yaml

@@ -6307,7 +6307,7 @@ paths:
       summary: "Install a plugin"
       summary: "Install a plugin"
       operationId: "PostPluginsPull"
       operationId: "PostPluginsPull"
       description: |
       description: |
-        Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginEnable).
+        Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable).
       produces:
       produces:
         - "application/json"
         - "application/json"
       responses:
       responses:
@@ -6430,6 +6430,11 @@ paths:
           description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted."
           description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted."
           required: true
           required: true
           type: "string"
           type: "string"
+        - name: "timeout"
+          in: "query"
+          description: "Set the HTTP client timeout (in seconds)"
+          type: "integer"
+          default: 0
       tags:
       tags:
         - "Plugins"
         - "Plugins"
   /plugins/{name}/disable:
   /plugins/{name}/disable:

+ 5 - 0
api/types/client.go

@@ -332,6 +332,11 @@ type PluginRemoveOptions struct {
 	Force bool
 	Force bool
 }
 }
 
 
+// PluginEnableOptions holds parameters to enable plugins.
+type PluginEnableOptions struct {
+	Timeout int
+}
+
 // PluginInstallOptions holds parameters to install a plugin.
 // PluginInstallOptions holds parameters to install a plugin.
 type PluginInstallOptions struct {
 type PluginInstallOptions struct {
 	Disabled              bool
 	Disabled              bool

+ 5 - 0
api/types/configs.go

@@ -59,3 +59,8 @@ type ExecConfig struct {
 type PluginRmConfig struct {
 type PluginRmConfig struct {
 	ForceRemove bool
 	ForceRemove bool
 }
 }
+
+// PluginEnableConfig holds arguments for the plugin enable
+type PluginEnableConfig struct {
+	Timeout int
+}

+ 20 - 3
cli/command/plugin/enable.go

@@ -3,6 +3,7 @@ package plugin
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/reference"
@@ -10,20 +11,32 @@ import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
+type enableOpts struct {
+	timeout int
+	name    string
+}
+
 func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
+	var opts enableOpts
+
 	cmd := &cobra.Command{
 	cmd := &cobra.Command{
 		Use:   "enable PLUGIN",
 		Use:   "enable PLUGIN",
 		Short: "Enable a plugin",
 		Short: "Enable a plugin",
 		Args:  cli.ExactArgs(1),
 		Args:  cli.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return runEnable(dockerCli, args[0])
+			opts.name = args[0]
+			return runEnable(dockerCli, &opts)
 		},
 		},
 	}
 	}
 
 
+	flags := cmd.Flags()
+	flags.IntVar(&opts.timeout, "timeout", 0, "HTTP client timeout (in seconds)")
 	return cmd
 	return cmd
 }
 }
 
 
-func runEnable(dockerCli *command.DockerCli, name string) error {
+func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
+	name := opts.name
+
 	named, err := reference.ParseNamed(name) // FIXME: validate
 	named, err := reference.ParseNamed(name) // FIXME: validate
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -35,7 +48,11 @@ func runEnable(dockerCli *command.DockerCli, name string) error {
 	if !ok {
 	if !ok {
 		return fmt.Errorf("invalid name: %s", named.String())
 		return fmt.Errorf("invalid name: %s", named.String())
 	}
 	}
-	if err := dockerCli.Client().PluginEnable(context.Background(), ref.String()); err != nil {
+	if opts.timeout < 0 {
+		return fmt.Errorf("negative timeout %d is invalid", opts.timeout)
+	}
+
+	if err := dockerCli.Client().PluginEnable(context.Background(), ref.String(), types.PluginEnableOptions{Timeout: opts.timeout}); err != nil {
 		return err
 		return err
 	}
 	}
 	fmt.Fprintln(dockerCli.Out(), name)
 	fmt.Fprintln(dockerCli.Out(), name)

+ 1 - 1
client/interface.go

@@ -109,7 +109,7 @@ type NodeAPIClient interface {
 type PluginAPIClient interface {
 type PluginAPIClient interface {
 	PluginList(ctx context.Context) (types.PluginsListResponse, error)
 	PluginList(ctx context.Context) (types.PluginsListResponse, error)
 	PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error
 	PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error
-	PluginEnable(ctx context.Context, name string) error
+	PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error
 	PluginDisable(ctx context.Context, name string) error
 	PluginDisable(ctx context.Context, name string) error
 	PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error
 	PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error
 	PluginPush(ctx context.Context, name string, registryAuth string) error
 	PluginPush(ctx context.Context, name string, registryAuth string) error

+ 9 - 2
client/plugin_enable.go

@@ -1,12 +1,19 @@
 package client
 package client
 
 
 import (
 import (
+	"net/url"
+	"strconv"
+
+	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // PluginEnable enables a plugin
 // PluginEnable enables a plugin
-func (cli *Client) PluginEnable(ctx context.Context, name string) error {
-	resp, err := cli.post(ctx, "/plugins/"+name+"/enable", nil, nil, nil)
+func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error {
+	query := url.Values{}
+	query.Set("timeout", strconv.Itoa(options.Timeout))
+
+	resp, err := cli.post(ctx, "/plugins/"+name+"/enable", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)
 	return err
 	return err
 }
 }

+ 3 - 2
client/plugin_enable_test.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 
 
+	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -16,7 +17,7 @@ func TestPluginEnableError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	err := client.PluginEnable(context.Background(), "plugin_name")
+	err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -40,7 +41,7 @@ func TestPluginEnable(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	err := client.PluginEnable(context.Background(), "plugin_name")
+	err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 1 - 1
client/plugin_install.go

@@ -62,7 +62,7 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types
 		return nil
 		return nil
 	}
 	}
 
 
-	return cli.PluginEnable(ctx, name)
+	return cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0})
 }
 }
 
 
 func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
 func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {

+ 2 - 2
hack/validate/swagger-gen

@@ -14,12 +14,12 @@ if [ ${#files[@]} -gt 0 ]; then
 	diffs="$(git status --porcelain -- api/types/ 2>/dev/null)"
 	diffs="$(git status --porcelain -- api/types/ 2>/dev/null)"
 	if [ "$diffs" ]; then
 	if [ "$diffs" ]; then
 		{
 		{
-			echo 'The result of hack/geneate-swagger-api.sh differs'
+			echo 'The result of hack/generate-swagger-api.sh differs'
 			echo
 			echo
 			echo "$diffs"
 			echo "$diffs"
 			echo
 			echo
 			echo 'Please update api/swagger.yaml with any api changes, then '
 			echo 'Please update api/swagger.yaml with any api changes, then '
-			echo 'run `hack/geneate-swagger-api.sh`.'
+			echo 'run `hack/generate-swagger-api.sh`.'
 		} >&2
 		} >&2
 		false
 		false
 	else
 	else

+ 23 - 6
pkg/plugins/client.go

@@ -19,8 +19,7 @@ const (
 	defaultTimeOut = 30
 	defaultTimeOut = 30
 )
 )
 
 
-// NewClient creates a new plugin client (http).
-func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
+func newTransport(addr string, tlsConfig *tlsconfig.Options) (transport.Transport, error) {
 	tr := &http.Transport{}
 	tr := &http.Transport{}
 
 
 	if tlsConfig != nil {
 	if tlsConfig != nil {
@@ -45,15 +44,33 @@ func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
 	}
 	}
 	scheme := httpScheme(u)
 	scheme := httpScheme(u)
 
 
-	clientTransport := transport.NewHTTPTransport(tr, scheme, socket)
-	return NewClientWithTransport(clientTransport), nil
+	return transport.NewHTTPTransport(tr, scheme, socket), nil
+}
+
+// NewClient creates a new plugin client (http).
+func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
+	clientTransport, err := newTransport(addr, tlsConfig)
+	if err != nil {
+		return nil, err
+	}
+	return newClientWithTransport(clientTransport, 0), nil
+}
+
+// NewClientWithTimeout creates a new plugin client (http).
+func NewClientWithTimeout(addr string, tlsConfig *tlsconfig.Options, timeoutInSecs int) (*Client, error) {
+	clientTransport, err := newTransport(addr, tlsConfig)
+	if err != nil {
+		return nil, err
+	}
+	return newClientWithTransport(clientTransport, timeoutInSecs), nil
 }
 }
 
 
-// NewClientWithTransport creates a new plugin client with a given transport.
-func NewClientWithTransport(tr transport.Transport) *Client {
+// newClientWithTransport creates a new plugin client with a given transport.
+func newClientWithTransport(tr transport.Transport, timeoutInSecs int) *Client {
 	return &Client{
 	return &Client{
 		http: &http.Client{
 		http: &http.Client{
 			Transport: tr,
 			Transport: tr,
+			Timeout:   time.Duration(timeoutInSecs) * time.Second,
 		},
 		},
 		requestFactory: tr,
 		requestFactory: tr,
 	}
 	}

+ 4 - 1
plugin/backend_linux.go

@@ -36,11 +36,14 @@ func (pm *Manager) Disable(name string) error {
 }
 }
 
 
 // Enable activates a plugin, which implies that they are ready to be used by containers.
 // Enable activates a plugin, which implies that they are ready to be used by containers.
-func (pm *Manager) Enable(name string) error {
+func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
+
 	p, err := pm.pluginStore.GetByName(name)
 	p, err := pm.pluginStore.GetByName(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
+	p.TimeoutInSecs = config.Timeout
 	if err := pm.enable(p, false); err != nil {
 	if err := pm.enable(p, false); err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
plugin/backend_unsupported.go

@@ -19,7 +19,7 @@ func (pm *Manager) Disable(name string) error {
 }
 }
 
 
 // Enable activates a plugin, which implies that they are ready to be used by containers.
 // Enable activates a plugin, which implies that they are ready to be used by containers.
-func (pm *Manager) Enable(name string) error {
+func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
 	return errNotSupported
 	return errNotSupported
 }
 }
 
 

+ 1 - 1
plugin/manager_linux.go

@@ -31,7 +31,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
 		return err
 		return err
 	}
 	}
 
 
-	p.PClient, err = plugins.NewClient("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil)
+	p.PClient, err = plugins.NewClientWithTimeout("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil, p.TimeoutInSecs)
 	if err != nil {
 	if err != nil {
 		p.Lock()
 		p.Lock()
 		p.Restart = false
 		p.Restart = false

+ 1 - 0
plugin/v2/plugin.go

@@ -24,6 +24,7 @@ type Plugin struct {
 	Restart           bool            `json:"-"`
 	Restart           bool            `json:"-"`
 	ExitChan          chan bool       `json:"-"`
 	ExitChan          chan bool       `json:"-"`
 	LibRoot           string          `json:"-"`
 	LibRoot           string          `json:"-"`
+	TimeoutInSecs     int             `json:"-"`
 }
 }
 
 
 const defaultPluginRuntimeDestination = "/run/docker/plugins"
 const defaultPluginRuntimeDestination = "/run/docker/plugins"