mirror of https://github.com/hashicorp/consul
init
parent
091925bcb7
commit
2f94024b35
|
@ -122,3 +122,20 @@ func (op *Operator) RaftRemovePeerByID(id string, q *WriteOptions) error {
|
||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -57,3 +57,22 @@ func TestAPI_OperatorRaftLeaderTransfer(t *testing.T) {
|
||||||
t.Fatalf("err:%v", transfer)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,10 +24,16 @@ type cmd struct {
|
||||||
flags *flag.FlagSet
|
flags *flag.FlagSet
|
||||||
http *flags.HTTPFlags
|
http *flags.HTTPFlags
|
||||||
help string
|
help string
|
||||||
|
|
||||||
|
// flags
|
||||||
|
detailed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
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{}
|
c.http = &flags.HTTPFlags{}
|
||||||
flags.Merge(c.flags, c.http.ClientFlags())
|
flags.Merge(c.flags, c.http.ClientFlags())
|
||||||
flags.Merge(c.flags, c.http.ServerFlags())
|
flags.Merge(c.flags, c.http.ServerFlags())
|
||||||
|
@ -51,13 +57,22 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the current configuration.
|
// Fetch the current configuration.
|
||||||
|
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())
|
result, err := raftListPeers(client, c.http.Stale())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error getting peers: %v", err))
|
c.UI.Error(fmt.Sprintf("Error getting peers: %v", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
c.UI.Output(result)
|
c.UI.Output(result)
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
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
|
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 {
|
func (c *cmd) Synopsis() string {
|
||||||
return synopsis
|
return synopsis
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
func TestOperatorRaftListPeersCommand_verticalBar(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("too slow for testing.Short")
|
t.Skip("too slow for testing.Short")
|
||||||
|
|
Loading…
Reference in New Issue