Makes the Raft configuration API easier to consume.
This commit is contained in:
parent
5df4b6bef2
commit
6be1e07fec
|
@ -14,23 +14,37 @@ func (c *Client) Operator() *Operator {
|
|||
return &Operator{c}
|
||||
}
|
||||
|
||||
// RaftServer has information about a server in the Raft configuration.
|
||||
type RaftServer struct {
|
||||
// ID is the unique ID for the server. These are currently the same
|
||||
// as the address, but they will be changed to a real GUID in a future
|
||||
// release of Consul.
|
||||
ID raft.ServerID
|
||||
|
||||
// Node is the node name of the server, as known by Consul, or this
|
||||
// will be set to "(unknown)" otherwise.
|
||||
Node string
|
||||
|
||||
// Address is the IP:port of the server, used for Raft communications.
|
||||
Address raft.ServerAddress
|
||||
|
||||
// Leader is true if this server is the current cluster leader.
|
||||
Leader bool
|
||||
|
||||
// Voter is true if this server has a vote in the cluster. This might
|
||||
// be false if the server is staging and still coming online, or if
|
||||
// it's a non-voting server, which will be added in a future release of
|
||||
// Consul.
|
||||
Voter bool
|
||||
}
|
||||
|
||||
// RaftConfigration is returned when querying for the current Raft configuration.
|
||||
// This has the low-level Raft structure, as well as some supplemental
|
||||
// information from Consul.
|
||||
type RaftConfiguration struct {
|
||||
// Configuration is the low-level Raft configuration structure.
|
||||
Configuration raft.Configuration
|
||||
// Servers has the list of servers in the Raft configuration.
|
||||
Servers []*RaftServer
|
||||
|
||||
// NodeMap maps IDs in the Raft configuration to node names known by
|
||||
// Consul. It's possible that not all configuration entries may have
|
||||
// an entry here if the node isn't known to Consul. Given how this is
|
||||
// generated, this may also contain entries that aren't present in the
|
||||
// Raft configuration.
|
||||
NodeMap map[raft.ServerID]string
|
||||
|
||||
// Leader is the ID of the current Raft leader. This may be blank if
|
||||
// there isn't one.
|
||||
Leader raft.ServerID
|
||||
// Index has the Raft index of this configuration.
|
||||
Index uint64
|
||||
}
|
||||
|
||||
// RaftGetConfiguration is used to query the current Raft peer set.
|
||||
|
|
|
@ -15,9 +15,9 @@ func TestOperator_RaftGetConfiguration(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if len(out.Configuration.Servers) != 1 ||
|
||||
len(out.NodeMap) != 1 ||
|
||||
len(out.Leader) == 0 {
|
||||
if len(out.Servers) != 1 ||
|
||||
!out.Servers[0].Leader ||
|
||||
!out.Servers[0].Voter {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ func TestOperator_OperatorRaftConfiguration(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatalf("unexpected: %T", obj)
|
||||
}
|
||||
if len(out.Configuration.Servers) != 1 ||
|
||||
len(out.NodeMap) != 1 ||
|
||||
len(out.Leader) == 0 {
|
||||
if len(out.Servers) != 1 ||
|
||||
!out.Servers[0].Leader ||
|
||||
!out.Servers[0].Voter {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -140,18 +140,13 @@ func (c *OperatorCommand) raft(args []string) error {
|
|||
|
||||
// Format it as a nice table.
|
||||
result := []string{"Node|ID|Address|State|Voter"}
|
||||
for _, s := range reply.Configuration.Servers {
|
||||
node := "(unknown)"
|
||||
if mappedNode, ok := reply.NodeMap[s.ID]; ok {
|
||||
node = mappedNode
|
||||
}
|
||||
for _, s := range reply.Servers {
|
||||
state := "follower"
|
||||
if s.ID == reply.Leader {
|
||||
if s.Leader {
|
||||
state = "leader"
|
||||
}
|
||||
voter := s.Suffrage == raft.Voter
|
||||
result = append(result, fmt.Sprintf("%s|%s|%s|%s|%v",
|
||||
node, s.ID, s.Address, state, voter))
|
||||
s.Node, s.ID, s.Address, state, s.Voter))
|
||||
}
|
||||
c.Ui.Output(columnize.SimpleFormat(result))
|
||||
} else if removePeer {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/hashicorp/consul/consul/agent"
|
||||
"github.com/hashicorp/consul/consul/structs"
|
||||
"github.com/hashicorp/raft"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
// Operator endpoint is used to perform low-level operator tasks for Consul.
|
||||
|
@ -35,33 +36,36 @@ func (op *Operator) RaftGetConfiguration(args *structs.DCSpecificRequest, reply
|
|||
if err := future.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
reply.Configuration = future.Configuration()
|
||||
leader := op.srv.raft.Leader()
|
||||
|
||||
// Index the configuration so we can easily look up IDs by address.
|
||||
idMap := make(map[raft.ServerAddress]raft.ServerID)
|
||||
for _, s := range reply.Configuration.Servers {
|
||||
idMap[s.Address] = s.ID
|
||||
}
|
||||
|
||||
// Fill out the node map and leader.
|
||||
reply.NodeMap = make(map[raft.ServerID]string)
|
||||
members := op.srv.serfLAN.Members()
|
||||
for _, member := range members {
|
||||
// Index the Consul information about the servers.
|
||||
serverMap := make(map[raft.ServerAddress]*serf.Member)
|
||||
for _, member := range op.srv.serfLAN.Members() {
|
||||
valid, parts := agent.IsConsulServer(member)
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO (slackpad) We need to add a Raft API to get the leader by
|
||||
// ID so we don't have to do this mapping.
|
||||
addr := (&net.TCPAddr{IP: member.Addr, Port: parts.Port}).String()
|
||||
if id, ok := idMap[raft.ServerAddress(addr)]; ok {
|
||||
reply.NodeMap[id] = member.Name
|
||||
if leader == raft.ServerAddress(addr) {
|
||||
reply.Leader = id
|
||||
}
|
||||
serverMap[raft.ServerAddress(addr)] = &member
|
||||
}
|
||||
|
||||
// Fill out the reply.
|
||||
leader := op.srv.raft.Leader()
|
||||
reply.Index = future.Index()
|
||||
for _, server := range future.Configuration().Servers {
|
||||
node := "(unknown)"
|
||||
if member, ok := serverMap[server.Address]; ok {
|
||||
node = member.Name
|
||||
}
|
||||
|
||||
entry := &structs.RaftServer{
|
||||
ID: server.ID,
|
||||
Node: node,
|
||||
Address: server.Address,
|
||||
Leader: server.Address == leader,
|
||||
Voter: server.Suffrage == raft.Voter,
|
||||
}
|
||||
reply.Servers = append(reply.Servers, entry)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -118,6 +122,6 @@ REMOVE:
|
|||
return err
|
||||
}
|
||||
|
||||
op.srv.logger.Printf("[WARN] consul.operator: Removed Raft peer %q by", args.Address)
|
||||
op.srv.logger.Printf("[WARN] consul.operator: Removed Raft peer %q", args.Address)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -34,13 +34,21 @@ func TestOperator_RaftGetConfiguration(t *testing.T) {
|
|||
if err := future.Error(); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(future.Configuration().Servers) != 1 {
|
||||
t.Fatalf("bad: %v", future.Configuration().Servers)
|
||||
}
|
||||
me := future.Configuration().Servers[0]
|
||||
expected := structs.RaftConfigurationResponse{
|
||||
Configuration: future.Configuration(),
|
||||
NodeMap: map[raft.ServerID]string{
|
||||
raft.ServerID(s1.config.RPCAddr.String()): s1.config.NodeName,
|
||||
Servers: []*structs.RaftServer{
|
||||
&structs.RaftServer{
|
||||
ID: me.ID,
|
||||
Node: s1.config.NodeName,
|
||||
Address: me.Address,
|
||||
Leader: true,
|
||||
Voter: true,
|
||||
},
|
||||
},
|
||||
Leader: raft.ServerID(s1.config.RPCAddr.String()),
|
||||
Index: future.Index(),
|
||||
}
|
||||
if !reflect.DeepEqual(reply, expected) {
|
||||
t.Fatalf("bad: %v", reply)
|
||||
|
@ -102,13 +110,21 @@ func TestOperator_RaftGetConfiguration_ACLDeny(t *testing.T) {
|
|||
if err := future.Error(); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(future.Configuration().Servers) != 1 {
|
||||
t.Fatalf("bad: %v", future.Configuration().Servers)
|
||||
}
|
||||
me := future.Configuration().Servers[0]
|
||||
expected := structs.RaftConfigurationResponse{
|
||||
Configuration: future.Configuration(),
|
||||
NodeMap: map[raft.ServerID]string{
|
||||
raft.ServerID(s1.config.RPCAddr.String()): s1.config.NodeName,
|
||||
Servers: []*structs.RaftServer{
|
||||
&structs.RaftServer{
|
||||
ID: me.ID,
|
||||
Node: s1.config.NodeName,
|
||||
Address: me.Address,
|
||||
Leader: true,
|
||||
Voter: true,
|
||||
},
|
||||
},
|
||||
Leader: raft.ServerID(s1.config.RPCAddr.String()),
|
||||
Index: future.Index(),
|
||||
}
|
||||
if !reflect.DeepEqual(reply, expected) {
|
||||
t.Fatalf("bad: %v", reply)
|
||||
|
|
|
@ -4,23 +4,38 @@ import (
|
|||
"github.com/hashicorp/raft"
|
||||
)
|
||||
|
||||
// RaftServer has information about a server in the Raft configuration.
|
||||
type RaftServer struct {
|
||||
// ID is the unique ID for the server. These are currently the same
|
||||
// as the address, but they will be changed to a real GUID in a future
|
||||
// release of Consul.
|
||||
ID raft.ServerID
|
||||
|
||||
// Node is the node name of the server, as known by Consul, or this
|
||||
// will be set to "(unknown)" otherwise.
|
||||
Node string
|
||||
|
||||
// Address is the IP:port of the server, used for Raft communications.
|
||||
Address raft.ServerAddress
|
||||
|
||||
// Leader is true if this server is the current cluster leader.
|
||||
Leader bool
|
||||
|
||||
// Voter is true if this server has a vote in the cluster. This might
|
||||
// be false if the server is staging and still coming online, or if
|
||||
// it's a non-voting server, which will be added in a future release of
|
||||
// Consul.
|
||||
Voter bool
|
||||
}
|
||||
|
||||
// RaftConfigrationResponse is returned when querying for the current Raft
|
||||
// configuration. This has the low-level Raft structure, as well as some
|
||||
// supplemental information from Consul.
|
||||
// configuration.
|
||||
type RaftConfigurationResponse struct {
|
||||
// Configuration is the low-level Raft configuration structure.
|
||||
Configuration raft.Configuration
|
||||
// Servers has the list of servers in the Raft configuration.
|
||||
Servers []*RaftServer
|
||||
|
||||
// NodeMap maps IDs in the Raft configuration to node names known by
|
||||
// Consul. It's possible that not all configuration entries may have
|
||||
// an entry here if the node isn't known to Consul. Given how this is
|
||||
// generated, this may also contain entries that aren't present in the
|
||||
// Raft configuration.
|
||||
NodeMap map[raft.ServerID]string
|
||||
|
||||
// Leader is the ID of the current Raft leader. This may be blank if
|
||||
// there isn't one.
|
||||
Leader raft.ServerID
|
||||
// Index has the Raft index of this configuration.
|
||||
Index uint64
|
||||
}
|
||||
|
||||
// RaftPeerByAddressRequest is used by the Operator endpoint to apply a Raft
|
||||
|
|
Loading…
Reference in New Issue