From aed5cb76690aee5a77a15a1cf3992c517ab5fd17 Mon Sep 17 00:00:00 2001 From: Sarah Adams Date: Mon, 2 Dec 2019 11:06:15 -0800 Subject: [PATCH] give feedback to CLI user on forceleave command if node does not exist (#6841) --- agent/agent.go | 15 +++++++++++++++ agent/agent_endpoint_test.go | 8 +++++--- api/agent_test.go | 4 ++-- command/forceleave/forceleave_test.go | 18 ++++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index d7d4c71cd0dc..7922900f54c7 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1782,6 +1782,9 @@ func (a *Agent) JoinWAN(addrs []string) (n int, err error) { // ForceLeave is used to remove a failed node from the cluster func (a *Agent) ForceLeave(node string, prune bool) (err error) { a.logger.Printf("[INFO] agent: Force leaving node: %v", node) + if ok := a.IsMember(node); !ok { + return fmt.Errorf("agent: No node found with name '%s'", node) + } err = a.delegate.RemoveFailedNode(node, prune) if err != nil { a.logger.Printf("[WARN] agent: Failed to remove node: %v", err) @@ -1807,6 +1810,18 @@ func (a *Agent) WANMembers() []serf.Member { return nil } +// IsMember is used to check if a node with the given nodeName +// is a member +func (a *Agent) IsMember(nodeName string) bool { + for _, m := range a.LANMembers() { + if m.Name == nodeName { + return true + } + } + + return false +} + // StartSync is called once Services and Checks are registered. // This is called to prevent a race between clients and the anti-entropy routines func (a *Agent) StartSync() { diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index f0e34f64d620..6437b7c15ff0 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -1632,15 +1632,17 @@ func TestAgent_ForceLeave_ACLDeny(t *testing.T) { defer a.Shutdown() testrpc.WaitForLeader(t, a.RPC, "dc1") + uri := fmt.Sprintf("/v1/agent/force-leave/%s", a.Config.NodeName) + t.Run("no token", func(t *testing.T) { - req, _ := http.NewRequest("PUT", "/v1/agent/force-leave/nope", nil) + req, _ := http.NewRequest("PUT", uri, nil) if _, err := a.srv.AgentForceLeave(nil, req); !acl.IsErrPermissionDenied(err) { t.Fatalf("err: %v", err) } }) t.Run("agent master token", func(t *testing.T) { - req, _ := http.NewRequest("PUT", "/v1/agent/force-leave/nope?token=towel", nil) + req, _ := http.NewRequest("PUT", uri+"?token=towel", nil) if _, err := a.srv.AgentForceLeave(nil, req); err != nil { t.Fatalf("err: %v", err) } @@ -1648,7 +1650,7 @@ func TestAgent_ForceLeave_ACLDeny(t *testing.T) { t.Run("read-only token", func(t *testing.T) { ro := makeReadOnlyAgentACL(t, a.srv) - req, _ := http.NewRequest("PUT", fmt.Sprintf("/v1/agent/force-leave/nope?token=%s", ro), nil) + req, _ := http.NewRequest("PUT", fmt.Sprintf(uri+"?token=%s", ro), nil) if _, err := a.srv.AgentForceLeave(nil, req); !acl.IsErrPermissionDenied(err) { t.Fatalf("err: %v", err) } diff --git a/api/agent_test.go b/api/agent_test.go index 2d2bb30a0c97..ccec5c14274a 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -1073,7 +1073,7 @@ func TestAPI_AgentForceLeave(t *testing.T) { agent := c.Agent() // Eject somebody - err := agent.ForceLeave("foo") + err := agent.ForceLeave(s.Config.NodeName) if err != nil { t.Fatalf("err: %v", err) } @@ -1087,7 +1087,7 @@ func TestAPI_AgentForceLeavePrune(t *testing.T) { agent := c.Agent() // Eject somebody - err := agent.ForceLeavePrune("foo") + err := agent.ForceLeavePrune(s.Config.NodeName) if err != nil { t.Fatalf("err: %v", err) } diff --git a/command/forceleave/forceleave_test.go b/command/forceleave/forceleave_test.go index eb923fa517a8..ce06bcfcd7de 100644 --- a/command/forceleave/forceleave_test.go +++ b/command/forceleave/forceleave_test.go @@ -56,6 +56,24 @@ func TestForceLeaveCommand(t *testing.T) { }) } +func TestForceLeaveCommand_NoNodeWithName(t *testing.T) { + t.Parallel() + a1 := agent.NewTestAgent(t, t.Name(), ``) + defer a1.Shutdown() + + ui := cli.NewMockUi() + c := New(ui) + args := []string{ + "-http-addr=" + a1.HTTPAddr(), + "garbage-name", + } + + code := c.Run(args) + if code != 1 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } +} + func TestForceLeaveCommand_prune(t *testing.T) { t.Parallel() a1 := agent.NewTestAgent(t, t.Name()+"-a1", ``)