1// Copyright 2019 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 rafttest 16 17import ( 18 "fmt" 19 "math" 20 "strings" 21 22 "go.etcd.io/etcd/raft" 23 pb "go.etcd.io/etcd/raft/raftpb" 24) 25 26// InteractionOpts groups the options for an InteractionEnv. 27type InteractionOpts struct { 28 OnConfig func(*raft.Config) 29} 30 31// A Node is a member of a raft group tested via an InteractionEnv. 32type Node struct { 33 *raft.RawNode 34 Storage 35 36 Config *raft.Config 37 History []pb.Snapshot 38} 39 40// InteractionEnv facilitates testing of complex interactions between the 41// members of a raft group. 42type InteractionEnv struct { 43 Options *InteractionOpts 44 Nodes []Node 45 Messages []pb.Message // in-flight messages 46 47 Output *RedirectLogger 48} 49 50// NewInteractionEnv initializes an InteractionEnv. opts may be nil. 51func NewInteractionEnv(opts *InteractionOpts) *InteractionEnv { 52 if opts == nil { 53 opts = &InteractionOpts{} 54 } 55 return &InteractionEnv{ 56 Options: opts, 57 Output: &RedirectLogger{ 58 Builder: &strings.Builder{}, 59 }, 60 } 61} 62 63// Storage is the interface used by InteractionEnv. It is comprised of raft's 64// Storage interface plus access to operations that maintain the log and drive 65// the Ready handling loop. 66type Storage interface { 67 raft.Storage 68 SetHardState(state pb.HardState) error 69 ApplySnapshot(pb.Snapshot) error 70 Compact(newFirstIndex uint64) error 71 Append([]pb.Entry) error 72} 73 74// defaultRaftConfig sets up a *raft.Config with reasonable testing defaults. 75// In particular, no limits are set. 76func defaultRaftConfig(id uint64, applied uint64, s raft.Storage) *raft.Config { 77 return &raft.Config{ 78 ID: id, 79 Applied: applied, 80 ElectionTick: 3, 81 HeartbeatTick: 1, 82 Storage: s, 83 MaxSizePerMsg: math.MaxUint64, 84 MaxInflightMsgs: math.MaxInt32, 85 } 86} 87 88func defaultEntryFormatter(b []byte) string { 89 return fmt.Sprintf("%q", b) 90} 91