diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go index 4503e14ec..80f3bc9b9 100644 --- a/etcdctl/ctlv3/command/printer_simple.go +++ b/etcdctl/ctlv3/command/printer_simple.go @@ -126,7 +126,11 @@ func (s *simplePrinter) Alarm(resp v3.AlarmResponse) { } func (s *simplePrinter) MemberAdd(r v3.MemberAddResponse) { - fmt.Printf("Member %16x added to cluster %16x\n", r.Member.ID, r.Header.ClusterId) + asLearner := " " + if r.Member.IsLearner { + asLearner = " as learner " + } + fmt.Printf("Member %16x added%sto cluster %16x\n", r.Member.ID, asLearner, r.Header.ClusterId) } func (s *simplePrinter) MemberRemove(id uint64, r v3.MemberRemoveResponse) { diff --git a/tests/e2e/ctl_v3_member_test.go b/tests/e2e/ctl_v3_member_test.go index 1766a0975..6ebd73597 100644 --- a/tests/e2e/ctl_v3_member_test.go +++ b/tests/e2e/ctl_v3_member_test.go @@ -28,7 +28,11 @@ import ( func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) } func TestCtlV3MemberListWithHex(t *testing.T) { testCtl(t, memberListWithHexTest) } -func TestCtlV3MemberUpdate(t *testing.T) { testCtl(t, memberUpdateTest) } + +func TestCtlV3MemberAdd(t *testing.T) { testCtl(t, memberAddTest) } +func TestCtlV3MemberAddAsLearner(t *testing.T) { testCtl(t, memberAddAsLearnerTest) } + +func TestCtlV3MemberUpdate(t *testing.T) { testCtl(t, memberUpdateTest) } func TestCtlV3MemberUpdateNoTLS(t *testing.T) { testCtl(t, memberUpdateTest, withCfg(*e2e.NewConfigNoTLS())) } @@ -137,12 +141,28 @@ func ctlV3MemberRemove(cx ctlCtx, ep, memberID, clusterID string) error { return e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, fmt.Sprintf("%s removed from cluster %s", memberID, clusterID)) } +func memberAddTest(cx ctlCtx) { + peerURL := fmt.Sprintf("http://localhost:%d", e2e.EtcdProcessBasePort+11) + if err := ctlV3MemberAdd(cx, peerURL, false); err != nil { + cx.t.Fatal(err) + } +} + +func memberAddAsLearnerTest(cx ctlCtx) { + peerURL := fmt.Sprintf("http://localhost:%d", e2e.EtcdProcessBasePort+11) + if err := ctlV3MemberAdd(cx, peerURL, true); err != nil { + cx.t.Fatal(err) + } +} + func ctlV3MemberAdd(cx ctlCtx, peerURL string, isLearner bool) error { cmdArgs := append(cx.PrefixArgs(), "member", "add", "newmember", fmt.Sprintf("--peer-urls=%s", peerURL)) + asLearner := " " if isLearner { cmdArgs = append(cmdArgs, "--learner") + asLearner = " as learner " } - return e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, " added to cluster ") + return e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, fmt.Sprintf(" added%sto cluster ", asLearner)) } func memberUpdateTest(cx ctlCtx) { diff --git a/tests/framework/integration/cluster.go b/tests/framework/integration/cluster.go index 35beb84ab..e6446d91e 100644 --- a/tests/framework/integration/cluster.go +++ b/tests/framework/integration/cluster.go @@ -1557,7 +1557,7 @@ func (c *Cluster) GetLearnerMembers() ([]*pb.Member, error) { return learners, nil } -// AddAndLaunchLearnerMember creates a leaner member, adds it to Cluster +// AddAndLaunchLearnerMember creates a learner member, adds it to Cluster // via v3 MemberAdd API, and then launches the new member. func (c *Cluster) AddAndLaunchLearnerMember(t testutil.TB) { m := c.mustNewMember(t)