clientv3: support MemberAdd for learner
Added IsLearner flag to clientv3 MemberAdd API.
This commit is contained in:
@ -38,7 +38,7 @@ type Cluster interface {
|
|||||||
MemberList(ctx context.Context) (*MemberListResponse, error)
|
MemberList(ctx context.Context) (*MemberListResponse, error)
|
||||||
|
|
||||||
// MemberAdd adds a new member into the cluster.
|
// MemberAdd adds a new member into the cluster.
|
||||||
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
|
MemberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error)
|
||||||
|
|
||||||
// MemberRemove removes an existing member from the cluster.
|
// MemberRemove removes an existing member from the cluster.
|
||||||
MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
|
MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
|
||||||
@ -71,13 +71,16 @@ func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
|
|||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) {
|
||||||
// fail-fast before panic in rafthttp
|
// fail-fast before panic in rafthttp
|
||||||
if _, err := types.NewURLs(peerAddrs); err != nil {
|
if _, err := types.NewURLs(peerAddrs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &pb.MemberAddRequest{PeerURLs: peerAddrs}
|
r := &pb.MemberAddRequest{
|
||||||
|
PeerURLs: peerAddrs,
|
||||||
|
IsLearner: isLearner,
|
||||||
|
}
|
||||||
resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
|
resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toErr(ctx, err)
|
return nil, toErr(ctx, err)
|
||||||
|
@ -51,7 +51,7 @@ func ExampleCluster_memberAdd() {
|
|||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
peerURLs := endpoints[2:]
|
peerURLs := endpoints[2:]
|
||||||
mresp, err := cli.MemberAdd(context.Background(), peerURLs)
|
mresp, err := cli.MemberAdd(context.Background(), peerURLs, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ package integration
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -52,7 +53,7 @@ func TestMemberAdd(t *testing.T) {
|
|||||||
capi := clus.RandClient()
|
capi := clus.RandClient()
|
||||||
|
|
||||||
urls := []string{"http://127.0.0.1:1234"}
|
urls := []string{"http://127.0.0.1:1234"}
|
||||||
resp, err := capi.MemberAdd(context.Background(), urls)
|
resp, err := capi.MemberAdd(context.Background(), urls, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to add member %v", err)
|
t.Fatalf("failed to add member %v", err)
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ func TestMemberAddUpdateWrongURLs(t *testing.T) {
|
|||||||
{"localhost:1234"},
|
{"localhost:1234"},
|
||||||
}
|
}
|
||||||
for i := range tt {
|
for i := range tt {
|
||||||
_, err := capi.MemberAdd(context.Background(), tt[i])
|
_, err := capi.MemberAdd(context.Background(), tt[i], false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("#%d: MemberAdd err = nil, but error", i)
|
t.Errorf("#%d: MemberAdd err = nil, but error", i)
|
||||||
}
|
}
|
||||||
@ -184,3 +185,47 @@ func TestMemberAddUpdateWrongURLs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMemberAddForLearner(t *testing.T) {
|
||||||
|
defer testutil.AfterTest(t)
|
||||||
|
|
||||||
|
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
capi := clus.RandClient()
|
||||||
|
|
||||||
|
urls := []string{"http://127.0.0.1:1234"}
|
||||||
|
isLearner := true
|
||||||
|
resp, err := capi.MemberAdd(context.Background(), urls, isLearner)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to add member %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Member.IsLearner != isLearner {
|
||||||
|
t.Errorf("Added a member with IsLearner = %v, got %v", isLearner, resp.Member.IsLearner)
|
||||||
|
}
|
||||||
|
|
||||||
|
numOfLearners, err := getNumberOfLearners(clus)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get the number of learners in cluster: %v", err)
|
||||||
|
}
|
||||||
|
if numOfLearners != 1 {
|
||||||
|
t.Errorf("Added 1 learner node to cluster, got %d", numOfLearners)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNumberOfLearners return the number of learner nodes in cluster using MemberList API
|
||||||
|
func getNumberOfLearners(clus *integration.ClusterV3) (int, error) {
|
||||||
|
cli := clus.RandClient()
|
||||||
|
resp, err := cli.MemberList(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to list member %v", err)
|
||||||
|
}
|
||||||
|
numberOfLearners := 0
|
||||||
|
for _, m := range resp.Members {
|
||||||
|
if m.IsLearner {
|
||||||
|
numberOfLearners++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numberOfLearners, nil
|
||||||
|
}
|
||||||
|
@ -55,7 +55,7 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
|
|||||||
|
|
||||||
urls := newEmbedURLs(2)
|
urls := newEmbedURLs(2)
|
||||||
newCURLs, newPURLs := urls[:1], urls[1:]
|
newCURLs, newPURLs := urls[:1], urls[1:]
|
||||||
if _, err = cli.MemberAdd(context.Background(), []string{newPURLs[0].String()}); err != nil {
|
if _, err = cli.MemberAdd(context.Background(), []string{newPURLs[0].String()}, false); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ func memberAddCommandFunc(cmd *cobra.Command, args []string) {
|
|||||||
urls := strings.Split(memberPeerURLs, ",")
|
urls := strings.Split(memberPeerURLs, ",")
|
||||||
ctx, cancel := commandCtx(cmd)
|
ctx, cancel := commandCtx(cmd)
|
||||||
cli := mustClientFromCmd(cmd)
|
cli := mustClientFromCmd(cmd)
|
||||||
resp, err := cli.MemberAdd(ctx, urls)
|
resp, err := cli.MemberAdd(ctx, urls, false)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ExitWithError(ExitError, err)
|
ExitWithError(ExitError, err)
|
||||||
|
@ -63,7 +63,7 @@ func (s *v2v3Server) Leader() types.ID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *v2v3Server) AddMember(ctx context.Context, memb membership.Member) ([]*membership.Member, error) {
|
func (s *v2v3Server) AddMember(ctx context.Context, memb membership.Member) ([]*membership.Member, error) {
|
||||||
resp, err := s.c.MemberAdd(ctx, memb.PeerURLs)
|
resp, err := s.c.MemberAdd(ctx, memb.PeerURLs, memb.IsLearner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -93,6 +93,7 @@ func v3MembersToMembership(v3membs []*pb.Member) []*membership.Member {
|
|||||||
ID: types.ID(m.ID),
|
ID: types.ID(m.ID),
|
||||||
RaftAttributes: membership.RaftAttributes{
|
RaftAttributes: membership.RaftAttributes{
|
||||||
PeerURLs: m.PeerURLs,
|
PeerURLs: m.PeerURLs,
|
||||||
|
IsLearner: m.IsLearner,
|
||||||
},
|
},
|
||||||
Attributes: membership.Attributes{
|
Attributes: membership.Attributes{
|
||||||
Name: m.Name,
|
Name: m.Name,
|
||||||
|
@ -109,7 +109,7 @@ func (cp *clusterProxy) monitor(wa gnaming.Watcher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cp *clusterProxy) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) (*pb.MemberAddResponse, error) {
|
func (cp *clusterProxy) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) (*pb.MemberAddResponse, error) {
|
||||||
mresp, err := cp.clus.MemberAdd(ctx, r.PeerURLs)
|
mresp, err := cp.clus.MemberAdd(ctx, r.PeerURLs, r.IsLearner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user