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