1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Add --force to docker plugin remove

Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Victor Vieux 2016-07-22 08:24:54 -07:00
parent cfd2719bf7
commit 0016b331da
13 changed files with 86 additions and 24 deletions

View file

@ -8,27 +8,41 @@ import (
"github.com/docker/docker/api/client" "github.com/docker/docker/api/client"
"github.com/docker/docker/cli" "github.com/docker/docker/cli"
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
"github.com/docker/engine-api/types"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type rmOptions struct {
force bool
plugins []string
}
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command { func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts rmOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rm PLUGIN", Use: "rm [OPTIONS] PLUGIN [PLUGIN...]",
Short: "Remove a plugin", Short: "Remove one or more plugins",
Aliases: []string{"remove"}, Aliases: []string{"remove"},
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 runRemove(dockerCli, args) opts.plugins = args
return runRemove(dockerCli, &opts)
}, },
} }
flags := cmd.Flags()
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of an active plugin")
return cmd return cmd
} }
func runRemove(dockerCli *client.DockerCli, names []string) error { func runRemove(dockerCli *client.DockerCli, opts *rmOptions) error {
ctx := context.Background()
var errs cli.Errors var errs cli.Errors
for _, name := range names { for _, name := range opts.plugins {
named, err := reference.ParseNamed(name) // FIXME: validate named, err := reference.ParseNamed(name) // FIXME: validate
if err != nil { if err != nil {
return err return err
@ -41,7 +55,7 @@ func runRemove(dockerCli *client.DockerCli, names []string) error {
return fmt.Errorf("invalid name: %s", named.String()) return fmt.Errorf("invalid name: %s", named.String())
} }
// TODO: pass names to api instead of making multiple api calls // TODO: pass names to api instead of making multiple api calls
if err := dockerCli.Client().PluginRemove(context.Background(), ref.String()); err != nil { if err := dockerCli.Client().PluginRemove(ctx, ref.String(), types.PluginRemoveOptions{Force: opts.force}); err != nil {
errs = append(errs, err) errs = append(errs, err)
continue continue
} }

View file

@ -14,7 +14,7 @@ type Backend interface {
Enable(name string) error Enable(name string) error
List() ([]enginetypes.Plugin, error) List() ([]enginetypes.Plugin, error)
Inspect(name string) (enginetypes.Plugin, error) Inspect(name string) (enginetypes.Plugin, error)
Remove(name string) error Remove(name string, config *enginetypes.PluginRmConfig) error
Set(name string, args []string) error Set(name string, args []string) error
Pull(name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig) (enginetypes.PluginPrivileges, error) Pull(name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig) (enginetypes.PluginPrivileges, error)
Push(name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig) error Push(name string, metaHeaders http.Header, authConfig *enginetypes.AuthConfig) error

View file

@ -51,7 +51,15 @@ func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter
} }
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 {
return pr.backend.Remove(vars["name"]) if err := httputils.ParseForm(r); err != nil {
return err
}
name := vars["name"]
config := &types.PluginRmConfig{
ForceRemove: httputils.BoolValue(r, "force"),
}
return pr.backend.Remove(name, config)
} }
func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

View file

@ -12,7 +12,7 @@ parent = "smn_cli"
# plugin rm (experimental) # plugin rm (experimental)
```markdown ```markdown
Usage: docker plugin rm PLUGIN Usage: docker plugin rm [OPTIONS] PLUGIN [PLUGIN...]
Remove a plugin Remove a plugin
@ -20,12 +20,14 @@ Aliases:
rm, remove rm, remove
Options: Options:
--help Print usage -f, --force Force the removal of an active plugin
--help Print usage
``` ```
Removes a plugin. You cannot remove a plugin if it is active, you must disable Removes a plugin. You cannot remove a plugin if it is active, you must disable
a plugin using the [`docker plugin disable`](plugin_disable.md) before removing a plugin using the [`docker plugin disable`](plugin_disable.md) before removing
it. it (or use --force, use of force is not recommended, since it can affect
functioning of running containers using the plugin).
The following example disables and removes the `no-remove:latest` plugin; The following example disables and removes the `no-remove:latest` plugin;

View file

@ -61,7 +61,7 @@ clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://gith
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d
clone git github.com/docker/engine-api 228c7390a733320d48697cb41ae8cde4942cd3e5 clone git github.com/docker/engine-api 603ec41824c63d1e6498a22271987fa1f268c0c0
clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837 clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
clone git github.com/imdario/mergo 0.2.1 clone git github.com/imdario/mergo 0.2.1

View file

@ -57,6 +57,19 @@ func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
} }
} }
func (s *DockerSuite) TestPluginForceRemove(c *check.C) {
testRequires(c, DaemonIsLinux, ExperimentalDaemon)
out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
c.Assert(err, checker.IsNil)
out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
c.Assert(out, checker.Contains, "is active")
out, _, err = dockerCmdWithError("plugin", "remove", "--force", pNameWithTag)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, pNameWithTag)
}
func (s *DockerSuite) TestPluginInstallDisable(c *check.C) { func (s *DockerSuite) TestPluginInstallDisable(c *check.C) {
testRequires(c, DaemonIsLinux, ExperimentalDaemon) testRequires(c, DaemonIsLinux, ExperimentalDaemon)
out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName) out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName)

View file

@ -131,12 +131,12 @@ func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.A
} }
// Remove deletes plugin's root directory. // Remove deletes plugin's root directory.
func (pm *Manager) Remove(name string) error { func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
p, err := pm.get(name) p, err := pm.get(name)
if err != nil { if err != nil {
return err return err
} }
if err := pm.remove(p); err != nil { if err := pm.remove(p, config.ForceRemove); err != nil {
return err return err
} }
pm.pluginEventLogger(p.PluginObj.ID, name, "remove") pm.pluginEventLogger(p.PluginObj.ID, name, "remove")

View file

@ -310,7 +310,7 @@ func (pm *Manager) init() error {
go func(p *plugin) { go func(p *plugin) {
defer group.Done() defer group.Done()
if err := pm.restorePlugin(p); err != nil { if err := pm.restorePlugin(p); err != nil {
logrus.Errorf("Error restoring plugin '%s': %s", p.Name(), err) logrus.Errorf("failed to restore plugin '%s': %s", p.Name(), err)
return return
} }
@ -322,7 +322,7 @@ func (pm *Manager) init() error {
if requiresManualRestore { if requiresManualRestore {
// if liveRestore is not enabled, the plugin will be stopped now so we should enable it // if liveRestore is not enabled, the plugin will be stopped now so we should enable it
if err := pm.enable(p); err != nil { if err := pm.enable(p); err != nil {
logrus.Errorf("Error enabling plugin '%s': %s", p.Name(), err) logrus.Errorf("failed to enable plugin '%s': %s", p.Name(), err)
} }
} }
}(p) }(p)
@ -363,9 +363,14 @@ func (pm *Manager) initPlugin(p *plugin) error {
return err return err
} }
func (pm *Manager) remove(p *plugin) error { func (pm *Manager) remove(p *plugin, force bool) error {
if p.PluginObj.Active { if p.PluginObj.Active {
return fmt.Errorf("plugin %s is active", p.Name()) if !force {
return fmt.Errorf("plugin %s is active", p.Name())
}
if err := pm.disable(p); err != nil {
logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
}
} }
pm.Lock() // fixme: lock single record pm.Lock() // fixme: lock single record
defer pm.Unlock() defer pm.Unlock()
@ -380,7 +385,7 @@ func (pm *Manager) set(p *plugin, args []string) error {
for _, arg := range args { for _, arg := range args {
i := strings.Index(arg, "=") i := strings.Index(arg, "=")
if i < 0 { if i < 0 {
return fmt.Errorf("No equal sign '=' found in %s", arg) return fmt.Errorf("no equal sign '=' found in %s", arg)
} }
m[arg[:i]] = arg[i+1:] m[arg[:i]] = arg[i+1:]
} }
@ -393,7 +398,7 @@ func (pm *Manager) save() error {
jsonData, err := json.Marshal(pm.plugins) jsonData, err := json.Marshal(pm.plugins)
if err != nil { if err != nil {
logrus.Debugf("Error in json.Marshal: %v", err) logrus.Debugf("failure in json.Marshal: %v", err)
return err return err
} }
ioutils.AtomicWriteFile(filePath, jsonData, 0600) ioutils.AtomicWriteFile(filePath, jsonData, 0600)

View file

@ -24,7 +24,7 @@ type CheckpointAPIClient interface {
// PluginAPIClient defines API client methods for the plugins // PluginAPIClient defines API client methods for the plugins
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) error PluginRemove(ctx context.Context, name string, options types.PluginRemoveOptions) error
PluginEnable(ctx context.Context, name string) error PluginEnable(ctx context.Context, name string) 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

View file

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

View file

@ -289,3 +289,8 @@ type ServiceListOptions struct {
type TaskListOptions struct { type TaskListOptions struct {
Filter filters.Args Filter filters.Args
} }
// PluginRemoveOptions holds parameters to remove plugins.
type PluginRemoveOptions struct {
Force bool
}

View file

@ -51,3 +51,10 @@ type ExecConfig struct {
DetachKeys string // Escape keys for detach DetachKeys string // Escape keys for detach
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.
type PluginRmConfig struct {
ForceRemove bool
}

View file

@ -36,7 +36,7 @@ type HealthConfig struct {
type Config struct { type Config struct {
Hostname string // Hostname Hostname string // Hostname
Domainname string // Domainname Domainname string // Domainname
User string // User that will run the command(s) inside the container User string // User that will run the command(s) inside the container, also support user:group
AttachStdin bool // Attach the standard input, makes possible user interaction AttachStdin bool // Attach the standard input, makes possible user interaction
AttachStdout bool // Attach the standard output AttachStdout bool // Attach the standard output
AttachStderr bool // Attach the standard error AttachStderr bool // Attach the standard error