1// Copyright 2016 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 integration 16 17import ( 18 "context" 19 "math/rand" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/coreos/etcd/clientv3" 25 pb "github.com/coreos/etcd/etcdserver/etcdserverpb" 26 "github.com/coreos/etcd/integration" 27 "github.com/coreos/etcd/pkg/testutil" 28 "github.com/coreos/etcd/pkg/transport" 29 "google.golang.org/grpc" 30) 31 32var ( 33 testTLSInfo = transport.TLSInfo{ 34 KeyFile: "../../integration/fixtures/server.key.insecure", 35 CertFile: "../../integration/fixtures/server.crt", 36 TrustedCAFile: "../../integration/fixtures/ca.crt", 37 ClientCertAuth: true, 38 } 39 40 testTLSInfoExpired = transport.TLSInfo{ 41 KeyFile: "../../integration/fixtures-expired/server-key.pem", 42 CertFile: "../../integration/fixtures-expired/server.pem", 43 TrustedCAFile: "../../integration/fixtures-expired/etcd-root-ca.pem", 44 ClientCertAuth: true, 45 } 46) 47 48// TestDialTLSExpired tests client with expired certs fails to dial. 49func TestDialTLSExpired(t *testing.T) { 50 defer testutil.AfterTest(t) 51 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1, PeerTLS: &testTLSInfo, ClientTLS: &testTLSInfo, SkipCreatingClient: true}) 52 defer clus.Terminate(t) 53 54 tls, err := testTLSInfoExpired.ClientConfig() 55 if err != nil { 56 t.Fatal(err) 57 } 58 // expect remote errors "tls: bad certificate" 59 _, err = clientv3.New(clientv3.Config{ 60 Endpoints: []string{clus.Members[0].GRPCAddr()}, 61 DialTimeout: 3 * time.Second, 62 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 63 TLS: tls, 64 }) 65 if !isClientTimeout(err) { 66 t.Fatalf("expected dial timeout error, got %v", err) 67 } 68} 69 70// TestDialTLSNoConfig ensures the client fails to dial / times out 71// when TLS endpoints (https, unixs) are given but no tls config. 72func TestDialTLSNoConfig(t *testing.T) { 73 defer testutil.AfterTest(t) 74 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1, ClientTLS: &testTLSInfo, SkipCreatingClient: true}) 75 defer clus.Terminate(t) 76 // expect "signed by unknown authority" 77 c, err := clientv3.New(clientv3.Config{ 78 Endpoints: []string{clus.Members[0].GRPCAddr()}, 79 DialTimeout: time.Second, 80 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 81 }) 82 defer func() { 83 if c != nil { 84 c.Close() 85 } 86 }() 87 if !isClientTimeout(err) { 88 t.Fatalf("expected dial timeout error, got %v", err) 89 } 90} 91 92// TestDialSetEndpointsBeforeFail ensures SetEndpoints can replace unavailable 93// endpoints with available ones. 94func TestDialSetEndpointsBeforeFail(t *testing.T) { 95 testDialSetEndpoints(t, true) 96} 97 98func TestDialSetEndpointsAfterFail(t *testing.T) { 99 testDialSetEndpoints(t, false) 100} 101 102// testDialSetEndpoints ensures SetEndpoints can replace unavailable endpoints with available ones. 103func testDialSetEndpoints(t *testing.T, setBefore bool) { 104 defer testutil.AfterTest(t) 105 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3, SkipCreatingClient: true}) 106 defer clus.Terminate(t) 107 108 // get endpoint list 109 eps := make([]string, 3) 110 for i := range eps { 111 eps[i] = clus.Members[i].GRPCAddr() 112 } 113 toKill := rand.Intn(len(eps)) 114 115 cfg := clientv3.Config{ 116 Endpoints: []string{eps[toKill]}, 117 DialTimeout: 1 * time.Second, 118 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 119 } 120 cli, err := clientv3.New(cfg) 121 if err != nil { 122 t.Fatal(err) 123 } 124 defer cli.Close() 125 126 if setBefore { 127 cli.SetEndpoints(eps[toKill%3], eps[(toKill+1)%3]) 128 } 129 // make a dead node 130 clus.Members[toKill].Stop(t) 131 clus.WaitLeader(t) 132 133 if !setBefore { 134 cli.SetEndpoints(eps[toKill%3], eps[(toKill+1)%3]) 135 } 136 time.Sleep(time.Second * 2) 137 ctx, cancel := context.WithTimeout(context.Background(), integration.RequestWaitTimeout) 138 if _, err = cli.Get(ctx, "foo", clientv3.WithSerializable()); err != nil { 139 t.Fatal(err) 140 } 141 cancel() 142} 143 144// TestSwitchSetEndpoints ensures SetEndpoints can switch one endpoint 145// with a new one that doesn't include original endpoint. 146func TestSwitchSetEndpoints(t *testing.T) { 147 defer testutil.AfterTest(t) 148 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 149 defer clus.Terminate(t) 150 151 // get non partitioned members endpoints 152 eps := []string{clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr()} 153 154 cli := clus.Client(0) 155 clus.Members[0].InjectPartition(t, clus.Members[1:]...) 156 157 cli.SetEndpoints(eps...) 158 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 159 defer cancel() 160 if _, err := cli.Get(ctx, "foo"); err != nil { 161 t.Fatal(err) 162 } 163} 164 165func TestRejectOldCluster(t *testing.T) { 166 defer testutil.AfterTest(t) 167 // 2 endpoints to test multi-endpoint Status 168 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2, SkipCreatingClient: true}) 169 defer clus.Terminate(t) 170 171 cfg := clientv3.Config{ 172 Endpoints: []string{clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr()}, 173 DialTimeout: 5 * time.Second, 174 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 175 RejectOldCluster: true, 176 } 177 cli, err := clientv3.New(cfg) 178 if err != nil { 179 t.Fatal(err) 180 } 181 cli.Close() 182} 183 184// TestDialForeignEndpoint checks an endpoint that is not registered 185// with the balancer can be dialed. 186func TestDialForeignEndpoint(t *testing.T) { 187 defer testutil.AfterTest(t) 188 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2}) 189 defer clus.Terminate(t) 190 191 conn, err := clus.Client(0).Dial(clus.Client(1).Endpoints()[0]) 192 if err != nil { 193 t.Fatal(err) 194 } 195 defer conn.Close() 196 197 // grpc can return a lazy connection that's not connected yet; confirm 198 // that it can communicate with the cluster. 199 kvc := clientv3.NewKVFromKVClient(pb.NewKVClient(conn), clus.Client(0)) 200 ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) 201 defer cancel() 202 if _, gerr := kvc.Get(ctx, "abc"); gerr != nil { 203 t.Fatal(err) 204 } 205} 206 207// TestSetEndpointAndPut checks that a Put following a SetEndpoints 208// to a working endpoint will always succeed. 209func TestSetEndpointAndPut(t *testing.T) { 210 defer testutil.AfterTest(t) 211 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2}) 212 defer clus.Terminate(t) 213 214 clus.Client(1).SetEndpoints(clus.Members[0].GRPCAddr()) 215 _, err := clus.Client(1).Put(context.TODO(), "foo", "bar") 216 if err != nil && !strings.Contains(err.Error(), "closing") { 217 t.Fatal(err) 218 } 219} 220