diff --git a/clientv3/cluster.go b/clientv3/cluster.go index b91b8b3ae..dbaebfb22 100644 --- a/clientv3/cluster.go +++ b/clientv3/cluster.go @@ -23,6 +23,7 @@ import ( ) type ( + Member pb.Member MemberListResponse pb.MemberListResponse MemberAddResponse pb.MemberAddResponse MemberRemoveResponse pb.MemberRemoveResponse @@ -33,6 +34,9 @@ type Cluster interface { // List lists the current cluster membership. MemberList(ctx context.Context) (*MemberListResponse, error) + // Leader returns the current leader member. + MemberLeader(ctx context.Context) (*Member, error) + // MemberAdd adds a new member into the cluster. MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) @@ -131,6 +135,19 @@ func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) { } } +func (c *cluster) MemberLeader(ctx context.Context) (*Member, error) { + resp, err := c.MemberList(ctx) + if err != nil { + return nil, err + } + for _, m := range resp.Members { + if m.IsLeader { + return (*Member)(m), nil + } + } + return nil, nil +} + func (c *cluster) getRemote() pb.ClusterClient { c.mu.Lock() defer c.mu.Unlock() diff --git a/etcdserver/api/v3rpc/member.go b/etcdserver/api/v3rpc/member.go index 4ea64edd5..592f021f1 100644 --- a/etcdserver/api/v3rpc/member.go +++ b/etcdserver/api/v3rpc/member.go @@ -59,7 +59,7 @@ func (cs *ClusterServer) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) return &pb.MemberAddResponse{ Header: cs.header(), - Member: &pb.Member{ID: uint64(m.ID), PeerURLs: m.PeerURLs}, + Member: &pb.Member{ID: uint64(m.ID), IsLeader: m.ID == cs.server.Leader(), PeerURLs: m.PeerURLs}, }, nil } @@ -103,6 +103,7 @@ func (cs *ClusterServer) MemberList(ctx context.Context, r *pb.MemberListRequest protoMembs[i] = &pb.Member{ Name: membs[i].Name, ID: uint64(membs[i].ID), + IsLeader: membs[i].ID == cs.server.Leader(), PeerURLs: membs[i].PeerURLs, ClientURLs: membs[i].ClientURLs, } diff --git a/etcdserver/etcdserverpb/rpc.pb.go b/etcdserver/etcdserverpb/rpc.pb.go index 24d6c4340..baf77217f 100644 --- a/etcdserver/etcdserverpb/rpc.pb.go +++ b/etcdserver/etcdserverpb/rpc.pb.go @@ -994,10 +994,11 @@ type Member struct { ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` // If the member is not started, name will be an empty string. Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - PeerURLs []string `protobuf:"bytes,3,rep,name=peerURLs" json:"peerURLs,omitempty"` + IsLeader bool `protobuf:"varint,3,opt,name=IsLeader,proto3" json:"IsLeader,omitempty"` + PeerURLs []string `protobuf:"bytes,4,rep,name=peerURLs" json:"peerURLs,omitempty"` // If the member is not started, client_URLs will be an zero length // string array. - ClientURLs []string `protobuf:"bytes,4,rep,name=clientURLs" json:"clientURLs,omitempty"` + ClientURLs []string `protobuf:"bytes,5,rep,name=clientURLs" json:"clientURLs,omitempty"` } func (m *Member) Reset() { *m = Member{} } @@ -2868,9 +2869,19 @@ func (m *Member) MarshalTo(data []byte) (int, error) { i = encodeVarintRpc(data, i, uint64(len(m.Name))) i += copy(data[i:], m.Name) } + if m.IsLeader { + data[i] = 0x18 + i++ + if m.IsLeader { + data[i] = 1 + } else { + data[i] = 0 + } + i++ + } if len(m.PeerURLs) > 0 { for _, s := range m.PeerURLs { - data[i] = 0x1a + data[i] = 0x22 i++ l = len(s) for l >= 1<<7 { @@ -2885,7 +2896,7 @@ func (m *Member) MarshalTo(data []byte) (int, error) { } if len(m.ClientURLs) > 0 { for _, s := range m.ClientURLs { - data[i] = 0x22 + data[i] = 0x2a i++ l = len(s) for l >= 1<<7 { @@ -3668,6 +3679,9 @@ func (m *Member) Size() (n int) { if l > 0 { n += 1 + l + sovRpc(uint64(l)) } + if m.IsLeader { + n += 2 + } if len(m.PeerURLs) > 0 { for _, s := range m.PeerURLs { l = len(s) @@ -6859,6 +6873,26 @@ func (m *Member) Unmarshal(data []byte) error { m.Name = string(data[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsLeader", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRpc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.IsLeader = bool(v != 0) + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType) } @@ -6887,7 +6921,7 @@ func (m *Member) Unmarshal(data []byte) error { } m.PeerURLs = append(m.PeerURLs, string(data[iNdEx:postIndex])) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientURLs", wireType) } diff --git a/etcdserver/etcdserverpb/rpc.proto b/etcdserver/etcdserverpb/rpc.proto index 4d4d1af6c..8734c6fb1 100644 --- a/etcdserver/etcdserverpb/rpc.proto +++ b/etcdserver/etcdserverpb/rpc.proto @@ -323,10 +323,11 @@ message Member { uint64 ID = 1; // If the member is not started, name will be an empty string. string name = 2; - repeated string peerURLs = 3; + bool IsLeader = 3; + repeated string peerURLs = 4; // If the member is not started, client_URLs will be an zero length // string array. - repeated string clientURLs = 4; + repeated string clientURLs = 5; } message MemberAddRequest {