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  }