ソースを参照

Merge pull request #29599 from anusha-ragunathan/refcount

Enforce zero plugin refcount during disable, not remove.
Anusha Ragunathan 8 年 前
コミット
d1dfc1a5ef

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

@@ -10,7 +10,7 @@ import (
 
 
 // Backend for Plugin
 // Backend for Plugin
 type Backend interface {
 type Backend interface {
-	Disable(name string) error
+	Disable(name string, config *enginetypes.PluginDisableConfig) error
 	Enable(name string, config *enginetypes.PluginEnableConfig) 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)

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

@@ -99,7 +99,16 @@ func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter,
 }
 }
 
 
 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 {
-	return pr.backend.Disable(vars["name"])
+	if err := httputils.ParseForm(r); err != nil {
+		return err
+	}
+
+	name := vars["name"]
+	config := &types.PluginDisableConfig{
+		ForceDisable: httputils.BoolValue(r, "force"),
+	}
+
+	return pr.backend.Disable(name, config)
 }
 }
 
 
 func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 5 - 0
api/types/client.go

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

+ 7 - 4
api/types/configs.go

@@ -53,14 +53,17 @@ type ExecConfig struct {
 	Cmd          []string // Execution commands and args
 	Cmd          []string // Execution commands and args
 }
 }
 
 
-// PluginRmConfig holds arguments for the plugin remove
-// operation. This struct is used to tell the backend what operations
-// to perform.
+// PluginRmConfig holds arguments for plugin remove.
 type PluginRmConfig struct {
 type PluginRmConfig struct {
 	ForceRemove bool
 	ForceRemove bool
 }
 }
 
 
-// PluginEnableConfig holds arguments for the plugin enable
+// PluginEnableConfig holds arguments for plugin enable
 type PluginEnableConfig struct {
 type PluginEnableConfig struct {
 	Timeout int
 	Timeout int
 }
 }
+
+// PluginDisableConfig holds arguments for plugin disable.
+type PluginDisableConfig struct {
+	ForceDisable bool
+}

+ 8 - 3
cli/command/plugin/disable.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"
@@ -11,19 +12,23 @@ import (
 )
 )
 
 
 func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
+	var force bool
+
 	cmd := &cobra.Command{
 	cmd := &cobra.Command{
 		Use:   "disable PLUGIN",
 		Use:   "disable PLUGIN",
 		Short: "Disable a plugin",
 		Short: "Disable 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 runDisable(dockerCli, args[0])
+			return runDisable(dockerCli, args[0], force)
 		},
 		},
 	}
 	}
 
 
+	flags := cmd.Flags()
+	flags.BoolVarP(&force, "force", "f", false, "Force the disable of an active plugin")
 	return cmd
 	return cmd
 }
 }
 
 
-func runDisable(dockerCli *command.DockerCli, name string) error {
+func runDisable(dockerCli *command.DockerCli, name string, force bool) error {
 	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 +40,7 @@ func runDisable(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().PluginDisable(context.Background(), ref.String()); err != nil {
+	if err := dockerCli.Client().PluginDisable(context.Background(), ref.String(), types.PluginDisableOptions{Force: force}); err != nil {
 		return err
 		return err
 	}
 	}
 	fmt.Fprintln(dockerCli.Out(), name)
 	fmt.Fprintln(dockerCli.Out(), name)

+ 1 - 1
client/interface.go

@@ -110,7 +110,7 @@ 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, options types.PluginEnableOptions) error
 	PluginEnable(ctx context.Context, name string, options types.PluginEnableOptions) error
-	PluginDisable(ctx context.Context, name string) error
+	PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) 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
 	PluginSet(ctx context.Context, name string, args []string) error
 	PluginSet(ctx context.Context, name string, args []string) error

+ 9 - 2
client/plugin_disable.go

@@ -1,12 +1,19 @@
 package client
 package client
 
 
 import (
 import (
+	"net/url"
+
+	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // PluginDisable disables a plugin
 // PluginDisable disables a plugin
-func (cli *Client) PluginDisable(ctx context.Context, name string) error {
-	resp, err := cli.post(ctx, "/plugins/"+name+"/disable", nil, nil, nil)
+func (cli *Client) PluginDisable(ctx context.Context, name string, options types.PluginDisableOptions) error {
+	query := url.Values{}
+	if options.Force {
+		query.Set("force", "1")
+	}
+	resp, err := cli.post(ctx, "/plugins/"+name+"/disable", query, nil, nil)
 	ensureReaderClosed(resp)
 	ensureReaderClosed(resp)
 	return err
 	return err
 }
 }

+ 3 - 2
client/plugin_disable_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 TestPluginDisableError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	err := client.PluginDisable(context.Background(), "plugin_name")
+	err := client.PluginDisable(context.Background(), "plugin_name", types.PluginDisableOptions{})
 	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 TestPluginDisable(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	err := client.PluginDisable(context.Background(), "plugin_name")
+	err := client.PluginDisable(context.Background(), "plugin_name", types.PluginDisableOptions{})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 4 - 2
docs/reference/commandline/plugin_disable.md

@@ -21,11 +21,13 @@ Usage:  docker plugin disable PLUGIN
 Disable a plugin
 Disable a plugin
 
 
 Options:
 Options:
-      --help   Print usage
+  -f, --force   Force the disable of an active plugin
+      --help    Print usage
 ```
 ```
 
 
 Disables a plugin. The plugin must be installed before it can be disabled,
 Disables a plugin. The plugin must be installed before it can be disabled,
-see [`docker plugin install`](plugin_install.md).
+see [`docker plugin install`](plugin_install.md). Without the `-f` option,
+a plugin that has references (eg, volumes, networks) cannot be disabled.
 
 
 
 
 The following example shows that the `no-remove` plugin is installed
 The following example shows that the `no-remove` plugin is installed

+ 9 - 2
integration-cli/docker_cli_daemon_plugins_test.go

@@ -268,10 +268,17 @@ func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) {
 	s.d.Restart(c, "--live-restore=true")
 	s.d.Restart(c, "--live-restore=true")
 
 
 	out, err = s.d.Cmd("plugin", "disable", pName)
 	out, err = s.d.Cmd("plugin", "disable", pName)
-	c.Assert(err, checker.IsNil, check.Commentf(out))
-	out, err = s.d.Cmd("plugin", "rm", pName)
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "in use")
 	c.Assert(out, checker.Contains, "in use")
+
+	out, err = s.d.Cmd("volume", "rm", "test")
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+
+	out, err = s.d.Cmd("plugin", "disable", pName)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
+
+	out, err = s.d.Cmd("plugin", "rm", pName)
+	c.Assert(err, checker.IsNil, check.Commentf(out))
 }
 }
 
 
 func existsMountpointWithPrefix(mountpointPrefix string) (bool, error) {
 func existsMountpointWithPrefix(mountpointPrefix string) (bool, error) {

+ 5 - 10
integration-cli/docker_cli_plugins_test.go

@@ -64,23 +64,18 @@ func (s *DockerSuite) TestPluginForceRemove(c *check.C) {
 
 
 func (s *DockerSuite) TestPluginActive(c *check.C) {
 func (s *DockerSuite) TestPluginActive(c *check.C) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
-	out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
+	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 
 
-	out, _, err = dockerCmdWithError("volume", "create", "-d", pNameWithTag)
+	_, _, err = dockerCmdWithError("volume", "create", "-d", pNameWithTag, "--name", "testvol1")
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 
 
-	vID := strings.TrimSpace(out)
-
-	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(out, checker.Contains, "is in use")
+	out, _, err := dockerCmdWithError("plugin", "disable", pNameWithTag)
+	c.Assert(out, checker.Contains, "in use")
 
 
-	_, _, err = dockerCmdWithError("volume", "rm", vID)
+	_, _, err = dockerCmdWithError("volume", "rm", "testvol1")
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 
 
-	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(out, checker.Contains, "is enabled")
-
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 
 

+ 6 - 2
plugin/backend_linux.go

@@ -31,8 +31,8 @@ var (
 	validPartialID = regexp.MustCompile(`^([a-f0-9]{1,64})$`)
 	validPartialID = regexp.MustCompile(`^([a-f0-9]{1,64})$`)
 )
 )
 
 
-// Disable deactivates a plugin, which implies that they cannot be used by containers.
-func (pm *Manager) Disable(name string) error {
+// Disable deactivates a plugin. This means resources (volumes, networks) cant use them.
+func (pm *Manager) Disable(name string, config *types.PluginDisableConfig) error {
 	p, err := pm.pluginStore.GetByName(name)
 	p, err := pm.pluginStore.GetByName(name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -41,6 +41,10 @@ func (pm *Manager) Disable(name string) error {
 	c := pm.cMap[p]
 	c := pm.cMap[p]
 	pm.mu.RUnlock()
 	pm.mu.RUnlock()
 
 
+	if !config.ForceDisable && p.GetRefCount() > 0 {
+		return fmt.Errorf("plugin %s is in use", p.Name())
+	}
+
 	if err := pm.disable(p, c); err != nil {
 	if err := pm.disable(p, c); err != nil {
 		return err
 		return err
 	}
 	}

+ 1 - 1
plugin/backend_unsupported.go

@@ -14,7 +14,7 @@ import (
 var errNotSupported = errors.New("plugins are not supported on this platform")
 var errNotSupported = errors.New("plugins are not supported on this platform")
 
 
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
-func (pm *Manager) Disable(name string) error {
+func (pm *Manager) Disable(name string, config *types.PluginDisableConfig) error {
 	return errNotSupported
 	return errNotSupported
 }
 }