Skip to content

Commit

Permalink
clientv3, etcdctl: MemberPromote for learner
Browse files Browse the repository at this point in the history
  • Loading branch information
WIZARD-CXY authored and jingyih committed May 15, 2019
1 parent bd7f428 commit a039f2e
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 2 deletions.
35 changes: 35 additions & 0 deletions clientv3/integration/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,27 @@ func TestMemberAddForLearner(t *testing.T) {
if !resp.Member.IsLearner {
t.Errorf("Added a member as learner, got resp.Member.IsLearner = %v", resp.Member.IsLearner)
}
}

func TestMemberPromoteForLearner(t *testing.T) {
// TODO test not ready learner promotion.
defer testutil.AfterTest(t)

clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// TODO change the random client to client that talk to leader directly.
capi := clus.RandClient()

urls := []string{"http://127.0.0.1:1234"}
isLearner := true
resp, err := capi.MemberAddAsLearner(context.Background(), urls)
if err != nil {
t.Fatalf("failed to add member %v", err)
}

if !resp.Member.IsLearner {
t.Errorf("Added a member as learner, got resp.Member.IsLearner = %v", resp.Member.IsLearner)
}

learners, err := clus.GetLearnerMembers()
if err != nil {
Expand All @@ -210,4 +231,18 @@ func TestMemberAddForLearner(t *testing.T) {
if len(learners) != 1 {
t.Errorf("Added 1 learner node to cluster, got %d", len(learners))
}
_, err = capi.MemberPromote(context.Background(), resp.Member.ID)

if err != nil {
t.Fatalf("failed to promote member error: %v", err)
}

learners, err = clus.GetLearnerMembers()
if err != nil {
t.Fatalf("failed to get the number of learners in cluster: %v", err)
}
if len(learners) != 0 {
t.Errorf("learner promoted, expect 0 learner, got %d", len(learners))
}

}
35 changes: 35 additions & 0 deletions etcdctl/ctlv3/command/member_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewMemberCommand() *cobra.Command {
mc.AddCommand(NewMemberRemoveCommand())
mc.AddCommand(NewMemberUpdateCommand())
mc.AddCommand(NewMemberListCommand())
mc.AddCommand(NewMemberPromoteCommand())

return mc
}
Expand Down Expand Up @@ -100,6 +101,20 @@ The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs, Is Learne
return cc
}

// NewMemberPromoteCommand returns the cobra command for "member promote".
func NewMemberPromoteCommand() *cobra.Command {
cc := &cobra.Command{
Use: "promote <memberID>",
Short: "Promotes a non-voting member in the cluster",
Long: `Promotes a non-voting learner member to a voting one in the cluster.
`,

Run: memberPromoteCommandFunc,
}

return cc
}

// memberAddCommandFunc executes the "member add" command.
func memberAddCommandFunc(cmd *cobra.Command, args []string) {
if len(args) < 1 {
Expand Down Expand Up @@ -238,3 +253,23 @@ func memberListCommandFunc(cmd *cobra.Command, args []string) {

display.MemberList(*resp)
}

// memberPromoteCommandFunc executes the "member promote" command.
func memberPromoteCommandFunc(cmd *cobra.Command, args []string) {
if len(args) != 1 {
ExitWithError(ExitBadArgs, fmt.Errorf("member ID is not provided"))
}

id, err := strconv.ParseUint(args[0], 16, 64)
if err != nil {
ExitWithError(ExitBadArgs, fmt.Errorf("bad member ID arg (%v), expecting ID in Hex", err))
}

ctx, cancel := commandCtx(cmd)
resp, err := mustClientFromCmd(cmd).MemberPromote(ctx, id)
cancel()
if err != nil {
ExitWithError(ExitError, err)
}
display.MemberPromote(id, *resp)
}
1 change: 1 addition & 0 deletions etcdctl/ctlv3/command/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type printer interface {
MemberAdd(v3.MemberAddResponse)
MemberRemove(id uint64, r v3.MemberRemoveResponse)
MemberUpdate(id uint64, r v3.MemberUpdateResponse)
MemberPromote(id uint64, r v3.MemberPromoteResponse)
MemberList(v3.MemberListResponse)

EndpointHealth([]epHealth)
Expand Down
4 changes: 4 additions & 0 deletions etcdctl/ctlv3/command/printer_simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ func (s *simplePrinter) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
fmt.Printf("Member %16x updated in cluster %16x\n", id, r.Header.ClusterId)
}

func (s *simplePrinter) MemberPromote(id uint64, r v3.MemberPromoteResponse) {
fmt.Printf("Member %16x promoted in cluster %16x\n", id, r.Header.ClusterId)
}

func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
_, rows := makeMemberListTable(resp)
for _, row := range rows {
Expand Down
8 changes: 6 additions & 2 deletions etcdserver/api/v3rpc/rpctypes/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ var (
ErrGRPCMemberNotEnoughStarted = status.New(codes.FailedPrecondition, "etcdserver: re-configuration failed due to not enough started members").Err()
ErrGRPCMemberBadURLs = status.New(codes.InvalidArgument, "etcdserver: given member URLs are invalid").Err()
ErrGRPCMemberNotFound = status.New(codes.NotFound, "etcdserver: member not found").Err()
ErrGRPCMemberNotLearner = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member which catches up with peers").Err()
ErrGRPCLearnerNotReady = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member").Err()
ErrGRPCMemberNotLearner = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member").Err()
ErrGRPCLearnerNotReady = status.New(codes.FailedPrecondition, "etcdserver: can only promote a learner member which catches up with leader").Err()

ErrGRPCRequestTooLarge = status.New(codes.InvalidArgument, "etcdserver: request is too large").Err()
ErrGRPCRequestTooManyRequests = status.New(codes.ResourceExhausted, "etcdserver: too many requests").Err()
Expand Down Expand Up @@ -95,6 +95,8 @@ var (
ErrorDesc(ErrGRPCMemberNotEnoughStarted): ErrGRPCMemberNotEnoughStarted,
ErrorDesc(ErrGRPCMemberBadURLs): ErrGRPCMemberBadURLs,
ErrorDesc(ErrGRPCMemberNotFound): ErrGRPCMemberNotFound,
ErrorDesc(ErrGRPCMemberNotLearner): ErrGRPCMemberNotLearner,
ErrorDesc(ErrGRPCLearnerNotReady): ErrGRPCLearnerNotReady,

ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
ErrorDesc(ErrGRPCRequestTooManyRequests): ErrGRPCRequestTooManyRequests,
Expand Down Expand Up @@ -149,6 +151,8 @@ var (
ErrMemberNotEnoughStarted = Error(ErrGRPCMemberNotEnoughStarted)
ErrMemberBadURLs = Error(ErrGRPCMemberBadURLs)
ErrMemberNotFound = Error(ErrGRPCMemberNotFound)
ErrMemberNotLearner = Error(ErrGRPCMemberNotLearner)
ErrMemberLearnerNotReady = Error(ErrGRPCLearnerNotReady)

ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)
ErrTooManyRequests = Error(ErrGRPCRequestTooManyRequests)
Expand Down

0 comments on commit a039f2e

Please sign in to comment.