Skip to content

Commit

Permalink
Merge pull request etcd-io#4 from jingyih/add_learner_clientv3
Browse files Browse the repository at this point in the history
clientv3, etcdctl: MemberAdd for learner
  • Loading branch information
jingyih authored Mar 21, 2019
2 parents f74f2f2 + 5e95b3e commit d1797d7
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 12 deletions.
9 changes: 6 additions & 3 deletions clientv3/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion clientv3/example_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
49 changes: 47 additions & 2 deletions clientv3/integration/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package integration

import (
"context"
"fmt"
"reflect"
"testing"

Expand Down Expand Up @@ -51,7 +52,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)
}
Expand Down Expand Up @@ -149,7 +150,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)
}
Expand All @@ -159,3 +160,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
}
2 changes: 1 addition & 1 deletion clientv3/snapshot/member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
8 changes: 6 additions & 2 deletions etcdctl/ctlv3/command/member_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import (
"github.com/spf13/cobra"
)

var memberPeerURLs string
var (
memberPeerURLs string
isLearner bool
)

// NewMemberCommand returns the cobra command for "member".
func NewMemberCommand() *cobra.Command {
Expand All @@ -50,6 +53,7 @@ func NewMemberAddCommand() *cobra.Command {
}

cc.Flags().StringVar(&memberPeerURLs, "peer-urls", "", "comma separated peer URLs for the new member.")
cc.Flags().BoolVar(&isLearner, "learner", false, "indicates if the new member is raft learner")

return cc
}
Expand Down Expand Up @@ -118,7 +122,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, isLearner)
cancel()
if err != nil {
ExitWithError(ExitError, err)
Expand Down
5 changes: 3 additions & 2 deletions etcdserver/api/v2v3/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion proxy/grpcproxy/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down

0 comments on commit d1797d7

Please sign in to comment.