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 }