diff --git a/etcdctl/ctlv3/command/move_leader_command.go b/etcdctl/ctlv3/command/move_leader_command.go index 1aee99b24445..a07e095b5e1a 100644 --- a/etcdctl/ctlv3/command/move_leader_command.go +++ b/etcdctl/ctlv3/command/move_leader_command.go @@ -42,7 +42,8 @@ func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) { ExitWithError(ExitBadArgs, err) } - c := mustClientFromCmd(cmd) + cfg := clientConfigFromCmd(cmd) + c := cfg.mustClient() eps := c.Endpoints() c.Close() @@ -52,7 +53,6 @@ func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) { var leaderCli *clientv3.Client var leaderID uint64 for _, ep := range eps { - cfg := clientConfigFromCmd(cmd) cfg.endpoints = []string{ep} cli := cfg.mustClient() resp, serr := cli.Status(ctx, ep) diff --git a/tests/e2e/ctl_v3_move_leader_test.go b/tests/e2e/ctl_v3_move_leader_test.go index 507ca4c16213..599addca6321 100644 --- a/tests/e2e/ctl_v3_move_leader_test.go +++ b/tests/e2e/ctl_v3_move_leader_test.go @@ -36,6 +36,14 @@ func TestCtlV3MoveLeaderInsecure(t *testing.T) { testCtlV3MoveLeader(t, *newConfigNoTLS()) } +func TestCtlV3MoveLeaderWithEnvSecure(t *testing.T) { + testCtlV3MoveLeaderWithEnv(t, *newConfigTLS()) +} + +func TestCtlV3MoveLeaderWithEnvInsecure(t *testing.T) { + testCtlV3MoveLeaderWithEnv(t, *newConfigNoTLS()) +} + func testCtlV3MoveLeader(t *testing.T, cfg etcdProcessClusterConfig) { defer testutil.AfterTest(t) @@ -117,3 +125,82 @@ func testCtlV3MoveLeader(t *testing.T, cfg etcdProcessClusterConfig) { } } } + +func testCtlV3MoveLeaderWithEnv(t *testing.T, cfg etcdProcessClusterConfig) { + defer testutil.AfterTest(t) + + epc := setupEtcdctlTest(t, &cfg, true) + defer func() { + if errC := epc.Close(); errC != nil { + t.Fatalf("error closing etcd processes (%v)", errC) + } + }() + + var tcfg *tls.Config + if cfg.clientTLS == clientTLS { + tinfo := transport.TLSInfo{ + CertFile: certPath, + KeyFile: privateKeyPath, + TrustedCAFile: caPath, + } + var err error + tcfg, err = tinfo.ClientConfig() + if err != nil { + t.Fatal(err) + } + } + + var leadIdx int + var leaderID uint64 + var transferee uint64 + for i, ep := range epc.EndpointsV3() { + cli, err := clientv3.New(clientv3.Config{ + Endpoints: []string{ep}, + DialTimeout: 3 * time.Second, + TLS: tcfg, + }) + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + resp, err := cli.Status(ctx, ep) + if err != nil { + t.Fatalf("failed to get status from endpoint %s: %v", ep, err) + } + cancel() + cli.Close() + + if resp.Header.GetMemberId() == resp.Leader { + leadIdx = i + leaderID = resp.Leader + } else { + transferee = resp.Header.GetMemberId() + } + } + + os.Setenv("ETCDCTL_API", "3") + defer os.Unsetenv("ETCDCTL_API") + cx := ctlCtx{ + t: t, + cfg: *newConfigNoTLS(), + dialTimeout: 7 * time.Second, + epc: epc, + envMap: map[string]struct{}{}, + } + + tests := []struct { + prefixes []string + expect string + }{ + { // request to leader + cx.prefixArgs([]string{cx.epc.EndpointsV3()[leadIdx]}), + fmt.Sprintf("Leadership transferred from %s to %s", types.ID(leaderID), types.ID(transferee)), + }, + } + for i, tc := range tests { + cmdArgs := append(tc.prefixes, "move-leader", types.ID(transferee).String()) + if err := spawnWithExpect(cmdArgs, tc.expect); err != nil { + t.Fatalf("#%d: %v", i, err) + } + } +}