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