Browse Source

clientv3: support MemberAdd for learner

Added IsLearner flag to clientv3 MemberAdd API.
Jingyi Hu 6 years ago
parent
commit
fc14608cb7

+ 6 - 3
clientv3/cluster.go

@@ -38,7 +38,7 @@ type Cluster interface {
 	MemberList(ctx context.Context) (*MemberListResponse, error)
 
 	// 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(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
@@ -71,13 +71,16 @@ func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
 	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
 	if _, err := types.NewURLs(peerAddrs); err != nil {
 		return nil, err
 	}
 
-	r := &pb.MemberAddRequest{PeerURLs: peerAddrs}
+	r := &pb.MemberAddRequest{
+		PeerURLs:  peerAddrs,
+		IsLearner: isLearner,
+	}
 	resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
 	if err != nil {
 		return nil, toErr(ctx, err)

+ 1 - 1
clientv3/example_cluster_test.go

@@ -51,7 +51,7 @@ func ExampleCluster_memberAdd() {
 	defer cli.Close()
 
 	peerURLs := endpoints[2:]
-	mresp, err := cli.MemberAdd(context.Background(), peerURLs)
+	mresp, err := cli.MemberAdd(context.Background(), peerURLs, false)
 	if err != nil {
 		log.Fatal(err)
 	}

+ 47 - 2
clientv3/integration/cluster_test.go

@@ -16,6 +16,7 @@ package integration
 
 import (
 	"context"
+	"fmt"
 	"reflect"
 	"strings"
 	"testing"
@@ -52,7 +53,7 @@ func TestMemberAdd(t *testing.T) {
 	capi := clus.RandClient()
 
 	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 {
 		t.Fatalf("failed to add member %v", err)
 	}
@@ -174,7 +175,7 @@ func TestMemberAddUpdateWrongURLs(t *testing.T) {
 		{"localhost:1234"},
 	}
 	for i := range tt {
-		_, err := capi.MemberAdd(context.Background(), tt[i])
+		_, err := capi.MemberAdd(context.Background(), tt[i], false)
 		if err == nil {
 			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
+}

+ 1 - 1
clientv3/snapshot/member_test.go

@@ -55,7 +55,7 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
 
 	urls := newEmbedURLs(2)
 	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)
 	}
 

+ 1 - 1
etcdctl/ctlv3/command/member_command.go

@@ -118,7 +118,7 @@ func memberAddCommandFunc(cmd *cobra.Command, args []string) {
 	urls := strings.Split(memberPeerURLs, ",")
 	ctx, cancel := commandCtx(cmd)
 	cli := mustClientFromCmd(cmd)
-	resp, err := cli.MemberAdd(ctx, urls)
+	resp, err := cli.MemberAdd(ctx, urls, false)
 	cancel()
 	if err != nil {
 		ExitWithError(ExitError, err)

+ 3 - 2
etcdserver/api/v2v3/server.go

@@ -63,7 +63,7 @@ func (s *v2v3Server) Leader() types.ID {
 }
 
 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 {
 		return nil, err
 	}
@@ -92,7 +92,8 @@ func v3MembersToMembership(v3membs []*pb.Member) []*membership.Member {
 		membs[i] = &membership.Member{
 			ID: types.ID(m.ID),
 			RaftAttributes: membership.RaftAttributes{
-				PeerURLs: m.PeerURLs,
+				PeerURLs:  m.PeerURLs,
+				IsLearner: m.IsLearner,
 			},
 			Attributes: membership.Attributes{
 				Name:       m.Name,

+ 1 - 1
proxy/grpcproxy/cluster.go

@@ -109,7 +109,7 @@ func (cp *clusterProxy) monitor(wa gnaming.Watcher) {
 }
 
 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 {
 		return nil, err
 	}