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 "go.etcd.io/etcd/clientv3" 25 pb "go.etcd.io/etcd/etcdserver/etcdserverpb" 26 "go.etcd.io/etcd/integration" 27 "go.etcd.io/etcd/pkg/testutil" 28 "go.etcd.io/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.insecure", 42 CertFile: "../../integration/fixtures-expired/server.crt", 43 TrustedCAFile: "../../integration/fixtures-expired/ca.crt", 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 159 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 160 defer cancel() 161 if _, err := cli.Get(ctx, "foo"); err != nil { 162 t.Fatal(err) 163 } 164} 165 166func TestRejectOldCluster(t *testing.T) { 167 defer testutil.AfterTest(t) 168 // 2 endpoints to test multi-endpoint Status 169 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2, SkipCreatingClient: true}) 170 defer clus.Terminate(t) 171 172 cfg := clientv3.Config{ 173 Endpoints: []string{clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr()}, 174 DialTimeout: 5 * time.Second, 175 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 176 RejectOldCluster: true, 177 } 178 cli, err := clientv3.New(cfg) 179 if err != nil { 180 t.Fatal(err) 181 } 182 cli.Close() 183} 184 185// TestDialForeignEndpoint checks an endpoint that is not registered 186// with the balancer can be dialed. 187func TestDialForeignEndpoint(t *testing.T) { 188 defer testutil.AfterTest(t) 189 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2}) 190 defer clus.Terminate(t) 191 192 conn, err := clus.Client(0).Dial(clus.Client(1).Endpoints()[0]) 193 if err != nil { 194 t.Fatal(err) 195 } 196 defer conn.Close() 197 198 // grpc can return a lazy connection that's not connected yet; confirm 199 // that it can communicate with the cluster. 200 kvc := clientv3.NewKVFromKVClient(pb.NewKVClient(conn), clus.Client(0)) 201 ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) 202 defer cancel() 203 if _, gerr := kvc.Get(ctx, "abc"); gerr != nil { 204 t.Fatal(err) 205 } 206} 207 208// TestSetEndpointAndPut checks that a Put following a SetEndpoints 209// to a working endpoint will always succeed. 210func TestSetEndpointAndPut(t *testing.T) { 211 defer testutil.AfterTest(t) 212 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 2}) 213 defer clus.Terminate(t) 214 215 clus.Client(1).SetEndpoints(clus.Members[0].GRPCAddr()) 216 _, err := clus.Client(1).Put(context.TODO(), "foo", "bar") 217 if err != nil && !strings.Contains(err.Error(), "closing") { 218 t.Fatal(err) 219 } 220} 221