diff --git a/CHANGELOG.md b/CHANGELOG.md index 525f714f262..2ad7c741261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ IMPROVEMENTS: * driver/lxc: Support for LXC containers [GH-1699] * driver/rkt: Support network configurations [GH-1862] * driver/rkt: Support rkt volumes (rkt >= 1.0.0 required) [GH-1812] + * server/rpc: Added an RPC endpoint for retreiving server members [GH-1947] BUG FIXES: * core: Fix case where dead nodes were not properly handled by System diff --git a/api/agent.go b/api/agent.go index 054d0c343a5..81c6e0e06d2 100644 --- a/api/agent.go +++ b/api/agent.go @@ -131,8 +131,8 @@ func (a *Agent) Join(addrs ...string) (int, error) { } // Members is used to query all of the known server members -func (a *Agent) Members() ([]*AgentMember, error) { - var resp []*AgentMember +func (a *Agent) Members() (*ServerMembers, error) { + var resp *ServerMembers // Query the known members _, err := a.client.query("/v1/agent/members", &resp, nil) @@ -217,6 +217,13 @@ type joinResponse struct { Error string `json:"error"` } +type ServerMembers struct { + ServerName string + Region string + DC string + Members []*AgentMember +} + // AgentMember represents a cluster member known to the agent type AgentMember struct { Name string diff --git a/command/agent/agent_endpoint.go b/command/agent/agent_endpoint.go index e22afcd4f36..2ca5c43d967 100644 --- a/command/agent/agent_endpoint.go +++ b/command/agent/agent_endpoint.go @@ -88,17 +88,13 @@ func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Req if req.Method != "GET" { return nil, CodedError(405, ErrInvalidMethod) } - srv := s.agent.Server() - if srv == nil { - return nil, CodedError(501, ErrInvalidMethod) + args := &structs.GenericRequest{} + var out structs.ServerMembersResponse + if err := s.agent.RPC("Status.Members", args, &out); err != nil { + return nil, err } - serfMembers := srv.Members() - members := make([]Member, len(serfMembers)) - for i, mem := range serfMembers { - members[i] = nomadMember(mem) - } - return members, nil + return out, nil } func (s *HTTPServer) AgentForceLeaveRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { diff --git a/command/server_members.go b/command/server_members.go index e8340920d77..378be3a6e00 100644 --- a/command/server_members.go +++ b/command/server_members.go @@ -64,17 +64,22 @@ func (c *ServerMembersCommand) Run(args []string) int { } // Query the members - mem, err := client.Agent().Members() + srvMembers, err := client.Agent().Members() if err != nil { c.Ui.Error(fmt.Sprintf("Error querying servers: %s", err)) return 1 } + if srvMembers == nil { + c.Ui.Error("Agent doesn't know about server members") + return 0 + } + // Sort the members - sort.Sort(api.AgentMembersNameSort(mem)) + sort.Sort(api.AgentMembersNameSort(srvMembers.Members)) // Determine the leaders per region. - leaders, err := regionLeaders(client, mem) + leaders, err := regionLeaders(client, srvMembers.Members) if err != nil { c.Ui.Error(fmt.Sprintf("Error determining leaders: %s", err)) return 1 @@ -83,9 +88,9 @@ func (c *ServerMembersCommand) Run(args []string) int { // Format the list var out []string if detailed { - out = detailedOutput(mem) + out = detailedOutput(srvMembers.Members) } else { - out = standardOutput(mem, leaders) + out = standardOutput(srvMembers.Members, leaders) } // Dump the list diff --git a/nomad/status_endpoint.go b/nomad/status_endpoint.go index aaddb220137..cdd871841c3 100644 --- a/nomad/status_endpoint.go +++ b/nomad/status_endpoint.go @@ -61,3 +61,32 @@ func (s *Status) Peers(args *structs.GenericRequest, reply *[]string) error { *reply = peers return nil } + +// Members return the list of servers in a cluster that a particular server is +// aware of +func (s *Status) Members(args *structs.GenericRequest, reply *structs.ServerMembersResponse) error { + serfMembers := s.srv.Members() + members := make([]*structs.ServerMember, len(serfMembers)) + for i, mem := range serfMembers { + members[i] = &structs.ServerMember{ + Name: mem.Name, + Addr: mem.Addr, + Port: mem.Port, + Tags: mem.Tags, + Status: mem.Status.String(), + ProtocolMin: mem.ProtocolMin, + ProtocolMax: mem.ProtocolMax, + ProtocolCur: mem.ProtocolCur, + DelegateMin: mem.DelegateMin, + DelegateMax: mem.DelegateMax, + DelegateCur: mem.DelegateCur, + } + } + *reply = structs.ServerMembersResponse{ + ServerName: s.srv.config.NodeName, + ServerRegion: s.srv.config.Region, + ServerDC: s.srv.config.Datacenter, + Members: members, + } + return nil +} diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index c39dbd1a771..a2da811b8c5 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io" + "net" "os" "path/filepath" "reflect" @@ -359,6 +360,30 @@ type PeriodicForceRequest struct { WriteRequest } +// ServerMembersResponse has the list of servers in a cluster +type ServerMembersResponse struct { + ServerName string + ServerRegion string + ServerDC string + Members []*ServerMember + QueryMeta +} + +// ServerMember holds information about a Nomad server agent in a cluster +type ServerMember struct { + Name string + Addr net.IP + Port uint16 + Tags map[string]string + Status string + ProtocolMin uint8 + ProtocolMax uint8 + ProtocolCur uint8 + DelegateMin uint8 + DelegateMax uint8 + DelegateCur uint8 +} + // DeriveVaultTokenRequest is used to request wrapped Vault tokens for the // following tasks in the given allocation type DeriveVaultTokenRequest struct {