From f25cac4c8f19e226b5f278ac2e86bec7a6d18904 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Wed, 19 Apr 2017 15:01:40 -0700 Subject: [PATCH] Show raft protocol in list-peers command (#2929) --- command/operator_raft.go | 5 ++- command/operator_raft_list.go | 35 +++++++++++++++---- command/operator_raft_list_test.go | 12 ++++--- .../commands/operator/raft.html.markdown.erb | 8 ++--- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/command/operator_raft.go b/command/operator_raft.go index d40e4cc7265a..b40e8c5c780b 100644 --- a/command/operator_raft.go +++ b/command/operator_raft.go @@ -77,17 +77,16 @@ func (c *OperatorRaftCommand) raft(args []string) error { if err != nil { return fmt.Errorf("error connecting to Consul agent: %s", err) } - operator := client.Operator() // Dispatch based on the verb argument. if listPeers { - result, err := raftListPeers(operator, c.Command.HTTPStale()) + result, err := raftListPeers(client, c.Command.HTTPStale()) if err != nil { c.Ui.Error(fmt.Sprintf("Error getting peers: %v", err)) } c.Ui.Output(result) } else if removePeer { - if err := raftRemovePeers(address, "", operator); err != nil { + if err := raftRemovePeers(address, "", client.Operator()); err != nil { return fmt.Errorf("Error removing peer: %v", err) } c.Ui.Output(fmt.Sprintf("Removed peer with address %q", address)) diff --git a/command/operator_raft_list.go b/command/operator_raft_list.go index c5285a56c64a..f309dad7bfed 100644 --- a/command/operator_raft_list.go +++ b/command/operator_raft_list.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/command/base" + "github.com/hashicorp/serf/serf" "github.com/ryanuber/columnize" ) @@ -48,7 +49,7 @@ func (c *OperatorRaftListCommand) Run(args []string) int { } // Fetch the current configuration. - result, err := raftListPeers(client.Operator(), c.Command.HTTPStale()) + result, err := raftListPeers(client, c.Command.HTTPStale()) if err != nil { c.Ui.Error(fmt.Sprintf("Error getting peers: %v", err)) } @@ -57,24 +58,46 @@ func (c *OperatorRaftListCommand) Run(args []string) int { return 0 } -func raftListPeers(operator *api.Operator, stale bool) (string, error) { +func raftListPeers(client *api.Client, stale bool) (string, error) { + raftProtocols := make(map[string]string) + members, err := client.Agent().Members(false) + if err != nil { + return "", err + } + + for _, member := range members { + if serf.MemberStatus(member.Status) == serf.StatusLeft { + continue + } + + if member.Tags["role"] != "consul" { + continue + } + + raftProtocols[member.Name] = member.Tags["raft_vsn"] + } + q := &api.QueryOptions{ AllowStale: stale, } - reply, err := operator.RaftGetConfiguration(q) + reply, err := client.Operator().RaftGetConfiguration(q) if err != nil { return "", fmt.Errorf("Failed to retrieve raft configuration: %v", err) } // Format it as a nice table. - result := []string{"Node|ID|Address|State|Voter"} + result := []string{"Node|ID|Address|State|Voter|RaftProtocol"} for _, s := range reply.Servers { + raftProtocol := raftProtocols[s.Node] + if raftProtocol == "" { + raftProtocol = "<=1" + } state := "follower" if s.Leader { state = "leader" } - result = append(result, fmt.Sprintf("%s|%s|%s|%s|%v", - s.Node, s.ID, s.Address, state, s.Voter)) + result = append(result, fmt.Sprintf("%s|%s|%s|%s|%v|%s", + s.Node, s.ID, s.Address, state, s.Voter, raftProtocol)) } return columnize.SimpleFormat(result), nil diff --git a/command/operator_raft_list_test.go b/command/operator_raft_list_test.go index 45de8b77ee84..666eecf2ffab 100644 --- a/command/operator_raft_list_test.go +++ b/command/operator_raft_list_test.go @@ -1,6 +1,7 @@ package command import ( + "fmt" "strings" "testing" @@ -17,6 +18,9 @@ func TestOperator_Raft_ListPeers(t *testing.T) { defer a1.Shutdown() waitForLeader(t, a1.httpAddr) + expected := fmt.Sprintf("%s 127.0.0.1:%d 127.0.0.1:%d leader true 2", + a1.config.NodeName, a1.config.Ports.Server, a1.config.Ports.Server) + // Test the legacy mode with 'consul operator raft -list-peers' { ui, c := testOperatorRaftCommand(t) @@ -27,8 +31,8 @@ func TestOperator_Raft_ListPeers(t *testing.T) { t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) } output := strings.TrimSpace(ui.OutputWriter.String()) - if !strings.Contains(output, "leader") { - t.Fatalf("bad: %s", output) + if !strings.Contains(output, expected) { + t.Fatalf("bad: %q, %q", output, expected) } } @@ -48,8 +52,8 @@ func TestOperator_Raft_ListPeers(t *testing.T) { t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) } output := strings.TrimSpace(ui.OutputWriter.String()) - if !strings.Contains(output, "leader") { - t.Fatalf("bad: %s", output) + if !strings.Contains(output, expected) { + t.Fatalf("bad: %q, %q", output, expected) } } } diff --git a/website/source/docs/commands/operator/raft.html.markdown.erb b/website/source/docs/commands/operator/raft.html.markdown.erb index c08465be5e15..8ee1621f180c 100644 --- a/website/source/docs/commands/operator/raft.html.markdown.erb +++ b/website/source/docs/commands/operator/raft.html.markdown.erb @@ -40,10 +40,10 @@ to set this to "true" to get the configuration from a non-leader server. The output looks like this: ``` -Node ID Address State Voter -alice 127.0.0.1:8300 127.0.0.1:8300 follower true -bob 127.0.0.2:8300 127.0.0.2:8300 leader true -carol 127.0.0.3:8300 127.0.0.3:8300 follower true +Node ID Address State Voter RaftProtocol +alice 127.0.0.1:8300 127.0.0.1:8300 follower true 2 +bob 127.0.0.2:8300 127.0.0.2:8300 leader true 3 +carol 127.0.0.3:8300 127.0.0.3:8300 follower true 2 ``` `Node` is the node name of the server, as known to Consul, or "(unknown)" if