forked from etcd-io/etcd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test
executable file
·559 lines (494 loc) · 16.3 KB
/
test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
#!/usr/bin/env bash
#
# Run all etcd tests
# ./test
# ./test -v
#
# Run tests for one package
#
# PKG=./wal ./test
# PKG=snap ./test
#
# Run code coverage
# COVERDIR must either be a absolute path or a relative path to the etcd root
# COVERDIR=coverage PASSES="build_cov cov" ./test
set -e
source ./build
# build before setting up test GOPATH
if [[ "${PASSES}" == *"functional"* ]]; then
./tools/functional-tester/build
fi
if [ -z "$PASSES" ]; then
PASSES="fmt bom dep compile build unit"
fi
USERPKG=${PKG:-}
# Invoke ./cover for HTML output
COVER=${COVER:-"-cover"}
# Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
IGNORE_PKGS="(vendor/|etcdserverpb|rafttest|gopath.proto|v3lockpb|v3electionpb)"
INTEGRATION_PKGS="(integration|e2e|contrib|functional-tester)"
# all github.com/coreos/etcd/whatever pkgs that are not auto-generated / tools
PKGS=$(find . -name \*.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | grep -vE "(tools/|contrib/|e2e|pb)" | sed "s|\.|${REPO_PATH}|g" | xargs echo)
# pkg1,pkg2,pkg3
PKGS_COMMA=${PKGS// /,}
TEST_PKGS=$(find . -name \*_test.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g")
FORMATTABLE=$(find . -name \*.go | while read -r a; do echo "$(dirname "$a")/*.go"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g")
TESTABLE_AND_FORMATTABLE=$(echo "$TEST_PKGS" | grep -vE "$INTEGRATION_PKGS")
# check if user provided PKG override
if [ -z "${USERPKG}" ]; then
TEST=$TESTABLE_AND_FORMATTABLE
FMT=$FORMATTABLE
else
# strip out leading dotslashes and trailing slashes from PKG=./foo/
TEST=${USERPKG/#./}
TEST=${TEST/#\//}
TEST=${TEST/%\//}
# only run gofmt on packages provided by user
FMT="$TEST"
fi
FMT=($FMT)
# prepend REPO_PATH to each local package
split=$TEST
TEST=""
for a in $split; do TEST="$TEST ${REPO_PATH}/${a}"; done
TEST=($TEST)
# TODO: 'client' pkg fails with gosimple from generated files
# TODO: 'rafttest' is failing with unused
STATIC_ANALYSIS_PATHS=$(find . -name \*.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | grep -v 'client')
STATIC_ANALYSIS_PATHS=($STATIC_ANALYSIS_PATHS)
if [ -z "$GOARCH" ]; then
GOARCH=$(go env GOARCH);
fi
# determine whether target supports race detection
if [ "$GOARCH" == "amd64" ]; then
RACE="--race"
fi
function unit_pass {
echo "Running unit tests..."
GO_TEST_FLAG=""
if [ "${VERBOSE}" == "1" ]; then
GO_TEST_FLAG="-v"
fi
if [ "${VERBOSE}" == "2" ]; then
GO_TEST_FLAG="-v"
export CLIENT_DEBUG=1
fi
# only -run=Test so examples can run in integration tests
go test ${GO_TEST_FLAG} -timeout 3m "${COVER}" ${RACE} -cpu 1,2,4 -run=Test "$@" "${TEST[@]}"
}
function integration_pass {
echo "Running integration tests..."
go test -timeout 15m -v -cpu 1,2,4 "$@" "${REPO_PATH}/integration"
integration_extra "$@"
}
function integration_extra {
go test -timeout 1m -v ${RACE} -cpu 1,2,4 "$@" "${REPO_PATH}/client/integration"
go test -timeout 20m -v ${RACE} -cpu 1,2,4 "$@" "${REPO_PATH}/clientv3/integration"
go test -timeout 1m -v -cpu 1,2,4 "$@" "${REPO_PATH}/contrib/raftexample"
go test -timeout 5m -v ${RACE} -tags v2v3 "$@" "${REPO_PATH}/internal/store"
go test -timeout 1m -v ${RACE} -cpu 1,2,4 -run=Example "$@" "${TEST[@]}"
}
function functional_pass {
# Clean up any data and logs from previous runs
rm -rf ./agent-*
for a in 1 2 3; do
mkdir -p ./agent-$a
./bin/etcd-agent -etcd-path ./bin/etcd -etcd-log-dir "./agent-$a" -port ":${a}9027" &
pid="$!"
agent_pids="${agent_pids} $pid"
done
for a in 1 2 3; do
echo "Waiting for 'etcd-agent' on ${a}9027..."
while ! nc -z localhost ${a}9027; do
sleep 1
done
done
echo "Starting 'etcd-tester'"
./bin/etcd-tester \
-agent-endpoints "127.0.0.1:19027,127.0.0.1:29027,127.0.0.1:39027" \
-client-ports 1379,2379,3379 \
-advertise-client-ports 13790,23790,33790 \
-peer-ports 1380,2380,3380 \
-advertise-peer-ports 13800,23800,33800 \
-limit 1 \
-schedule-cases "0 1 2 3 4 5 6 7 8 9" \
-stress-qps 1000 \
-stress-key-txn-count 100 \
-stress-key-txn-ops 10 \
-exit-on-failure && echo "'etcd-tester' succeeded"
ETCD_TESTER_EXIT_CODE=$?
echo "ETCD_TESTER_EXIT_CODE:" ${ETCD_TESTER_EXIT_CODE}
echo "Waiting for processes to exit"
agent_pids=($agent_pids)
kill -s TERM "${agent_pids[@]}"
for a in "${agent_pids[@]}"; do wait "$a" || true; done
if [[ "${ETCD_TESTER_EXIT_CODE}" -ne "0" ]]; then
echo "--- FAIL: exit code" ${ETCD_TESTER_EXIT_CODE}
exit ${ETCD_TESTER_EXIT_CODE}
fi
}
function cov_pass {
echo "Running code coverage..."
# install gocovmerge before running code coverage from github.com/wadey/gocovmerge
# gocovmerge merges coverage files
if ! which gocovmerge >/dev/null; then
echo "gocovmerge not installed"
exit 255
fi
if [ -z "$COVERDIR" ]; then
echo "COVERDIR undeclared"
exit 255
fi
if [ ! -f "bin/etcd_test" ]; then
echo "etcd_test binary not found"
exit 255
fi
mkdir -p "$COVERDIR"
# run code coverage for unit and integration tests
GOCOVFLAGS="-covermode=set -coverpkg ${PKGS_COMMA} -v -timeout 15m"
GOCOVFLAGS=($GOCOVFLAGS)
failed=""
for t in $(echo "${TEST_PKGS}" | grep -vE "(e2e|functional-tester)"); do
tf=$(echo "$t" | tr / _)
# cache package compilation data for faster repeated builds
go test "${GOCOVFLAGS[@]}" -i "${REPO_PATH}/$t" || true
# uses -run=Test to skip examples because clientv3/ example tests will leak goroutines
go test "${GOCOVFLAGS[@]}" -run=Test -coverprofile "$COVERDIR/${tf}.coverprofile" "${REPO_PATH}/$t" || failed="$failed $t"
done
# v2v3 tests
go test -tags v2v3 "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/store-v2v3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed store-v2v3"
# proxy tests
go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_integration.coverprofile" "${REPO_PATH}/integration" || failed="$failed proxy-integration"
go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_clientv3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed proxy-clientv3/integration"
# run code coverage for e2e tests
# use 30m timeout because e2e coverage takes longer
# due to many tests cause etcd process to wait
# on leadership transfer timeout during gracefully shutdown
echo Testing e2e without proxy...
go test -tags cov -timeout 30m -v "${REPO_PATH}/e2e" || failed="$failed e2e"
echo Testing e2e with proxy...
go test -tags "cov cluster_proxy" -timeout 30m -v "${REPO_PATH}/e2e" || failed="$failed e2e-proxy"
# incrementally merge to get coverage data even if some coverage files are corrupted
# optimistically assume etcdserver package's coverage file is OK since gocovmerge
# expects to start with a non-empty file
cp "$COVERDIR"/etcdserver.coverprofile "$COVERDIR"/cover.out
for f in "$COVERDIR"/*.coverprofile; do
echo "merging test coverage file ${f}"
gocovmerge "$f" "$COVERDIR"/cover.out >"$COVERDIR"/cover.tmp || failed="$failed $f"
if [ -s "$COVERDIR"/cover.tmp ]; then
mv "$COVERDIR"/cover.tmp "$COVERDIR"/cover.out
fi
done
# strip out generated files (using GNU-style sed)
sed --in-place '/generated.go/d' "$COVERDIR"/cover.out || true
# held failures to generate the full coverage file, now fail
if [ -n "$failed" ]; then
for f in $failed; do
echo "--- FAIL:" "$f"
done
exit 255
fi
}
function e2e_pass {
echo "Running e2e tests..."
go test -timeout 15m -v -cpu 1,2,4 "$@" "${REPO_PATH}/e2e"
}
function integration_e2e_pass {
echo "Running integration and e2e tests..."
go test -timeout 15m -v -cpu 1,2,4 "$@" "${REPO_PATH}/e2e" &
e2epid="$!"
go test -timeout 15m -v -cpu 1,2,4 "$@" "${REPO_PATH}/integration" &
intpid="$!"
wait $e2epid
wait $intpid
integration_extra "$@"
}
function grpcproxy_pass {
go test -timeout 20m -v ${RACE} -tags cluster_proxy -cpu 1,2,4 "$@" "${REPO_PATH}/integration"
go test -timeout 20m -v ${RACE} -tags cluster_proxy -cpu 1,2,4 "$@" "${REPO_PATH}/clientv3/integration"
go test -timeout 15m -v -tags cluster_proxy "$@" "${REPO_PATH}/e2e"
}
function release_pass {
rm -f ./bin/etcd-last-release
# to grab latest patch release; bump this up for every minor release
UPGRADE_VER=$(git tag -l --sort=-version:refname "v3.3.*" | head -1)
if [ -n "$MANUAL_VER" ]; then
# in case, we need to test against different version
UPGRADE_VER=$MANUAL_VER
fi
if [[ -z ${UPGRADE_VER} ]]; then
UPGRADE_VER="v3.3.0"
echo "fallback to" ${UPGRADE_VER}
fi
local file="etcd-$UPGRADE_VER-linux-$GOARCH.tar.gz"
echo "Downloading $file"
set +e
curl --fail -L "https://github.com/coreos/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
local result=$?
set -e
case $result in
0) ;;
*) echo "--- FAIL:" ${result}
exit $result
;;
esac
tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
mkdir -p ./bin
mv /tmp/etcd ./bin/etcd-last-release
}
function gofmt_pass {
fmtRes=$(gofmt -l -s -d "${FMT[@]}")
if [ -n "${fmtRes}" ]; then
echo -e "gofmt checking failed:\n${fmtRes}"
exit 255
fi
}
function govet_pass {
vetRes=$(go vet "${TEST[@]}")
if [ -n "${vetRes}" ]; then
echo -e "govet checking failed:\n${vetRes}"
exit 255
fi
}
function govet_shadow_pass {
fmtpkgs=$(for a in "${FMT[@]}"; do dirname "$a"; done | sort | uniq | grep -v "\\.")
fmtpkgs=($fmtpkgs)
vetRes=$(go tool vet -all -shadow "${fmtpkgs[@]}" 2>&1 | grep -v '/gw/' || true)
if [ -n "${vetRes}" ]; then
echo -e "govet -all -shadow checking failed:\n${vetRes}"
exit 255
fi
}
function shellcheck_pass {
if which shellcheck >/dev/null; then
shellcheckResult=$(shellcheck -fgcc build test scripts/* 2>&1 || true)
if [ -n "${shellcheckResult}" ]; then
echo -e "shellcheck checking failed:\n${shellcheckResult}"
exit 255
fi
fi
}
function markdown_you_pass {
# eschew you
yous=$(find . -name \*.md -exec grep -E --color "[Yy]ou[r]?[ '.,;]" {} + | grep -v /v2/ || true)
if [ ! -z "$yous" ]; then
echo -e "found 'you' in documentation:\n${yous}"
exit 255
fi
}
function markdown_marker_pass {
# TODO: check other markdown files when marker handles headers with '[]'
if which marker >/dev/null; then
markerResult=$(marker --skip-http --root ./Documentation 2>&1 || true)
if [ -n "${markerResult}" ]; then
echo -e "marker checking failed:\n${markerResult}"
exit 255
fi
else
echo "Skipping marker..."
fi
}
function goword_pass {
if which goword >/dev/null; then
# get all go files to process
gofiles=$(find "${FMT[@]}" -iname '*.go' 2>/dev/null)
gofiles_all=($gofiles)
# ignore tests and protobuf files
gofiles=$(echo "${gofiles_all[@]}" | sort | uniq | sed "s/ /\n/g" | grep -vE "(\\_test.go|\\.pb\\.go)")
gofiles=($gofiles)
# only check for broken exported godocs
gowordRes=$(goword -use-spell=false "${gofiles[@]}" | grep godoc-export | sort)
if [ ! -z "$gowordRes" ]; then
echo -e "goword checking failed:\n${gowordRes}"
exit 255
fi
# check some spelling
gowordRes=$(goword -ignore-file=.words clientv3/{*,*/*}.go 2>&1 | grep spell | sort)
if [ ! -z "$gowordRes" ]; then
echo -e "goword checking failed:\n${gowordRes}"
exit 255
fi
else
echo "Skipping goword..."
fi
}
function gosimple_pass {
if which gosimple >/dev/null; then
gosimpleResult=$(gosimple "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
if [ -n "${gosimpleResult}" ]; then
echo -e "gosimple checking failed:\n${gosimpleResult}"
exit 255
fi
else
echo "Skipping gosimple..."
fi
}
function unused_pass {
if which unused >/dev/null; then
unusedResult=$(unused "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
if [ -n "${unusedResult}" ]; then
echo -e "unused checking failed:\n${unusedResult}"
exit 255
fi
else
echo "Skipping unused..."
fi
}
function staticcheck_pass {
if which staticcheck >/dev/null; then
staticcheckResult=$(staticcheck "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
if [ -n "${staticcheckResult}" ]; then
# TODO: resolve these after go1.8 migration
# See https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck
STATIC_CHECK_MASK="SA(1012|1019|2002)"
if echo "${staticcheckResult}" | grep -vE "$STATIC_CHECK_MASK"; then
echo -e "staticcheck checking failed:\n${staticcheckResult}"
exit 255
else
suppressed=$(echo "${staticcheckResult}" | sed 's/ /\n/g' | grep "(SA" | sort | uniq -c)
echo -e "staticcheck suppressed warnings:\n${suppressed}"
fi
fi
else
echo "Skipping staticcheck..."
fi
}
function ineffassign_pass {
if which ineffassign >/dev/null; then
ineffassignResult=$(ineffassign "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
if [ -n "${ineffassignResult}" ]; then
echo -e "ineffassign checking failed:\n${ineffassignResult}"
exit 255
fi
else
echo "Skipping ineffassign..."
fi
}
function nakedret_pass {
if which nakedret >/dev/null; then
nakedretResult=$(nakedret "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
if [ -n "${nakedretResult}" ]; then
echo -e "nakedret checking failed:\n${nakedretResult}"
exit 255
fi
else
echo "Skipping nakedret..."
fi
}
function license_header_pass {
licRes=""
files=$(find . -type f -iname '*.go' ! -path './vendor/*' ! -path './gopath.proto/*')
for file in $files; do
if ! head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" ; then
licRes="${licRes}"$(echo -e " ${file}")
fi
done
if [ -n "${licRes}" ]; then
echo -e "license header checking failed:\n${licRes}"
exit 255
fi
}
function receiver_name_pass {
recvs=$(grep 'func ([^*]' {*,*/*,*/*/*}.go | grep -Ev "(generated|pb/)" | tr ':' ' ' | \
awk ' { print $2" "$3" "$4" "$1 }' | sed "s/[a-zA-Z\.]*go//g" | sort | uniq | \
grep -Ev "(Descriptor|Proto|_)" | awk ' { print $3" "$4 } ' | sort | uniq -c | grep -v ' 1 ' | awk ' { print $2 } ')
if [ -n "${recvs}" ]; then
recvs=($recvs)
for recv in "${recvs[@]}"; do
echo "Mismatched receiver for $recv..."
grep "$recv" "${FMT[@]}" | grep 'func ('
done
exit 255
fi
}
function commit_title_pass {
git log --oneline "$(git merge-base HEAD master)"...HEAD | while read -r l; do
commitMsg=$(echo "$l" | cut -f2- -d' ')
if [[ "$commitMsg" == Merge* ]]; then
# ignore "Merge pull" commits
continue
fi
if [[ "$commitMsg" == Revert* ]]; then
# ignore revert commits
continue
fi
pkgPrefix=$(echo "$commitMsg" | cut -f1 -d':')
spaceCommas=$(echo "$commitMsg" | sed 's/ /\n/g' | grep -c ',$' || echo 0)
commaSpaces=$(echo "$commitMsg" | sed 's/,/\n/g' | grep -c '^ ' || echo 0)
if [[ $(echo "$commitMsg" | grep -c ":..*") == 0 || "$commitMsg" == "$pkgPrefix" || "$spaceCommas" != "$commaSpaces" ]]; then
echo "$l"...
echo "Expected commit title format '<package>{\", \"<package>}: <description>'"
echo "Got: $l"
exit 255
fi
done
}
function fmt_pass {
toggle_failpoints disable
for p in gofmt \
govet \
govet_shadow \
shellcheck \
markdown_you \
markdown_marker \
goword \
gosimple \
unused \
staticcheck \
ineffassign \
nakedret \
license_header \
receiver_name \
commit_title \
; do
echo "Starting '$p' pass at $(date)"
"${p}"_pass "$@"
echo "Finished '$p' pass at $(date)"
done
}
function bom_pass {
if ! which license-bill-of-materials >/dev/null; then
return
fi
echo "Checking bill of materials..."
license-bill-of-materials \
--override-file bill-of-materials.override.json \
github.com/coreos/etcd github.com/coreos/etcd/etcdctl >bom-now.json || true
if ! diff bill-of-materials.json bom-now.json; then
echo "vendored licenses do not match given bill of materials"
exit 255
fi
rm bom-now.json
}
function dep_pass {
echo "Checking package dependencies..."
# don't pull in etcdserver package
pushd clientv3 >/dev/null
badpkg="(etcdserver$|mvcc$|backend$|grpc-gateway)"
deps=$(go list -f '{{ .Deps }}' | sed 's/ /\n/g' | grep -E "${badpkg}" || echo "")
popd >/dev/null
if [ ! -z "$deps" ]; then
echo -e "clientv3 has masked dependencies:\n${deps}"
exit 255
fi
}
function build_cov_pass {
out="bin"
if [ -n "${BINDIR}" ]; then out="${BINDIR}"; fi
go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcd_test"
go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcdctl_test" "${REPO_PATH}/etcdctl"
}
function compile_pass {
echo "Checking build..."
go build -v ./tools/...
}
# fail fast on static tests
function build_pass {
GO_BUILD_FLAGS="-a -v" etcd_build
}
for pass in $PASSES; do
echo "Starting '$pass' pass at $(date)"
"${pass}"_pass "$@"
echo "Finished '$pass' pass at $(date)"
done
echo "Success"