1// Copyright 2017 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package e2e
16
17import (
18	"context"
19	"crypto/tls"
20	"fmt"
21	"os"
22	"testing"
23	"time"
24
25	"go.etcd.io/etcd/clientv3"
26	"go.etcd.io/etcd/pkg/testutil"
27	"go.etcd.io/etcd/pkg/transport"
28	"go.etcd.io/etcd/pkg/types"
29)
30
31func TestCtlV3MoveLeaderSecure(t *testing.T) {
32	testCtlV3MoveLeader(t, configTLS)
33}
34
35func TestCtlV3MoveLeaderInsecure(t *testing.T) {
36	testCtlV3MoveLeader(t, configNoTLS)
37}
38
39func testCtlV3MoveLeader(t *testing.T, cfg etcdProcessClusterConfig) {
40	defer testutil.AfterTest(t)
41
42	epc := setupEtcdctlTest(t, &cfg, true)
43	defer func() {
44		if errC := epc.Close(); errC != nil {
45			t.Fatalf("error closing etcd processes (%v)", errC)
46		}
47	}()
48
49	var tcfg *tls.Config
50	if cfg.clientTLS == clientTLS {
51		tinfo := transport.TLSInfo{
52			CertFile:      certPath,
53			KeyFile:       privateKeyPath,
54			TrustedCAFile: caPath,
55		}
56		var err error
57		tcfg, err = tinfo.ClientConfig()
58		if err != nil {
59			t.Fatal(err)
60		}
61	}
62
63	var leadIdx int
64	var leaderID uint64
65	var transferee uint64
66	for i, ep := range epc.EndpointsV3() {
67		cli, err := clientv3.New(clientv3.Config{
68			Endpoints:   []string{ep},
69			DialTimeout: 3 * time.Second,
70			TLS:         tcfg,
71		})
72		if err != nil {
73			t.Fatal(err)
74		}
75		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
76		resp, err := cli.Status(ctx, ep)
77		if err != nil {
78			t.Fatalf("failed to get status from endpoint %s: %v", ep, err)
79		}
80		cancel()
81		cli.Close()
82
83		if resp.Header.GetMemberId() == resp.Leader {
84			leadIdx = i
85			leaderID = resp.Leader
86		} else {
87			transferee = resp.Header.GetMemberId()
88		}
89	}
90
91	os.Setenv("ETCDCTL_API", "3")
92	defer os.Unsetenv("ETCDCTL_API")
93	cx := ctlCtx{
94		t:           t,
95		cfg:         configNoTLS,
96		dialTimeout: 7 * time.Second,
97		epc:         epc,
98	}
99
100	tests := []struct {
101		prefixes []string
102		expect   string
103	}{
104		{ // request to non-leader
105			cx.prefixArgs([]string{cx.epc.EndpointsV3()[(leadIdx+1)%3]}),
106			"no leader endpoint given at ",
107		},
108		{ // request to leader
109			cx.prefixArgs([]string{cx.epc.EndpointsV3()[leadIdx]}),
110			fmt.Sprintf("Leadership transferred from %s to %s", types.ID(leaderID), types.ID(transferee)),
111		},
112	}
113	for i, tc := range tests {
114		cmdArgs := append(tc.prefixes, "move-leader", types.ID(transferee).String())
115		if err := spawnWithExpect(cmdArgs, tc.expect); err != nil {
116			t.Fatalf("#%d: %v", i, err)
117		}
118	}
119}
120