1// Copyright 2018 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 tester
16
17import (
18	"reflect"
19	"sort"
20	"testing"
21
22	"go.etcd.io/etcd/functional/rpcpb"
23
24	"go.uber.org/zap"
25)
26
27func Test_read(t *testing.T) {
28	exp := &Cluster{
29		Members: []*rpcpb.Member{
30			{
31				EtcdExec:           "./bin/etcd",
32				AgentAddr:          "127.0.0.1:19027",
33				FailpointHTTPAddr:  "http://127.0.0.1:7381",
34				BaseDir:            "/tmp/etcd-functional-1",
35				EtcdClientProxy:    false,
36				EtcdPeerProxy:      true,
37				EtcdClientEndpoint: "127.0.0.1:1379",
38				Etcd: &rpcpb.Etcd{
39					Name:                "s1",
40					DataDir:             "/tmp/etcd-functional-1/etcd.data",
41					WALDir:              "/tmp/etcd-functional-1/etcd.data/member/wal",
42					HeartbeatIntervalMs: 100,
43					ElectionTimeoutMs:   1000,
44					ListenClientURLs:    []string{"https://127.0.0.1:1379"},
45					AdvertiseClientURLs: []string{"https://127.0.0.1:1379"},
46					ClientAutoTLS:       true,
47					ClientCertAuth:      false,
48					ClientCertFile:      "",
49					ClientKeyFile:       "",
50					ClientTrustedCAFile: "",
51					ListenPeerURLs:      []string{"https://127.0.0.1:1380"},
52					AdvertisePeerURLs:   []string{"https://127.0.0.1:1381"},
53					PeerAutoTLS:         true,
54					PeerClientCertAuth:  false,
55					PeerCertFile:        "",
56					PeerKeyFile:         "",
57					PeerTrustedCAFile:   "",
58					InitialCluster:      "s1=https://127.0.0.1:1381,s2=https://127.0.0.1:2381,s3=https://127.0.0.1:3381",
59					InitialClusterState: "new",
60					InitialClusterToken: "tkn",
61					SnapshotCount:       10000,
62					QuotaBackendBytes:   10740000000,
63					PreVote:             true,
64					InitialCorruptCheck: true,
65					Logger:              "zap",
66					LogOutputs:          []string{"/tmp/etcd-functional-1/etcd.log"},
67				},
68				ClientCertData:      "",
69				ClientCertPath:      "",
70				ClientKeyData:       "",
71				ClientKeyPath:       "",
72				ClientTrustedCAData: "",
73				ClientTrustedCAPath: "",
74				PeerCertData:        "",
75				PeerCertPath:        "",
76				PeerKeyData:         "",
77				PeerKeyPath:         "",
78				PeerTrustedCAData:   "",
79				PeerTrustedCAPath:   "",
80				SnapshotPath:        "/tmp/etcd-functional-1.snapshot.db",
81			},
82			{
83				EtcdExec:           "./bin/etcd",
84				AgentAddr:          "127.0.0.1:29027",
85				FailpointHTTPAddr:  "http://127.0.0.1:7382",
86				BaseDir:            "/tmp/etcd-functional-2",
87				EtcdClientProxy:    false,
88				EtcdPeerProxy:      true,
89				EtcdClientEndpoint: "127.0.0.1:2379",
90				Etcd: &rpcpb.Etcd{
91					Name:                "s2",
92					DataDir:             "/tmp/etcd-functional-2/etcd.data",
93					WALDir:              "/tmp/etcd-functional-2/etcd.data/member/wal",
94					HeartbeatIntervalMs: 100,
95					ElectionTimeoutMs:   1000,
96					ListenClientURLs:    []string{"https://127.0.0.1:2379"},
97					AdvertiseClientURLs: []string{"https://127.0.0.1:2379"},
98					ClientAutoTLS:       true,
99					ClientCertAuth:      false,
100					ClientCertFile:      "",
101					ClientKeyFile:       "",
102					ClientTrustedCAFile: "",
103					ListenPeerURLs:      []string{"https://127.0.0.1:2380"},
104					AdvertisePeerURLs:   []string{"https://127.0.0.1:2381"},
105					PeerAutoTLS:         true,
106					PeerClientCertAuth:  false,
107					PeerCertFile:        "",
108					PeerKeyFile:         "",
109					PeerTrustedCAFile:   "",
110					InitialCluster:      "s1=https://127.0.0.1:1381,s2=https://127.0.0.1:2381,s3=https://127.0.0.1:3381",
111					InitialClusterState: "new",
112					InitialClusterToken: "tkn",
113					SnapshotCount:       10000,
114					QuotaBackendBytes:   10740000000,
115					PreVote:             true,
116					InitialCorruptCheck: true,
117					Logger:              "zap",
118					LogOutputs:          []string{"/tmp/etcd-functional-2/etcd.log"},
119				},
120				ClientCertData:      "",
121				ClientCertPath:      "",
122				ClientKeyData:       "",
123				ClientKeyPath:       "",
124				ClientTrustedCAData: "",
125				ClientTrustedCAPath: "",
126				PeerCertData:        "",
127				PeerCertPath:        "",
128				PeerKeyData:         "",
129				PeerKeyPath:         "",
130				PeerTrustedCAData:   "",
131				PeerTrustedCAPath:   "",
132				SnapshotPath:        "/tmp/etcd-functional-2.snapshot.db",
133			},
134			{
135				EtcdExec:           "./bin/etcd",
136				AgentAddr:          "127.0.0.1:39027",
137				FailpointHTTPAddr:  "http://127.0.0.1:7383",
138				BaseDir:            "/tmp/etcd-functional-3",
139				EtcdClientProxy:    false,
140				EtcdPeerProxy:      true,
141				EtcdClientEndpoint: "127.0.0.1:3379",
142				Etcd: &rpcpb.Etcd{
143					Name:                "s3",
144					DataDir:             "/tmp/etcd-functional-3/etcd.data",
145					WALDir:              "/tmp/etcd-functional-3/etcd.data/member/wal",
146					HeartbeatIntervalMs: 100,
147					ElectionTimeoutMs:   1000,
148					ListenClientURLs:    []string{"https://127.0.0.1:3379"},
149					AdvertiseClientURLs: []string{"https://127.0.0.1:3379"},
150					ClientAutoTLS:       true,
151					ClientCertAuth:      false,
152					ClientCertFile:      "",
153					ClientKeyFile:       "",
154					ClientTrustedCAFile: "",
155					ListenPeerURLs:      []string{"https://127.0.0.1:3380"},
156					AdvertisePeerURLs:   []string{"https://127.0.0.1:3381"},
157					PeerAutoTLS:         true,
158					PeerClientCertAuth:  false,
159					PeerCertFile:        "",
160					PeerKeyFile:         "",
161					PeerTrustedCAFile:   "",
162					InitialCluster:      "s1=https://127.0.0.1:1381,s2=https://127.0.0.1:2381,s3=https://127.0.0.1:3381",
163					InitialClusterState: "new",
164					InitialClusterToken: "tkn",
165					SnapshotCount:       10000,
166					QuotaBackendBytes:   10740000000,
167					PreVote:             true,
168					InitialCorruptCheck: true,
169					Logger:              "zap",
170					LogOutputs:          []string{"/tmp/etcd-functional-3/etcd.log"},
171				},
172				ClientCertData:      "",
173				ClientCertPath:      "",
174				ClientKeyData:       "",
175				ClientKeyPath:       "",
176				ClientTrustedCAData: "",
177				ClientTrustedCAPath: "",
178				PeerCertData:        "",
179				PeerCertPath:        "",
180				PeerKeyData:         "",
181				PeerKeyPath:         "",
182				PeerTrustedCAData:   "",
183				PeerTrustedCAPath:   "",
184				SnapshotPath:        "/tmp/etcd-functional-3.snapshot.db",
185			},
186		},
187		Tester: &rpcpb.Tester{
188			DataDir:               "/tmp/etcd-tester-data",
189			Network:               "tcp",
190			Addr:                  "127.0.0.1:9028",
191			DelayLatencyMs:        5000,
192			DelayLatencyMsRv:      500,
193			UpdatedDelayLatencyMs: 5000,
194			RoundLimit:            1,
195			ExitOnCaseFail:        true,
196			EnablePprof:           true,
197			CaseDelayMs:           7000,
198			CaseShuffle:           true,
199			Cases: []string{
200				"SIGTERM_ONE_FOLLOWER",
201				"SIGTERM_ONE_FOLLOWER_UNTIL_TRIGGER_SNAPSHOT",
202				"SIGTERM_LEADER",
203				"SIGTERM_LEADER_UNTIL_TRIGGER_SNAPSHOT",
204				"SIGTERM_QUORUM",
205				"SIGTERM_ALL",
206				"SIGQUIT_AND_REMOVE_ONE_FOLLOWER",
207				"SIGQUIT_AND_REMOVE_ONE_FOLLOWER_UNTIL_TRIGGER_SNAPSHOT",
208				// "SIGQUIT_AND_REMOVE_LEADER",
209				// "SIGQUIT_AND_REMOVE_LEADER_UNTIL_TRIGGER_SNAPSHOT",
210				// "SIGQUIT_AND_REMOVE_QUORUM_AND_RESTORE_LEADER_SNAPSHOT_FROM_SCRATCH",
211				// "BLACKHOLE_PEER_PORT_TX_RX_ONE_FOLLOWER",
212				// "BLACKHOLE_PEER_PORT_TX_RX_ONE_FOLLOWER_UNTIL_TRIGGER_SNAPSHOT",
213				"BLACKHOLE_PEER_PORT_TX_RX_LEADER",
214				"BLACKHOLE_PEER_PORT_TX_RX_LEADER_UNTIL_TRIGGER_SNAPSHOT",
215				"BLACKHOLE_PEER_PORT_TX_RX_QUORUM",
216				"BLACKHOLE_PEER_PORT_TX_RX_ALL",
217				// "DELAY_PEER_PORT_TX_RX_ONE_FOLLOWER",
218				// "RANDOM_DELAY_PEER_PORT_TX_RX_ONE_FOLLOWER",
219				// "DELAY_PEER_PORT_TX_RX_ONE_FOLLOWER_UNTIL_TRIGGER_SNAPSHOT",
220				// "RANDOM_DELAY_PEER_PORT_TX_RX_ONE_FOLLOWER_UNTIL_TRIGGER_SNAPSHOT",
221				"DELAY_PEER_PORT_TX_RX_LEADER",
222				"RANDOM_DELAY_PEER_PORT_TX_RX_LEADER",
223				"DELAY_PEER_PORT_TX_RX_LEADER_UNTIL_TRIGGER_SNAPSHOT",
224				"RANDOM_DELAY_PEER_PORT_TX_RX_LEADER_UNTIL_TRIGGER_SNAPSHOT",
225				"DELAY_PEER_PORT_TX_RX_QUORUM",
226				"RANDOM_DELAY_PEER_PORT_TX_RX_QUORUM",
227				"DELAY_PEER_PORT_TX_RX_ALL",
228				"RANDOM_DELAY_PEER_PORT_TX_RX_ALL",
229				"NO_FAIL_WITH_STRESS",
230				"NO_FAIL_WITH_NO_STRESS_FOR_LIVENESS",
231			},
232			FailpointCommands: []string{`panic("etcd-tester")`},
233			RunnerExecPath:    "./bin/etcd-runner",
234			ExternalExecPath:  "",
235			Stressers: []*rpcpb.Stresser{
236				{Type: "KV_WRITE_SMALL", Weight: 0.35},
237				{Type: "KV_WRITE_LARGE", Weight: 0.002},
238				{Type: "KV_READ_ONE_KEY", Weight: 0.07},
239				{Type: "KV_READ_RANGE", Weight: 0.07},
240				{Type: "KV_DELETE_ONE_KEY", Weight: 0.07},
241				{Type: "KV_DELETE_RANGE", Weight: 0.07},
242				{Type: "KV_TXN_WRITE_DELETE", Weight: 0.35},
243				{Type: "LEASE", Weight: 0.0},
244			},
245			Checkers:                []string{"KV_HASH", "LEASE_EXPIRE"},
246			StressKeySize:           100,
247			StressKeySizeLarge:      32769,
248			StressKeySuffixRange:    250000,
249			StressKeySuffixRangeTxn: 100,
250			StressKeyTxnOps:         10,
251			StressClients:           100,
252			StressQPS:               2000,
253		},
254	}
255
256	logger, err := zap.NewProduction()
257	if err != nil {
258		t.Fatal(err)
259	}
260	defer logger.Sync()
261
262	cfg, err := read(logger, "../../functional.yaml")
263	if err != nil {
264		t.Fatal(err)
265	}
266	cfg.lg = nil
267
268	if !reflect.DeepEqual(exp, cfg) {
269		t.Fatalf("expected %+v, got %+v", exp, cfg)
270	}
271
272	cfg.lg = logger
273
274	cfg.updateCases()
275	fs1 := cfg.listCases()
276
277	cfg.shuffleCases()
278	fs2 := cfg.listCases()
279	if reflect.DeepEqual(fs1, fs2) {
280		t.Fatalf("expected shuffled failure cases, got %q", fs2)
281	}
282
283	cfg.shuffleCases()
284	fs3 := cfg.listCases()
285	if reflect.DeepEqual(fs2, fs3) {
286		t.Fatalf("expected reshuffled failure cases from %q, got %q", fs2, fs3)
287	}
288
289	// shuffle ensures visit all exactly once
290	// so when sorted, failure cases must be equal
291	sort.Strings(fs1)
292	sort.Strings(fs2)
293	sort.Strings(fs3)
294
295	if !reflect.DeepEqual(fs1, fs2) {
296		t.Fatalf("expected %q, got %q", fs1, fs2)
297	}
298	if !reflect.DeepEqual(fs2, fs3) {
299		t.Fatalf("expected %q, got %q", fs2, fs3)
300	}
301}
302