From 139064008d085b63d93a29b9404fa98113d98077 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Thu, 17 Mar 2016 16:04:09 -0700 Subject: [PATCH] display server leaders per region --- api/status.go | 11 +++++++++ command/server_members.go | 52 ++++++++++++++++++++++++++++++++++++--- nomad/server.go | 2 +- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/api/status.go b/api/status.go index 4972d5b2bf11..da1cb4c02eea 100644 --- a/api/status.go +++ b/api/status.go @@ -20,6 +20,17 @@ func (s *Status) Leader() (string, error) { return resp, nil } +// RegionLeader is used to query for the leader in the passed region. +func (s *Status) RegionLeader(region string) (string, error) { + var resp string + q := QueryOptions{Region: region} + _, err := s.client.query("/v1/status/leader", &resp, &q) + if err != nil { + return "", err + } + return resp, nil +} + // Peers is used to query the addresses of the server peers // in the cluster. func (s *Status) Peers() ([]string, error) { diff --git a/command/server_members.go b/command/server_members.go index 5eaa1f1a94d7..f0181c796253 100644 --- a/command/server_members.go +++ b/command/server_members.go @@ -72,12 +72,19 @@ func (c *ServerMembersCommand) Run(args []string) int { // Sort the members sort.Sort(api.AgentMembersNameSort(mem)) + // Determine the leaders per region. + leaders, err := regionLeaders(client, mem) + if err != nil && !strings.Contains(err.Error(), "No cluster leader") { + c.Ui.Error(fmt.Sprintf("Error determining leaders: %s", err)) + return 1 + } + // Format the list var out []string if detailed { out = detailedOutput(mem) } else { - out = standardOutput(mem) + out = standardOutput(mem, leaders) } // Dump the list @@ -85,16 +92,27 @@ func (c *ServerMembersCommand) Run(args []string) int { return 0 } -func standardOutput(mem []*api.AgentMember) []string { +func standardOutput(mem []*api.AgentMember, leaders map[string]string) []string { // Format the members list members := make([]string, len(mem)+1) - members[0] = "Name|Address|Port|Status|Protocol|Build|Datacenter|Region" + members[0] = "Name|Address|Port|Status|Leader|Protocol|Build|Datacenter|Region" for i, member := range mem { - members[i+1] = fmt.Sprintf("%s|%s|%d|%s|%d|%s|%s|%s", + reg := member.Tags["region"] + regLeader, ok := leaders[reg] + isLeader := false + if ok { + if regLeader == fmt.Sprintf("%s:%s", member.Addr, member.Tags["port"]) { + + isLeader = true + } + } + + members[i+1] = fmt.Sprintf("%s|%s|%d|%s|%t|%d|%s|%s|%s", member.Name, member.Addr, member.Port, member.Status, + isLeader, member.ProtocolCur, member.Tags["build"], member.Tags["dc"], @@ -123,3 +141,29 @@ func detailedOutput(mem []*api.AgentMember) []string { } return members } + +// regionLeaders returns a map of regions to the IP of the member that is the +// leader. +func regionLeaders(client *api.Client, mem []*api.AgentMember) (map[string]string, error) { + // Determine the unique regions. + leaders := make(map[string]string) + regions := make(map[string]struct{}) + for _, m := range mem { + regions[m.Tags["region"]] = struct{}{} + } + + if len(regions) == 0 { + return leaders, nil + } + + status := client.Status() + for reg := range regions { + l, err := status.RegionLeader(reg) + if err != nil { + return leaders, err + } + leaders[reg] = l + } + + return leaders, nil +} diff --git a/nomad/server.go b/nomad/server.go index f0a66e68835d..8b9712cd0dd1 100644 --- a/nomad/server.go +++ b/nomad/server.go @@ -591,7 +591,7 @@ func (s *Server) setupWorkers() error { } // numOtherPeers is used to check on the number of known peers -// excluding the local ndoe +// excluding the local node func (s *Server) numOtherPeers() (int, error) { peers, err := s.raftPeers.Peers() if err != nil {