Signed-off-by: Anusha Ragunathan <anusha@docker.com>
| ... | ... |
@@ -11,7 +11,7 @@ import ( |
| 11 | 11 |
// Backend for Plugin |
| 12 | 12 |
type Backend interface {
|
| 13 | 13 |
Disable(name string) error |
| 14 |
- Enable(name string) error |
|
| 14 |
+ Enable(name string, config *enginetypes.PluginEnableConfig) error |
|
| 15 | 15 |
List() ([]enginetypes.Plugin, error) |
| 16 | 16 |
Inspect(name string) (enginetypes.Plugin, error) |
| 17 | 17 |
Remove(name string, config *enginetypes.PluginRmConfig) error |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"encoding/base64" |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"net/http" |
| 7 |
+ "strconv" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
|
| 9 | 10 |
"github.com/docker/docker/api/server/httputils" |
| ... | ... |
@@ -56,7 +57,18 @@ func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 | 58 |
func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| 59 |
- return pr.backend.Enable(vars["name"]) |
|
| 59 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 60 |
+ return err |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ name := vars["name"] |
|
| 64 |
+ timeout, err := strconv.Atoi(r.Form.Get("timeout"))
|
|
| 65 |
+ if err != nil {
|
|
| 66 |
+ return err |
|
| 67 |
+ } |
|
| 68 |
+ config := &types.PluginEnableConfig{Timeout: timeout}
|
|
| 69 |
+ |
|
| 70 |
+ return pr.backend.Enable(name, config) |
|
| 60 | 71 |
} |
| 61 | 72 |
|
| 62 | 73 |
func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| ... | ... |
@@ -6307,7 +6307,7 @@ paths: |
| 6307 | 6307 |
summary: "Install a plugin" |
| 6308 | 6308 |
operationId: "PostPluginsPull" |
| 6309 | 6309 |
description: | |
| 6310 |
- Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginEnable).
|
|
| 6310 |
+ Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable).
|
|
| 6311 | 6311 |
produces: |
| 6312 | 6312 |
- "application/json" |
| 6313 | 6313 |
responses: |
| ... | ... |
@@ -6430,6 +6430,11 @@ paths: |
| 6430 | 6430 |
description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." |
| 6431 | 6431 |
required: true |
| 6432 | 6432 |
type: "string" |
| 6433 |
+ - name: "timeout" |
|
| 6434 |
+ in: "query" |
|
| 6435 |
+ description: "Set the HTTP client timeout (in seconds)" |
|
| 6436 |
+ type: "integer" |
|
| 6437 |
+ default: 0 |
|
| 6433 | 6438 |
tags: |
| 6434 | 6439 |
- "Plugins" |
| 6435 | 6440 |
/plugins/{name}/disable:
|
| ... | ... |
@@ -332,6 +332,11 @@ type PluginRemoveOptions struct {
|
| 332 | 332 |
Force bool |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
+// PluginEnableOptions holds parameters to enable plugins. |
|
| 336 |
+type PluginEnableOptions struct {
|
|
| 337 |
+ Timeout int |
|
| 338 |
+} |
|
| 339 |
+ |
|
| 335 | 340 |
// PluginInstallOptions holds parameters to install a plugin. |
| 336 | 341 |
type PluginInstallOptions struct {
|
| 337 | 342 |
Disabled bool |
| ... | ... |
@@ -3,6 +3,7 @@ package plugin |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
|
| 6 |
+ "github.com/docker/docker/api/types" |
|
| 6 | 7 |
"github.com/docker/docker/cli" |
| 7 | 8 |
"github.com/docker/docker/cli/command" |
| 8 | 9 |
"github.com/docker/docker/reference" |
| ... | ... |
@@ -10,20 +11,32 @@ import ( |
| 10 | 10 |
"golang.org/x/net/context" |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
+type enableOpts struct {
|
|
| 14 |
+ timeout int |
|
| 15 |
+ name string |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 13 | 18 |
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 19 |
+ var opts enableOpts |
|
| 20 |
+ |
|
| 14 | 21 |
cmd := &cobra.Command{
|
| 15 | 22 |
Use: "enable PLUGIN", |
| 16 | 23 |
Short: "Enable a plugin", |
| 17 | 24 |
Args: cli.ExactArgs(1), |
| 18 | 25 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 19 |
- return runEnable(dockerCli, args[0]) |
|
| 26 |
+ opts.name = args[0] |
|
| 27 |
+ return runEnable(dockerCli, &opts) |
|
| 20 | 28 |
}, |
| 21 | 29 |
} |
| 22 | 30 |
|
| 31 |
+ flags := cmd.Flags() |
|
| 32 |
+ flags.IntVar(&opts.timeout, "timeout", 0, "HTTP client timeout (in seconds)") |
|
| 23 | 33 |
return cmd |
| 24 | 34 |
} |
| 25 | 35 |
|
| 26 |
-func runEnable(dockerCli *command.DockerCli, name string) error {
|
|
| 36 |
+func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
|
|
| 37 |
+ name := opts.name |
|
| 38 |
+ |
|
| 27 | 39 |
named, err := reference.ParseNamed(name) // FIXME: validate |
| 28 | 40 |
if err != nil {
|
| 29 | 41 |
return err |
| ... | ... |
@@ -35,7 +48,11 @@ func runEnable(dockerCli *command.DockerCli, name string) error {
|
| 35 | 35 |
if !ok {
|
| 36 | 36 |
return fmt.Errorf("invalid name: %s", named.String())
|
| 37 | 37 |
} |
| 38 |
- if err := dockerCli.Client().PluginEnable(context.Background(), ref.String()); err != nil {
|
|
| 38 |
+ if opts.timeout < 0 {
|
|
| 39 |
+ return fmt.Errorf("negative timeout %d is invalid", opts.timeout)
|
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ if err := dockerCli.Client().PluginEnable(context.Background(), ref.String(), types.PluginEnableOptions{Timeout: opts.timeout}); err != nil {
|
|
| 39 | 43 |
return err |
| 40 | 44 |
} |
| 41 | 45 |
fmt.Fprintln(dockerCli.Out(), name) |
| ... | ... |
@@ -109,7 +109,7 @@ type NodeAPIClient interface {
|
| 109 | 109 |
type PluginAPIClient interface {
|
| 110 | 110 |
PluginList(ctx context.Context) (types.PluginsListResponse, error) |
| 111 | 111 |
PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error |
| 112 |
- PluginEnable(ctx context.Context, name string) error |
|
| 112 |
+ PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error |
|
| 113 | 113 |
PluginDisable(ctx context.Context, name string) error |
| 114 | 114 |
PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error |
| 115 | 115 |
PluginPush(ctx context.Context, name string, registryAuth string) error |
| ... | ... |
@@ -1,12 +1,19 @@ |
| 1 | 1 |
package client |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "net/url" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/api/types" |
|
| 4 | 8 |
"golang.org/x/net/context" |
| 5 | 9 |
) |
| 6 | 10 |
|
| 7 | 11 |
// PluginEnable enables a plugin |
| 8 |
-func (cli *Client) PluginEnable(ctx context.Context, name string) error {
|
|
| 9 |
- resp, err := cli.post(ctx, "/plugins/"+name+"/enable", nil, nil, nil) |
|
| 12 |
+func (cli *Client) PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error {
|
|
| 13 |
+ query := url.Values{}
|
|
| 14 |
+ query.Set("timeout", strconv.Itoa(options.Timeout))
|
|
| 15 |
+ |
|
| 16 |
+ resp, err := cli.post(ctx, "/plugins/"+name+"/enable", query, nil, nil) |
|
| 10 | 17 |
ensureReaderClosed(resp) |
| 11 | 18 |
return err |
| 12 | 19 |
} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"strings" |
| 9 | 9 |
"testing" |
| 10 | 10 |
|
| 11 |
+ "github.com/docker/docker/api/types" |
|
| 11 | 12 |
"golang.org/x/net/context" |
| 12 | 13 |
) |
| 13 | 14 |
|
| ... | ... |
@@ -16,7 +17,7 @@ func TestPluginEnableError(t *testing.T) {
|
| 16 | 16 |
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), |
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 |
- err := client.PluginEnable(context.Background(), "plugin_name") |
|
| 19 |
+ err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
|
| 20 | 20 |
if err == nil || err.Error() != "Error response from daemon: Server error" {
|
| 21 | 21 |
t.Fatalf("expected a Server Error, got %v", err)
|
| 22 | 22 |
} |
| ... | ... |
@@ -40,7 +41,7 @@ func TestPluginEnable(t *testing.T) {
|
| 40 | 40 |
}), |
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
- err := client.PluginEnable(context.Background(), "plugin_name") |
|
| 43 |
+ err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{})
|
|
| 44 | 44 |
if err != nil {
|
| 45 | 45 |
t.Fatal(err) |
| 46 | 46 |
} |
| ... | ... |
@@ -62,7 +62,7 @@ func (cli *Client) PluginInstall(ctx context.Context, name string, options types |
| 62 | 62 |
return nil |
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
- return cli.PluginEnable(ctx, name) |
|
| 65 |
+ return cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0})
|
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 | 68 |
func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
| ... | ... |
@@ -14,12 +14,12 @@ if [ ${#files[@]} -gt 0 ]; then
|
| 14 | 14 |
diffs="$(git status --porcelain -- api/types/ 2>/dev/null)" |
| 15 | 15 |
if [ "$diffs" ]; then |
| 16 | 16 |
{
|
| 17 |
- echo 'The result of hack/geneate-swagger-api.sh differs' |
|
| 17 |
+ echo 'The result of hack/generate-swagger-api.sh differs' |
|
| 18 | 18 |
echo |
| 19 | 19 |
echo "$diffs" |
| 20 | 20 |
echo |
| 21 | 21 |
echo 'Please update api/swagger.yaml with any api changes, then ' |
| 22 |
- echo 'run `hack/geneate-swagger-api.sh`.' |
|
| 22 |
+ echo 'run `hack/generate-swagger-api.sh`.' |
|
| 23 | 23 |
} >&2 |
| 24 | 24 |
false |
| 25 | 25 |
else |
| ... | ... |
@@ -19,8 +19,7 @@ const ( |
| 19 | 19 |
defaultTimeOut = 30 |
| 20 | 20 |
) |
| 21 | 21 |
|
| 22 |
-// NewClient creates a new plugin client (http). |
|
| 23 |
-func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
|
| 22 |
+func newTransport(addr string, tlsConfig *tlsconfig.Options) (transport.Transport, error) {
|
|
| 24 | 23 |
tr := &http.Transport{}
|
| 25 | 24 |
|
| 26 | 25 |
if tlsConfig != nil {
|
| ... | ... |
@@ -45,15 +44,33 @@ func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
| 45 | 45 |
} |
| 46 | 46 |
scheme := httpScheme(u) |
| 47 | 47 |
|
| 48 |
- clientTransport := transport.NewHTTPTransport(tr, scheme, socket) |
|
| 49 |
- return NewClientWithTransport(clientTransport), nil |
|
| 48 |
+ return transport.NewHTTPTransport(tr, scheme, socket), nil |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// NewClient creates a new plugin client (http). |
|
| 52 |
+func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
|
| 53 |
+ clientTransport, err := newTransport(addr, tlsConfig) |
|
| 54 |
+ if err != nil {
|
|
| 55 |
+ return nil, err |
|
| 56 |
+ } |
|
| 57 |
+ return newClientWithTransport(clientTransport, 0), nil |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+// NewClientWithTimeout creates a new plugin client (http). |
|
| 61 |
+func NewClientWithTimeout(addr string, tlsConfig *tlsconfig.Options, timeoutInSecs int) (*Client, error) {
|
|
| 62 |
+ clientTransport, err := newTransport(addr, tlsConfig) |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ return nil, err |
|
| 65 |
+ } |
|
| 66 |
+ return newClientWithTransport(clientTransport, timeoutInSecs), nil |
|
| 50 | 67 |
} |
| 51 | 68 |
|
| 52 |
-// NewClientWithTransport creates a new plugin client with a given transport. |
|
| 53 |
-func NewClientWithTransport(tr transport.Transport) *Client {
|
|
| 69 |
+// newClientWithTransport creates a new plugin client with a given transport. |
|
| 70 |
+func newClientWithTransport(tr transport.Transport, timeoutInSecs int) *Client {
|
|
| 54 | 71 |
return &Client{
|
| 55 | 72 |
http: &http.Client{
|
| 56 | 73 |
Transport: tr, |
| 74 |
+ Timeout: time.Duration(timeoutInSecs) * time.Second, |
|
| 57 | 75 |
}, |
| 58 | 76 |
requestFactory: tr, |
| 59 | 77 |
} |
| ... | ... |
@@ -36,11 +36,14 @@ func (pm *Manager) Disable(name string) error {
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
// Enable activates a plugin, which implies that they are ready to be used by containers. |
| 39 |
-func (pm *Manager) Enable(name string) error {
|
|
| 39 |
+func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
|
|
| 40 |
+ |
|
| 40 | 41 |
p, err := pm.pluginStore.GetByName(name) |
| 41 | 42 |
if err != nil {
|
| 42 | 43 |
return err |
| 43 | 44 |
} |
| 45 |
+ |
|
| 46 |
+ p.TimeoutInSecs = config.Timeout |
|
| 44 | 47 |
if err := pm.enable(p, false); err != nil {
|
| 45 | 48 |
return err |
| 46 | 49 |
} |
| ... | ... |
@@ -19,7 +19,7 @@ func (pm *Manager) Disable(name string) error {
|
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 | 21 |
// Enable activates a plugin, which implies that they are ready to be used by containers. |
| 22 |
-func (pm *Manager) Enable(name string) error {
|
|
| 22 |
+func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
|
|
| 23 | 23 |
return errNotSupported |
| 24 | 24 |
} |
| 25 | 25 |
|
| ... | ... |
@@ -31,7 +31,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
|
| 31 | 31 |
return err |
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 |
- p.PClient, err = plugins.NewClient("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil)
|
|
| 34 |
+ p.PClient, err = plugins.NewClientWithTimeout("unix://"+filepath.Join(p.RuntimeSourcePath, p.GetSocket()), nil, p.TimeoutInSecs)
|
|
| 35 | 35 |
if err != nil {
|
| 36 | 36 |
p.Lock() |
| 37 | 37 |
p.Restart = false |