*: add MemberAddAsLearner to clientv3 Cluster API

Made changes to Clientv3 Cluster API:

- Added MemberAddAsLearner.
- Reverted changes to MemberAdd - removed input parameter isLearner.
This commit is contained in:
Jingyi Hu
2019-05-14 16:56:44 -07:00
parent 1e38de5b9d
commit 2b76200f70
7 changed files with 71 additions and 14 deletions

View File

@ -16,11 +16,11 @@ package clientv3
import ( import (
"context" "context"
"errors"
pb "go.etcd.io/etcd/v3/etcdserver/etcdserverpb" pb "go.etcd.io/etcd/v3/etcdserver/etcdserverpb"
"go.etcd.io/etcd/v3/pkg/types" "go.etcd.io/etcd/v3/pkg/types"
"errors"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
@ -38,7 +38,10 @@ 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, isLearner bool) (*MemberAddResponse, error) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
// MemberAddAsLearner adds a new learner member into the cluster.
MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*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,7 +74,15 @@ func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
return api return api
} }
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) { func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return c.memberAdd(ctx, peerAddrs, false)
}
func (c *cluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
return c.memberAdd(ctx, peerAddrs, true)
}
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

View File

@ -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, false) mresp, err := cli.MemberAdd(context.Background(), peerURLs)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -59,6 +59,27 @@ func ExampleCluster_memberAdd() {
// added member.PeerURLs: [http://localhost:32380] // added member.PeerURLs: [http://localhost:32380]
} }
func ExampleCluster_memberAddAsLearner() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints[:2],
DialTimeout: dialTimeout,
})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
peerURLs := endpoints[2:]
mresp, err := cli.MemberAddAsLearner(context.Background(), peerURLs)
if err != nil {
log.Fatal(err)
}
fmt.Println("added member.PeerURLs:", mresp.Member.PeerURLs)
fmt.Println("added member.IsLearner:", mresp.Member.IsLearner)
// added member.PeerURLs: [http://localhost:32380]
// added member.IsLearner: true
}
func ExampleCluster_memberRemove() { func ExampleCluster_memberRemove() {
cli, err := clientv3.New(clientv3.Config{ cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints[1:], Endpoints: endpoints[1:],

View File

@ -53,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, false) resp, err := capi.MemberAdd(context.Background(), urls)
if err != nil { if err != nil {
t.Fatalf("failed to add member %v", err) t.Fatalf("failed to add member %v", err)
} }
@ -175,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], false) _, err := capi.MemberAdd(context.Background(), tt[i])
if err == nil { if err == nil {
t.Errorf("#%d: MemberAdd err = nil, but error", i) t.Errorf("#%d: MemberAdd err = nil, but error", i)
} }
@ -195,14 +195,13 @@ func TestMemberAddForLearner(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"}
isLearner := true resp, err := capi.MemberAddAsLearner(context.Background(), urls)
resp, err := capi.MemberAdd(context.Background(), urls, isLearner)
if err != nil { if err != nil {
t.Fatalf("failed to add member %v", err) t.Fatalf("failed to add member %v", err)
} }
if resp.Member.IsLearner != isLearner { if !resp.Member.IsLearner {
t.Errorf("Added a member with IsLearner = %v, got %v", isLearner, resp.Member.IsLearner) t.Errorf("Added a member as learner, got resp.Member.IsLearner = %v", resp.Member.IsLearner)
} }
numOfLearners, err := getNumberOfLearners(clus) numOfLearners, err := getNumberOfLearners(clus)

View File

@ -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()}, false); err != nil { if _, err = cli.MemberAdd(context.Background(), []string{newPURLs[0].String()}); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.etcd.io/etcd/v3/clientv3"
) )
var ( var (
@ -122,7 +123,15 @@ 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, isLearner) var (
resp *clientv3.MemberAddResponse
err error
)
if isLearner {
resp, err = cli.MemberAddAsLearner(ctx, urls)
} else {
resp, err = cli.MemberAdd(ctx, urls)
}
cancel() cancel()
if err != nil { if err != nil {
ExitWithError(ExitError, err) ExitWithError(ExitError, err)

View File

@ -63,7 +63,8 @@ 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, memb.IsLearner) // adding member as learner is not supported by V2 Server.
resp, err := s.c.MemberAdd(ctx, memb.PeerURLs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -109,7 +109,23 @@ 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, r.IsLearner) if r.IsLearner {
return cp.memberAddAsLearner(ctx, r.PeerURLs)
}
return cp.memberAdd(ctx, r.PeerURLs)
}
func (cp *clusterProxy) memberAdd(ctx context.Context, peerURLs []string) (*pb.MemberAddResponse, error) {
mresp, err := cp.clus.MemberAdd(ctx, peerURLs)
if err != nil {
return nil, err
}
resp := (pb.MemberAddResponse)(*mresp)
return &resp, err
}
func (cp *clusterProxy) memberAddAsLearner(ctx context.Context, peerURLs []string) (*pb.MemberAddResponse, error) {
mresp, err := cp.clus.MemberAddAsLearner(ctx, peerURLs)
if err != nil { if err != nil {
return nil, err return nil, err
} }