go.etcd.io/etcd@v3.3.27+incompatible/etcdserver/raft_test.go (about)

     1  // Copyright 2015 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 etcdserver
    16  
    17  import (
    18  	"encoding/json"
    19  	"reflect"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/coreos/etcd/etcdserver/membership"
    24  	"github.com/coreos/etcd/pkg/mock/mockstorage"
    25  	"github.com/coreos/etcd/pkg/pbutil"
    26  	"github.com/coreos/etcd/pkg/types"
    27  	"github.com/coreos/etcd/raft"
    28  	"github.com/coreos/etcd/raft/raftpb"
    29  	"github.com/coreos/etcd/rafthttp"
    30  )
    31  
    32  func TestGetIDs(t *testing.T) {
    33  	addcc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 2}
    34  	addEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc)}
    35  	removecc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
    36  	removeEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc)}
    37  	normalEntry := raftpb.Entry{Type: raftpb.EntryNormal}
    38  	updatecc := &raftpb.ConfChange{Type: raftpb.ConfChangeUpdateNode, NodeID: 2}
    39  	updateEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(updatecc)}
    40  
    41  	tests := []struct {
    42  		confState *raftpb.ConfState
    43  		ents      []raftpb.Entry
    44  
    45  		widSet []uint64
    46  	}{
    47  		{nil, []raftpb.Entry{}, []uint64{}},
    48  		{&raftpb.ConfState{Nodes: []uint64{1}},
    49  			[]raftpb.Entry{}, []uint64{1}},
    50  		{&raftpb.ConfState{Nodes: []uint64{1}},
    51  			[]raftpb.Entry{addEntry}, []uint64{1, 2}},
    52  		{&raftpb.ConfState{Nodes: []uint64{1}},
    53  			[]raftpb.Entry{addEntry, removeEntry}, []uint64{1}},
    54  		{&raftpb.ConfState{Nodes: []uint64{1}},
    55  			[]raftpb.Entry{addEntry, normalEntry}, []uint64{1, 2}},
    56  		{&raftpb.ConfState{Nodes: []uint64{1}},
    57  			[]raftpb.Entry{addEntry, normalEntry, updateEntry}, []uint64{1, 2}},
    58  		{&raftpb.ConfState{Nodes: []uint64{1}},
    59  			[]raftpb.Entry{addEntry, removeEntry, normalEntry}, []uint64{1}},
    60  	}
    61  
    62  	for i, tt := range tests {
    63  		var snap raftpb.Snapshot
    64  		if tt.confState != nil {
    65  			snap.Metadata.ConfState = *tt.confState
    66  		}
    67  		idSet := getIDs(&snap, tt.ents)
    68  		if !reflect.DeepEqual(idSet, tt.widSet) {
    69  			t.Errorf("#%d: idset = %#v, want %#v", i, idSet, tt.widSet)
    70  		}
    71  	}
    72  }
    73  
    74  func TestCreateConfigChangeEnts(t *testing.T) {
    75  	m := membership.Member{
    76  		ID:             types.ID(1),
    77  		RaftAttributes: membership.RaftAttributes{PeerURLs: []string{"http://localhost:2380"}},
    78  	}
    79  	ctx, err := json.Marshal(m)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	addcc1 := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1, Context: ctx}
    84  	removecc2 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
    85  	removecc3 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 3}
    86  	tests := []struct {
    87  		ids         []uint64
    88  		self        uint64
    89  		term, index uint64
    90  
    91  		wents []raftpb.Entry
    92  	}{
    93  		{
    94  			[]uint64{1},
    95  			1,
    96  			1, 1,
    97  
    98  			[]raftpb.Entry{},
    99  		},
   100  		{
   101  			[]uint64{1, 2},
   102  			1,
   103  			1, 1,
   104  
   105  			[]raftpb.Entry{{Term: 1, Index: 2, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
   106  		},
   107  		{
   108  			[]uint64{1, 2},
   109  			1,
   110  			2, 2,
   111  
   112  			[]raftpb.Entry{{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
   113  		},
   114  		{
   115  			[]uint64{1, 2, 3},
   116  			1,
   117  			2, 2,
   118  
   119  			[]raftpb.Entry{
   120  				{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
   121  				{Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
   122  			},
   123  		},
   124  		{
   125  			[]uint64{2, 3},
   126  			2,
   127  			2, 2,
   128  
   129  			[]raftpb.Entry{
   130  				{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
   131  			},
   132  		},
   133  		{
   134  			[]uint64{2, 3},
   135  			1,
   136  			2, 2,
   137  
   138  			[]raftpb.Entry{
   139  				{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
   140  				{Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
   141  				{Term: 2, Index: 5, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc1)},
   142  			},
   143  		},
   144  	}
   145  
   146  	for i, tt := range tests {
   147  		gents := createConfigChangeEnts(tt.ids, tt.self, tt.term, tt.index)
   148  		if !reflect.DeepEqual(gents, tt.wents) {
   149  			t.Errorf("#%d: ents = %v, want %v", i, gents, tt.wents)
   150  		}
   151  	}
   152  }
   153  
   154  func TestStopRaftWhenWaitingForApplyDone(t *testing.T) {
   155  	n := newNopReadyNode()
   156  	r := newRaftNode(raftNodeConfig{
   157  		Node:        n,
   158  		storage:     mockstorage.NewStorageRecorder(""),
   159  		raftStorage: raft.NewMemoryStorage(),
   160  		transport:   rafthttp.NewNopTransporter(),
   161  	})
   162  	srv := &EtcdServer{r: *r}
   163  	srv.r.start(nil)
   164  	n.readyc <- raft.Ready{}
   165  	select {
   166  	case <-srv.r.applyc:
   167  	case <-time.After(time.Second):
   168  		t.Fatalf("failed to receive apply struct")
   169  	}
   170  
   171  	srv.r.stopped <- struct{}{}
   172  	select {
   173  	case <-srv.r.done:
   174  	case <-time.After(time.Second):
   175  		t.Fatalf("failed to stop raft loop")
   176  	}
   177  }
   178  
   179  // TestConfgChangeBlocksApply ensures apply blocks if committed entries contain config-change.
   180  func TestConfgChangeBlocksApply(t *testing.T) {
   181  	n := newNopReadyNode()
   182  
   183  	r := newRaftNode(raftNodeConfig{
   184  		Node:        n,
   185  		storage:     mockstorage.NewStorageRecorder(""),
   186  		raftStorage: raft.NewMemoryStorage(),
   187  		transport:   rafthttp.NewNopTransporter(),
   188  	})
   189  	srv := &EtcdServer{r: *r}
   190  
   191  	srv.r.start(&raftReadyHandler{updateLeadership: func(bool) {}})
   192  	defer srv.r.Stop()
   193  
   194  	n.readyc <- raft.Ready{
   195  		SoftState:        &raft.SoftState{RaftState: raft.StateFollower},
   196  		CommittedEntries: []raftpb.Entry{{Type: raftpb.EntryConfChange}},
   197  	}
   198  	ap := <-srv.r.applyc
   199  
   200  	continueC := make(chan struct{})
   201  	go func() {
   202  		n.readyc <- raft.Ready{}
   203  		<-srv.r.applyc
   204  		close(continueC)
   205  	}()
   206  
   207  	select {
   208  	case <-continueC:
   209  		t.Fatalf("unexpected execution: raft routine should block waiting for apply")
   210  	case <-time.After(time.Second):
   211  	}
   212  
   213  	// finish apply, unblock raft routine
   214  	<-ap.notifyc
   215  
   216  	select {
   217  	case <-continueC:
   218  	case <-time.After(time.Second):
   219  		t.Fatalf("unexpected blocking on execution")
   220  	}
   221  }