go.etcd.io/etcd@v3.3.27+incompatible/clientv3/integration/maintenance_test.go (about) 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 15 package integration 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "path/filepath" 24 "testing" 25 "time" 26 27 "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes" 28 "github.com/coreos/etcd/integration" 29 "github.com/coreos/etcd/lease" 30 "github.com/coreos/etcd/mvcc" 31 "github.com/coreos/etcd/mvcc/backend" 32 "github.com/coreos/etcd/pkg/testutil" 33 ) 34 35 func TestMaintenanceHashKV(t *testing.T) { 36 defer testutil.AfterTest(t) 37 38 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 39 defer clus.Terminate(t) 40 41 for i := 0; i < 3; i++ { 42 if _, err := clus.RandClient().Put(context.Background(), "foo", "bar"); err != nil { 43 t.Fatal(err) 44 } 45 } 46 47 var hv uint32 48 for i := 0; i < 3; i++ { 49 cli := clus.Client(i) 50 // ensure writes are replicated 51 if _, err := cli.Get(context.TODO(), "foo"); err != nil { 52 t.Fatal(err) 53 } 54 hresp, err := cli.HashKV(context.Background(), clus.Members[i].GRPCAddr(), 0) 55 if err != nil { 56 t.Fatal(err) 57 } 58 if hv == 0 { 59 hv = hresp.Hash 60 continue 61 } 62 if hv != hresp.Hash { 63 t.Fatalf("#%d: hash expected %d, got %d", i, hv, hresp.Hash) 64 } 65 } 66 } 67 68 func TestMaintenanceMoveLeader(t *testing.T) { 69 defer testutil.AfterTest(t) 70 71 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3}) 72 defer clus.Terminate(t) 73 74 oldLeadIdx := clus.WaitLeader(t) 75 targetIdx := (oldLeadIdx + 1) % 3 76 target := uint64(clus.Members[targetIdx].ID()) 77 78 cli := clus.Client(targetIdx) 79 _, err := cli.MoveLeader(context.Background(), target) 80 if err != rpctypes.ErrNotLeader { 81 t.Fatalf("error expected %v, got %v", rpctypes.ErrNotLeader, err) 82 } 83 84 cli = clus.Client(oldLeadIdx) 85 _, err = cli.MoveLeader(context.Background(), target) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 leadIdx := clus.WaitLeader(t) 91 lead := uint64(clus.Members[leadIdx].ID()) 92 if target != lead { 93 t.Fatalf("new leader expected %d, got %d", target, lead) 94 } 95 } 96 97 // TestMaintenanceSnapshotError ensures that context cancel/timeout 98 // before snapshot reading returns corresponding context errors. 99 func TestMaintenanceSnapshotError(t *testing.T) { 100 defer testutil.AfterTest(t) 101 102 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) 103 defer clus.Terminate(t) 104 105 // reading snapshot with canceled context should error out 106 ctx, cancel := context.WithCancel(context.Background()) 107 rc1, err := clus.RandClient().Snapshot(ctx) 108 if err != nil { 109 t.Fatal(err) 110 } 111 defer rc1.Close() 112 113 cancel() 114 _, err = io.Copy(ioutil.Discard, rc1) 115 if err != context.Canceled { 116 t.Errorf("expected %v, got %v", context.Canceled, err) 117 } 118 119 // reading snapshot with deadline exceeded should error out 120 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 121 defer cancel() 122 rc2, err := clus.RandClient().Snapshot(ctx) 123 if err != nil { 124 t.Fatal(err) 125 } 126 defer rc2.Close() 127 128 time.Sleep(2 * time.Second) 129 130 _, err = io.Copy(ioutil.Discard, rc2) 131 if err != nil && err != context.DeadlineExceeded { 132 t.Errorf("expected %v, got %v", context.DeadlineExceeded, err) 133 } 134 } 135 136 // TestMaintenanceSnapshotErrorInflight ensures that inflight context cancel/timeout 137 // fails snapshot reading with corresponding context errors. 138 func TestMaintenanceSnapshotErrorInflight(t *testing.T) { 139 defer testutil.AfterTest(t) 140 141 clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) 142 defer clus.Terminate(t) 143 144 // take about 1-second to read snapshot 145 clus.Members[0].Stop(t) 146 dpath := filepath.Join(clus.Members[0].DataDir, "member", "snap", "db") 147 b := backend.NewDefaultBackend(dpath) 148 s := mvcc.NewStore(b, &lease.FakeLessor{}, nil) 149 rev := 100000 150 for i := 2; i <= rev; i++ { 151 s.Put([]byte(fmt.Sprintf("%10d", i)), bytes.Repeat([]byte("a"), 1024), lease.NoLease) 152 } 153 s.Close() 154 b.Close() 155 clus.Members[0].Restart(t) 156 157 // reading snapshot with canceled context should error out 158 ctx, cancel := context.WithCancel(context.Background()) 159 rc1, err := clus.RandClient().Snapshot(ctx) 160 if err != nil { 161 t.Fatal(err) 162 } 163 defer rc1.Close() 164 165 donec := make(chan struct{}) 166 go func() { 167 time.Sleep(300 * time.Millisecond) 168 cancel() 169 close(donec) 170 }() 171 _, err = io.Copy(ioutil.Discard, rc1) 172 if err != nil && err != context.Canceled { 173 t.Errorf("expected %v, got %v", context.Canceled, err) 174 } 175 <-donec 176 177 // reading snapshot with deadline exceeded should error out 178 ctx, cancel = context.WithTimeout(context.Background(), time.Second) 179 defer cancel() 180 rc2, err := clus.RandClient().Snapshot(ctx) 181 if err != nil { 182 t.Fatal(err) 183 } 184 defer rc2.Close() 185 186 // 300ms left and expect timeout while snapshot reading is in progress 187 time.Sleep(700 * time.Millisecond) 188 _, err = io.Copy(ioutil.Discard, rc2) 189 if err != nil && err != context.DeadlineExceeded { 190 t.Errorf("expected %v, got %v", context.DeadlineExceeded, err) 191 } 192 }