From 2f94024b356f774d0032ad069babb1a3fce6cfbd Mon Sep 17 00:00:00 2001 From: Ashesh Vidyut Date: Tue, 6 Jun 2023 15:21:24 +0530 Subject: [PATCH] init --- api/operator_raft.go | 17 +++++ api/operator_raft_test.go | 19 +++++ .../raft/listpeers/operator_raft_list.go | 71 +++++++++++++++++-- .../raft/listpeers/operator_raft_list_test.go | 27 +++++++ 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/api/operator_raft.go b/api/operator_raft.go index 393d6fb3c5f7..6ee75c4d8bb0 100644 --- a/api/operator_raft.go +++ b/api/operator_raft.go @@ -122,3 +122,20 @@ func (op *Operator) RaftRemovePeerByID(id string, q *WriteOptions) error { } return nil } + +// GetAutoPilotHealth is used to query the autopilot health. +func (op *Operator) GetAutoPilotHealth(q *QueryOptions) (*OperatorHealthReply, error) { + r := op.c.newRequest("GET", "/v1/operator/autopilot/health") + r.setQueryOptions(q) + _, resp, err := op.c.doRequest(r) + if err != nil { + return nil, err + } + defer closeResponseBody(resp) + + var out OperatorHealthReply + if err := decodeBody(resp, &out); err != nil { + return nil, err + } + return &out, nil +} diff --git a/api/operator_raft_test.go b/api/operator_raft_test.go index 6e3b7fc0e310..57b50b745269 100644 --- a/api/operator_raft_test.go +++ b/api/operator_raft_test.go @@ -57,3 +57,22 @@ func TestAPI_OperatorRaftLeaderTransfer(t *testing.T) { t.Fatalf("err:%v", transfer) } } + +func TestAPI_GetAutoPilotHealth(t *testing.T) { + t.Parallel() + c, s := makeClient(t) + defer s.Stop() + + operator := c.Operator() + out, err := operator.GetAutoPilotHealth(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + if len(out.Servers) != 1 || + !out.Servers[0].Leader || + !out.Servers[0].Voter || + out.Servers[0].LastIndex <= 0 { + t.Fatalf("bad: %v", out) + } +} diff --git a/command/operator/raft/listpeers/operator_raft_list.go b/command/operator/raft/listpeers/operator_raft_list.go index 47bd161fed48..9cc4db67250a 100644 --- a/command/operator/raft/listpeers/operator_raft_list.go +++ b/command/operator/raft/listpeers/operator_raft_list.go @@ -24,10 +24,16 @@ type cmd struct { flags *flag.FlagSet http *flags.HTTPFlags help string + + // flags + detailed bool } func (c *cmd) init() { c.flags = flag.NewFlagSet("", flag.ContinueOnError) + c.flags.BoolVar(&c.detailed, "detailed", false, + "Outputs additional information 'commit_index' which is "+ + "the index of the server's last committed Raft log entry.") c.http = &flags.HTTPFlags{} flags.Merge(c.flags, c.http.ClientFlags()) flags.Merge(c.flags, c.http.ServerFlags()) @@ -51,13 +57,22 @@ func (c *cmd) Run(args []string) int { } // Fetch the current configuration. - result, err := raftListPeers(client, c.http.Stale()) - if err != nil { - c.UI.Error(fmt.Sprintf("Error getting peers: %v", err)) - return 1 + if c.detailed { + result, err := raftListPeersDetailed(client, c.http.Stale()) + if err != nil { + c.UI.Error(fmt.Sprintf("Error getting peers: %v", err)) + return 1 + } + c.UI.Output(result) + } else { + result, err := raftListPeers(client, c.http.Stale()) + if err != nil { + c.UI.Error(fmt.Sprintf("Error getting peers: %v", err)) + return 1 + } + c.UI.Output(result) } - c.UI.Output(result) return 0 } @@ -89,6 +104,52 @@ func raftListPeers(client *api.Client, stale bool) (string, error) { return columnize.Format(result, &columnize.Config{Delim: string([]byte{0x1f})}), nil } +func raftListPeersDetailed(client *api.Client, stale bool) (string, error) { + q := &api.QueryOptions{ + AllowStale: stale, + } + reply, err := client.Operator().RaftGetConfiguration(q) + if err != nil { + return "", fmt.Errorf("Failed to retrieve raft configuration: %v", err) + } + + autoPilotReply, err := client.Operator().GetAutoPilotHealth(q) + if err != nil { + return "", fmt.Errorf("Failed to retrieve autopilot health: %v", err) + } + + serverHealthDataMap := make(map[string]api.ServerHealth) + + for _, serverHealthData := range autoPilotReply.Servers { + serverHealthDataMap[serverHealthData.ID] = serverHealthData + } + + // Format it as a nice table. + result := []string{"Node\x1fID\x1fAddress\x1fState\x1fVoter\x1fRaftProtocol\x1fCommitIndex"} + for _, s := range reply.Servers { + raftProtocol := s.ProtocolVersion + + if raftProtocol == "" { + raftProtocol = "<=1" + } + state := "follower" + if s.Leader { + state = "leader" + } + + serverHealthData, ok := serverHealthDataMap[s.ID] + if ok { + result = append(result, fmt.Sprintf("%s\x1f%s\x1f%s\x1f%s\x1f%v\x1f%s\x1f%v", + s.Node, s.ID, s.Address, state, s.Voter, raftProtocol, serverHealthData.LastIndex)) + } else { + result = append(result, fmt.Sprintf("%s\x1f%s\x1f%s\x1f%s\x1f%v\x1f%s\x1f%v", + s.Node, s.ID, s.Address, state, s.Voter, raftProtocol, "")) + } + } + + return columnize.Format(result, &columnize.Config{Delim: string([]byte{0x1f})}), nil +} + func (c *cmd) Synopsis() string { return synopsis } diff --git a/command/operator/raft/listpeers/operator_raft_list_test.go b/command/operator/raft/listpeers/operator_raft_list_test.go index 15bd1bfbe34f..a610c099955f 100644 --- a/command/operator/raft/listpeers/operator_raft_list_test.go +++ b/command/operator/raft/listpeers/operator_raft_list_test.go @@ -46,6 +46,33 @@ func TestOperatorRaftListPeersCommand(t *testing.T) { } } +func TestOperatorRaftListPeersCommandDetailed(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + a := agent.NewTestAgent(t, ``) + defer a.Shutdown() + + expected := fmt.Sprintf("%s %s 127.0.0.1:%d leader true 3", + a.Config.NodeName, a.Config.NodeID, a.Config.ServerPort) + + // Test the list-peers subcommand directly + ui := cli.NewMockUi() + c := New(ui) + args := []string{"-http-addr=" + a.HTTPAddr(), "-detailed"} + + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + output := strings.TrimSpace(ui.OutputWriter.String()) + if !strings.Contains(output, expected) { + t.Fatalf("bad: %q, %q", output, expected) + } +} + func TestOperatorRaftListPeersCommand_verticalBar(t *testing.T) { if testing.Short() { t.Skip("too slow for testing.Short")