Add --force to node removal

Signed-off-by: Diogo Monica <diogo.monica@gmail.com>
(cherry picked from commit a327c231b5)
Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
Diogo Monica 2016-07-27 21:17:00 -07:00 committed by Tibor Vass
parent b32462e2a4
commit caaf53ad3e
7 changed files with 87 additions and 10 deletions

View file

@ -7,26 +7,36 @@ import (
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/engine-api/types"
"github.com/spf13/cobra"
)
type removeOptions struct {
force bool
}
func newRemoveCommand(dockerCli *client.DockerCli) *cobra.Command {
return &cobra.Command{
Use: "rm NODE [NODE...]",
opts := removeOptions{}
cmd := &cobra.Command{
Use: "rm [OPTIONS] NODE [NODE...]",
Aliases: []string{"remove"},
Short: "Remove one or more nodes from the swarm",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runRemove(dockerCli, args)
return runRemove(dockerCli, args, opts)
},
}
flags := cmd.Flags()
flags.BoolVar(&opts.force, "force", false, "Force remove an active node")
return cmd
}
func runRemove(dockerCli *client.DockerCli, args []string) error {
func runRemove(dockerCli *client.DockerCli, args []string, opts removeOptions) error {
client := dockerCli.Client()
ctx := context.Background()
for _, nodeID := range args {
err := client.NodeRemove(ctx, nodeID)
err := client.NodeRemove(ctx, nodeID, types.NodeRemoveOptions{Force: opts.force})
if err != nil {
return err
}

View file

@ -20,7 +20,7 @@ type Backend interface {
GetNodes(basictypes.NodeListOptions) ([]types.Node, error)
GetNode(string) (types.Node, error)
UpdateNode(string, uint64, types.NodeSpec) error
RemoveNode(string) error
RemoveNode(string, bool) error
GetTasks(basictypes.TaskListOptions) ([]types.Task, error)
GetTask(string) (types.Task, error)
}

View file

@ -219,7 +219,13 @@ func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r
}
func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := sr.backend.RemoveNode(vars["id"]); err != nil {
if err := httputils.ParseForm(r); err != nil {
return err
}
force := httputils.BoolValue(r, "force")
if err := sr.backend.RemoveNode(vars["id"], force); err != nil {
logrus.Errorf("Error removing node %s: %v", vars["id"], err)
return err
}

View file

@ -1023,7 +1023,7 @@ func (c *Cluster) UpdateNode(nodeID string, version uint64, spec types.NodeSpec)
}
// RemoveNode removes a node from a cluster
func (c *Cluster) RemoveNode(input string) error {
func (c *Cluster) RemoveNode(input string, force bool) error {
c.RLock()
defer c.RUnlock()
@ -1039,7 +1039,7 @@ func (c *Cluster) RemoveNode(input string) error {
return err
}
if _, err := c.client.RemoveNode(ctx, &swarmapi.RemoveNodeRequest{NodeID: node.ID}); err != nil {
if _, err := c.client.RemoveNode(ctx, &swarmapi.RemoveNodeRequest{NodeID: node.ID, Force: force}); err != nil {
return err
}
return nil

View file

@ -13,7 +13,7 @@ parent = "smn_cli"
# node rm
```markdown
Usage: docker node rm NODE [NODE...]
Usage: docker node rm [OPTIONS] NODE [NODE...]
Remove one or more nodes from the swarm
@ -21,6 +21,7 @@ Aliases:
rm, remove
Options:
--force Force remove an active node
--help Print usage
```
@ -32,6 +33,24 @@ Example output:
$ docker node rm swarm-node-02
Node swarm-node-02 removed from swarm
Removes nodes from the swarm that are in the down state. Attempting to remove
an active node will result in an error:
```bash
$ docker node rm swarm-node-03
Error response from daemon: rpc error: code = 9 desc = node swarm-node-03 is not down and can't be removed
```
If a worker node becomes compromised, exhibits unexpected or unwanted behavior, or if you lose access to it so
that a clean shutdown is impossible, you can use the force option.
```bash
$ docker node rm --force swarm-node-03
Node swarm-node-03 removed from swarm
```
Note that manager nodes have to be demoted to worker nodes before they can be removed
from the cluster.
## Related information

View file

@ -208,6 +208,17 @@ func (d *SwarmDaemon) getNode(c *check.C, id string) *swarm.Node {
return &node
}
func (d *SwarmDaemon) removeNode(c *check.C, id string, force bool) {
url := "/nodes/" + id
if force {
url += "?force=1"
}
status, out, err := d.SockRequest("DELETE", url, nil)
c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
c.Assert(err, checker.IsNil)
}
func (d *SwarmDaemon) updateNode(c *check.C, id string, f ...nodeConstructor) {
for i := 0; ; i++ {
node := d.getNode(c, id)

View file

@ -510,6 +510,37 @@ func (s *DockerSwarmSuite) TestApiSwarmNodeUpdate(c *check.C) {
c.Assert(n.Spec.Availability, checker.Equals, swarm.NodeAvailabilityPause)
}
func (s *DockerSwarmSuite) TestApiSwarmNodeRemove(c *check.C) {
testRequires(c, Network)
d1 := s.AddDaemon(c, true, true)
d2 := s.AddDaemon(c, true, false)
_ = s.AddDaemon(c, true, false)
nodes := d1.listNodes(c)
c.Assert(len(nodes), checker.Equals, 3, check.Commentf("nodes: %#v", nodes))
// Getting the info so we can take the NodeID
d2Info, err := d2.info()
c.Assert(err, checker.IsNil)
// forceful removal of d2 should work
d1.removeNode(c, d2Info.NodeID, true)
nodes = d1.listNodes(c)
c.Assert(len(nodes), checker.Equals, 2, check.Commentf("nodes: %#v", nodes))
// Restart the node that was removed
err = d2.Restart()
c.Assert(err, checker.IsNil)
// Give some time for the node to rejoin
time.Sleep(1 * time.Second)
// Make sure the node didn't rejoin
nodes = d1.listNodes(c)
c.Assert(len(nodes), checker.Equals, 2, check.Commentf("nodes: %#v", nodes))
}
func (s *DockerSwarmSuite) TestApiSwarmNodeDrainPause(c *check.C) {
testRequires(c, Network)
d1 := s.AddDaemon(c, true, true)