14807: Fix go vet warnings about %s args.
[arvados.git] / build / run-tests.sh
1 #!/bin/bash
2 # Copyright (C) The Arvados Authors. All rights reserved.
3 #
4 # SPDX-License-Identifier: AGPL-3.0
5
6 . `dirname "$(readlink -f "$0")"`/libcloud-pin.sh
7
8 COLUMNS=80
9 . `dirname "$(readlink -f "$0")"`/run-library.sh
10
11 read -rd "\000" helpmessage <<EOF
12 $(basename $0): Install and test Arvados components.
13
14 Exit non-zero if any tests fail.
15
16 Syntax:
17         $(basename $0) WORKSPACE=/path/to/arvados [options]
18
19 Options:
20
21 --skip FOO     Do not test the FOO component.
22 --skip sanity  Skip initial dev environment sanity checks.
23 --skip install Do not run any install steps. Just run tests.
24                You should provide GOPATH, GEMHOME, and VENVDIR options
25                from a previous invocation if you use this option.
26 --only FOO     Do not test anything except the FOO component.
27 --temp DIR     Install components and dependencies under DIR instead of
28                making a new temporary directory. Implies --leave-temp.
29 --leave-temp   Do not remove GOPATH, virtualenv, and other temp dirs at exit.
30                Instead, show the path to give as --temp to reuse them in
31                subsequent invocations.
32 --repeat N     Repeat each install/test step until it succeeds N times.
33 --retry        Prompt to retry if an install or test suite fails.
34 --only-install Run specific install step
35 --short        Skip (or scale down) some slow tests.
36 --interactive  Set up, then prompt for test/install steps to perform.
37 WORKSPACE=path Arvados source tree to test.
38 CONFIGSRC=path Dir with api server config files to copy into source tree.
39                (If none given, leave config files alone in source tree.)
40 services/api_test="TEST=test/functional/arvados/v1/collections_controller_test.rb"
41                Restrict apiserver tests to the given file
42 sdk/python_test="--test-suite tests.test_keep_locator"
43                Restrict Python SDK tests to the given class
44 apps/workbench_test="TEST=test/integration/pipeline_instances_test.rb"
45                Restrict Workbench tests to the given file
46 services/arv-git-httpd_test="-check.vv"
47                Show all log messages, even when tests pass (also works
48                with services/keepstore_test etc.)
49 ARVADOS_DEBUG=1
50                Print more debug messages
51 envvar=value   Set \$envvar to value. Primarily useful for WORKSPACE,
52                *_test, and other examples shown above.
53
54 Assuming "--skip install" is not given, all components are installed
55 into \$GOPATH, \$VENDIR, and \$GEMHOME before running any tests. Many
56 test suites depend on other components being installed, and installing
57 everything tends to be quicker than debugging dependencies.
58
59 As a special concession to the current CI server config, CONFIGSRC
60 defaults to $HOME/arvados-api-server if that directory exists.
61
62 More information and background:
63
64 https://arvados.org/projects/arvados/wiki/Running_tests
65
66 Available tests:
67
68 apps/workbench (*)
69 apps/workbench_units (*)
70 apps/workbench_functionals (*)
71 apps/workbench_integration (*)
72 apps/workbench_benchmark
73 apps/workbench_profile
74 cmd/arvados-client
75 cmd/arvados-server
76 doc
77 lib/cli
78 lib/cmd
79 lib/controller
80 lib/crunchstat
81 lib/cloud
82 lib/cloud/azure
83 lib/dispatchcloud
84 lib/dispatchcloud/container
85 lib/dispatchcloud/scheduler
86 lib/dispatchcloud/ssh_executor
87 lib/dispatchcloud/worker
88 services/api
89 services/arv-git-httpd
90 services/crunchstat
91 services/dockercleaner
92 services/fuse
93 services/health
94 services/keep-web
95 services/keepproxy
96 services/keepstore
97 services/keep-balance
98 services/login-sync
99 services/nodemanager
100 services/nodemanager_integration
101 services/crunch-run
102 services/crunch-dispatch-local
103 services/crunch-dispatch-slurm
104 services/ws
105 sdk/cli
106 sdk/pam
107 sdk/python
108 sdk/python:py3
109 sdk/ruby
110 sdk/go/arvados
111 sdk/go/arvadosclient
112 sdk/go/auth
113 sdk/go/dispatch
114 sdk/go/keepclient
115 sdk/go/health
116 sdk/go/httpserver
117 sdk/go/manifest
118 sdk/go/blockdigest
119 sdk/go/asyncbuf
120 sdk/go/stats
121 sdk/go/crunchrunner
122 sdk/cwl
123 sdk/R
124 tools/sync-groups
125 tools/crunchstat-summary
126 tools/crunchstat-summary:py3
127 tools/keep-exercise
128 tools/keep-rsync
129 tools/keep-block-check
130
131 (*) apps/workbench is shorthand for apps/workbench_units +
132     apps/workbench_functionals + apps/workbench_integration
133
134 EOF
135
136 # First make sure to remove any ARVADOS_ variables from the calling
137 # environment that could interfere with the tests.
138 unset $(env | cut -d= -f1 | grep \^ARVADOS_)
139
140 # Reset other variables that could affect our [tests'] behavior by
141 # accident.
142 GITDIR=
143 GOPATH=
144 VENVDIR=
145 VENV3DIR=
146 PYTHONPATH=
147 GEMHOME=
148 PERLINSTALLBASE=
149 R_LIBS=
150
151 short=
152 only_install=
153 temp=
154 temp_preserve=
155
156 clear_temp() {
157     if [[ -z "$temp" ]]; then
158         # we didn't even get as far as making a temp dir
159         :
160     elif [[ -z "$temp_preserve" ]]; then
161         rm -rf "$temp"
162     else
163         echo "Leaving behind temp dirs in $temp"
164     fi
165 }
166
167 fatal() {
168     clear_temp
169     echo >&2 "Fatal: $* (encountered in ${FUNCNAME[1]} at ${BASH_SOURCE[1]} line ${BASH_LINENO[0]})"
170     exit 1
171 }
172
173 exit_cleanly() {
174     trap - INT
175     if which create-plot-data-from-log.sh >/dev/null; then
176         create-plot-data-from-log.sh $BUILD_NUMBER "$WORKSPACE/apps/workbench/log/test.log" "$WORKSPACE/apps/workbench/log/"
177     fi
178     rotate_logfile "$WORKSPACE/apps/workbench/log/" "test.log"
179     stop_services
180     rotate_logfile "$WORKSPACE/services/api/log/" "test.log"
181     report_outcomes
182     clear_temp
183     exit ${#failures}
184 }
185
186 sanity_checks() {
187     [[ -n "${skip[sanity]}" ]] && return 0
188     ( [[ -n "$WORKSPACE" ]] && [[ -d "$WORKSPACE/services" ]] ) \
189         || fatal "WORKSPACE environment variable not set to a source directory (see: $0 --help)"
190     echo Checking dependencies:
191     echo -n 'virtualenv: '
192     virtualenv --version \
193         || fatal "No virtualenv. Try: apt-get install virtualenv (on ubuntu: python-virtualenv)"
194     echo -n 'ruby: '
195     ruby -v \
196         || fatal "No ruby. Install >=2.1.9 (using rbenv, rvm, or source)"
197     echo -n 'go: '
198     go version \
199         || fatal "No go binary. See http://golang.org/doc/install"
200     [[ $(go version) =~ go1.([0-9]+) ]] && [[ ${BASH_REMATCH[1]} -ge 10 ]] \
201         || fatal "Go >= 1.10 required. See http://golang.org/doc/install"
202     echo -n 'gcc: '
203     gcc --version | egrep ^gcc \
204         || fatal "No gcc. Try: apt-get install build-essential"
205     echo -n 'fuse.h: '
206     find /usr/include -path '*fuse/fuse.h' | egrep --max-count=1 . \
207         || fatal "No fuse/fuse.h. Try: apt-get install libfuse-dev"
208     echo -n 'gnutls.h: '
209     find /usr/include -path '*gnutls/gnutls.h' | egrep --max-count=1 . \
210         || fatal "No gnutls/gnutls.h. Try: apt-get install libgnutls28-dev"
211     echo -n 'Python2 pyconfig.h: '
212     find /usr/include -path '*/python2*/pyconfig.h' | egrep --max-count=1 . \
213         || fatal "No Python2 pyconfig.h. Try: apt-get install python2.7-dev"
214     echo -n 'Python3 pyconfig.h: '
215     find /usr/include -path '*/python3*/pyconfig.h' | egrep --max-count=1 . \
216         || fatal "No Python3 pyconfig.h. Try: apt-get install python3-dev"
217     echo -n 'nginx: '
218     PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" nginx -v \
219         || fatal "No nginx. Try: apt-get install nginx"
220     echo -n 'perl: '
221     perl -v | grep version \
222         || fatal "No perl. Try: apt-get install perl"
223     for mod in ExtUtils::MakeMaker JSON LWP Net::SSL; do
224         echo -n "perl $mod: "
225         perl -e "use $mod; print \"\$$mod::VERSION\\n\"" \
226             || fatal "No $mod. Try: apt-get install perl-modules libcrypt-ssleay-perl libjson-perl libwww-perl"
227     done
228     echo -n 'gitolite: '
229     which gitolite \
230         || fatal "No gitolite. Try: apt-get install gitolite3"
231     echo -n 'npm: '
232     npm --version \
233         || fatal "No npm. Try: wget -O- https://nodejs.org/dist/v6.11.2/node-v6.11.2-linux-x64.tar.xz | sudo tar -C /usr/local -xJf - && sudo ln -s ../node-v6.11.2-linux-x64/bin/{node,npm} /usr/local/bin/"
234     echo -n 'cadaver: '
235     cadaver --version | grep -w cadaver \
236           || fatal "No cadaver. Try: apt-get install cadaver"
237     echo -n 'libattr1 xattr.h: '
238     find /usr/include -path '*/attr/xattr.h' | egrep --max-count=1 . \
239         || fatal "No libattr1 xattr.h. Try: apt-get install libattr1-dev"
240     echo -n 'libcurl curl.h: '
241     find /usr/include -path '*/curl/curl.h' | egrep --max-count=1 . \
242         || fatal "No libcurl curl.h. Try: apt-get install libcurl4-gnutls-dev"
243     echo -n 'libpq libpq-fe.h: '
244     find /usr/include -path '*/postgresql/libpq-fe.h' | egrep --max-count=1 . \
245         || fatal "No libpq libpq-fe.h. Try: apt-get install libpq-dev"
246     echo -n 'services/api/config/database.yml: '
247     if [[ ! -f "$WORKSPACE/services/api/config/database.yml" ]]; then
248             fatal "Please provide a database.yml file for the test suite"
249     else
250             echo "OK"
251     fi
252     echo -n 'postgresql: '
253     psql --version || fatal "No postgresql. Try: apt-get install postgresql postgresql-client-common"
254     echo -n 'phantomjs: '
255     phantomjs --version || fatal "No phantomjs. Try: apt-get install phantomjs"
256     echo -n 'xvfb: '
257     which Xvfb || fatal "No xvfb. Try: apt-get install xvfb"
258     echo -n 'graphviz: '
259     dot -V || fatal "No graphviz. Try: apt-get install graphviz"
260     echo -n 'geckodriver: '
261     geckodriver --version | grep ^geckodriver || echo "No geckodriver. Try: wget -O- https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz | sudo tar -C /usr/local/bin -xzf - geckodriver"
262
263     if [[ "$NEED_SDK_R" = true ]]; then
264       # R SDK stuff
265       echo -n 'R: '
266       which Rscript || fatal "No Rscript. Try: apt-get install r-base"
267       echo -n 'testthat: '
268       Rscript -e "library('testthat')" || fatal "No testthat. Try: apt-get install r-cran-testthat"
269       # needed for roxygen2, needed for devtools, needed for R sdk
270       pkg-config --exists libxml-2.0 || fatal "No libxml2. Try: apt-get install libxml2-dev"
271       # needed for pkgdown, builds R SDK doc pages
272       which pandoc || fatal "No pandoc. Try: apt-get install pandoc"
273     fi
274 }
275
276 rotate_logfile() {
277   # i.e.  rotate_logfile "$WORKSPACE/apps/workbench/log/" "test.log"
278   # $BUILD_NUMBER is set by Jenkins if this script is being called as part of a Jenkins run
279   if [[ -f "$1/$2" ]]; then
280     THEDATE=`date +%Y%m%d%H%M%S`
281     mv "$1/$2" "$1/$THEDATE-$BUILD_NUMBER-$2"
282     gzip "$1/$THEDATE-$BUILD_NUMBER-$2"
283   fi
284 }
285
286 declare -a failures
287 declare -A skip
288 declare -A only
289 declare -A testargs
290 skip[apps/workbench_profile]=1
291 # nodemanager_integration tests are not reliable, see #12061.
292 skip[services/nodemanager_integration]=1
293
294 while [[ -n "$1" ]]
295 do
296     arg="$1"; shift
297     case "$arg" in
298         --help)
299             echo >&2 "$helpmessage"
300             echo >&2
301             exit 1
302             ;;
303         --skip)
304             skip[$1]=1; shift
305             ;;
306         --only)
307             only[$1]=1; skip[$1]=""; shift
308             ;;
309         --short)
310             short=1
311             ;;
312         --interactive)
313             interactive=1
314             ;;
315         --skip-install)
316             skip[install]=1
317             ;;
318         --only-install)
319             only_install="$1"; shift
320             ;;
321         --temp)
322             temp="$1"; shift
323             temp_preserve=1
324             ;;
325         --leave-temp)
326             temp_preserve=1
327             ;;
328         --repeat)
329             repeat=$((${1}+0)); shift
330             ;;
331         --retry)
332             retry=1
333             ;;
334         *_test=*)
335             suite="${arg%%_test=*}"
336             args="${arg#*=}"
337             testargs["$suite"]="$args"
338             ;;
339         *=*)
340             eval export $(echo $arg | cut -d= -f1)=\"$(echo $arg | cut -d= -f2-)\"
341             ;;
342         *)
343             echo >&2 "$0: Unrecognized option: '$arg'. Try: $0 --help"
344             exit 1
345             ;;
346     esac
347 done
348
349 # R SDK installation is very slow (~360s in a clean environment) and only
350 # required when testing it. Skip that step if it is not needed.
351 NEED_SDK_R=true
352
353 if [[ ${#only[@]} -ne 0 ]] &&
354    [[ -z "${only['sdk/R']}" && -z "${only['doc']}" ]]; then
355   NEED_SDK_R=false
356 fi
357
358 if [[ ${skip["sdk/R"]} == 1 && ${skip["doc"]} == 1 ]]; then
359   NEED_SDK_R=false
360 fi
361
362 if [[ $NEED_SDK_R == false ]]; then
363         echo "R SDK not needed, it will not be installed."
364 fi
365
366 start_services() {
367     if [[ -n "$ARVADOS_TEST_API_HOST" ]]; then
368         return 0
369     fi
370     . "$VENVDIR/bin/activate"
371     echo 'Starting API, keepproxy, keep-web, ws, arv-git-httpd, and nginx ssl proxy...'
372     if [[ ! -d "$WORKSPACE/services/api/log" ]]; then
373         mkdir -p "$WORKSPACE/services/api/log"
374     fi
375     # Remove empty api.pid file if it exists
376     if [[ -f "$WORKSPACE/tmp/api.pid" && ! -s "$WORKSPACE/tmp/api.pid" ]]; then
377         rm -f "$WORKSPACE/tmp/api.pid"
378     fi
379     all_services_stopped=
380     fail=0
381     cd "$WORKSPACE" \
382         && eval $(python sdk/python/tests/run_test_server.py start --auth admin || echo "fail=1; false") \
383         && export ARVADOS_TEST_API_HOST="$ARVADOS_API_HOST" \
384         && export ARVADOS_TEST_API_INSTALLED="$$" \
385         && python sdk/python/tests/run_test_server.py start_controller \
386         && python sdk/python/tests/run_test_server.py start_keep_proxy \
387         && python sdk/python/tests/run_test_server.py start_keep-web \
388         && python sdk/python/tests/run_test_server.py start_arv-git-httpd \
389         && python sdk/python/tests/run_test_server.py start_ws \
390         && eval $(python sdk/python/tests/run_test_server.py start_nginx || echo "fail=1; false") \
391         && (env | egrep ^ARVADOS) \
392         || fail=1
393     deactivate
394     if [[ $fail != 0 ]]; then
395         unset ARVADOS_TEST_API_HOST
396     fi
397     return $fail
398 }
399
400 stop_services() {
401     if [[ -n "$all_services_stopped" ]]; then
402         return
403     fi
404     unset ARVADOS_TEST_API_HOST
405     . "$VENVDIR/bin/activate" || return
406     cd "$WORKSPACE" \
407         && python sdk/python/tests/run_test_server.py stop_nginx \
408         && python sdk/python/tests/run_test_server.py stop_arv-git-httpd \
409         && python sdk/python/tests/run_test_server.py stop_ws \
410         && python sdk/python/tests/run_test_server.py stop_keep-web \
411         && python sdk/python/tests/run_test_server.py stop_keep_proxy \
412         && python sdk/python/tests/run_test_server.py stop_controller \
413         && python sdk/python/tests/run_test_server.py stop \
414         && all_services_stopped=1
415     deactivate
416 }
417
418 interrupt() {
419     failures+=("($(basename $0) interrupted)")
420     exit_cleanly
421 }
422 trap interrupt INT
423
424 setup_ruby_environment() {
425     if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
426         source "$HOME/.rvm/scripts/rvm"
427         using_rvm=true
428     elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
429         source "/usr/local/rvm/scripts/rvm"
430         using_rvm=true
431     else
432         using_rvm=false
433     fi
434
435     if [[ "$using_rvm" == true ]]; then
436         # If rvm is in use, we can't just put separate "dependencies"
437         # and "gems-under-test" paths to GEM_PATH: passenger resets
438         # the environment to the "current gemset", which would lose
439         # our GEM_PATH and prevent our test suites from running ruby
440         # programs (for example, the Workbench test suite could not
441         # boot an API server or run arv). Instead, we have to make an
442         # rvm gemset and use it for everything.
443
444         [[ `type rvm | head -n1` == "rvm is a function" ]] \
445             || fatal 'rvm check'
446
447         # Put rvm's favorite path back in first place (overriding
448         # virtualenv, which just put itself there). Ignore rvm's
449         # complaint about not being in first place already.
450         rvm use @default 2>/dev/null
451
452         # Create (if needed) and switch to an @arvados-tests-* gemset,
453         # salting the gemset name so it doesn't interfere with
454         # concurrent builds in other workspaces. Leave the choice of
455         # ruby to the caller.
456         gemset="arvados-tests-$(echo -n "${WORKSPACE}" | md5sum | head -c16)"
457         rvm use "@${gemset}" --create \
458             || fatal 'rvm gemset setup'
459
460         rvm env
461     else
462         # When our "bundle install"s need to install new gems to
463         # satisfy dependencies, we want them to go where "gem install
464         # --user-install" would put them. (However, if the caller has
465         # already set GEM_HOME, we assume that's where dependencies
466         # should be installed, and we should leave it alone.)
467
468         if [ -z "$GEM_HOME" ]; then
469             user_gempath="$(gem env gempath)"
470             export GEM_HOME="${user_gempath%%:*}"
471         fi
472         PATH="$(gem env gemdir)/bin:$PATH"
473
474         # When we build and install our own gems, we install them in our
475         # $GEMHOME tmpdir, and we want them to be at the front of GEM_PATH and
476         # PATH so integration tests prefer them over other versions that
477         # happen to be installed in $user_gempath, system dirs, etc.
478
479         tmpdir_gem_home="$(env - PATH="$PATH" HOME="$GEMHOME" gem env gempath | cut -f1 -d:)"
480         PATH="$tmpdir_gem_home/bin:$PATH"
481         export GEM_PATH="$tmpdir_gem_home"
482
483         echo "Will install dependencies to $(gem env gemdir)"
484         echo "Will install arvados gems to $tmpdir_gem_home"
485         echo "Gem search path is GEM_PATH=$GEM_PATH"
486     fi
487     bundle config || gem install bundler \
488         || fatal 'install bundler'
489 }
490
491 with_test_gemset() {
492     if [[ "$using_rvm" == true ]]; then
493         "$@"
494     else
495         GEM_HOME="$tmpdir_gem_home" GEM_PATH="$tmpdir_gem_home" "$@"
496     fi
497 }
498
499 gem_uninstall_if_exists() {
500     if gem list "$1\$" | egrep '^\w'; then
501         gem uninstall --force --all --executables "$1"
502     fi
503 }
504
505 setup_virtualenv() {
506     local venvdest="$1"; shift
507     if ! [[ -e "$venvdest/bin/activate" ]] || ! [[ -e "$venvdest/bin/pip" ]]; then
508         virtualenv --setuptools "$@" "$venvdest" || fatal "virtualenv $venvdest failed"
509     elif [[ -n "$short" ]]; then
510         return
511     fi
512     if [[ $("$venvdest/bin/python" --version 2>&1) =~ \ 3\.[012]\. ]]; then
513         # pip 8.0.0 dropped support for python 3.2, e.g., debian wheezy
514         "$venvdest/bin/pip" install --no-cache-dir 'setuptools>=18.5' 'pip>=7,<8'
515     else
516         "$venvdest/bin/pip" install --no-cache-dir 'setuptools>=18.5' 'pip>=7'
517     fi
518     # ubuntu1404 can't seem to install mock via tests_require, but it can do this.
519     "$venvdest/bin/pip" install --no-cache-dir 'mock>=1.0' 'pbr<1.7.0'
520 }
521
522 initialize() {
523     sanity_checks
524
525     echo "WORKSPACE=$WORKSPACE"
526
527     if [[ -z "$CONFIGSRC" ]] && [[ -d "$HOME/arvados-api-server" ]]; then
528         # Jenkins expects us to use this by default.
529         CONFIGSRC="$HOME/arvados-api-server"
530     fi
531
532     # Clean up .pyc files that may exist in the workspace
533     cd "$WORKSPACE"
534     find -name '*.pyc' -delete
535
536     if [[ -z "$temp" ]]; then
537         temp="$(mktemp -d)"
538     fi
539
540     # Set up temporary install dirs (unless existing dirs were supplied)
541     for tmpdir in VENVDIR VENV3DIR GOPATH GEMHOME PERLINSTALLBASE R_LIBS
542     do
543         if [[ -z "${!tmpdir}" ]]; then
544             eval "$tmpdir"="$temp/$tmpdir"
545         fi
546         if ! [[ -d "${!tmpdir}" ]]; then
547             mkdir "${!tmpdir}" || fatal "can't create ${!tmpdir} (does $temp exist?)"
548         fi
549     done
550
551     rm -vf "${WORKSPACE}/tmp/*.log"
552
553     export PERLINSTALLBASE
554     export PERL5LIB="$PERLINSTALLBASE/lib/perl5${PERL5LIB:+:$PERL5LIB}"
555
556     export R_LIBS
557
558     export GOPATH
559
560     # Jenkins config requires that glob tmp/*.log match something. Ensure
561     # that happens even if we don't end up running services that set up
562     # logging.
563     mkdir -p "${WORKSPACE}/tmp/" || fatal "could not mkdir ${WORKSPACE}/tmp"
564     touch "${WORKSPACE}/tmp/controller.log" || fatal "could not touch ${WORKSPACE}/tmp/controller.log"
565
566     unset http_proxy https_proxy no_proxy
567
568
569     # Note: this must be the last time we change PATH, otherwise rvm will
570     # whine a lot.
571     setup_ruby_environment
572
573     echo "PATH is $PATH"
574 }
575
576 install_env() {
577     (
578         set -e
579         mkdir -p "$GOPATH/src/git.curoverse.com"
580         if [[ ! -h "$GOPATH/src/git.curoverse.com/arvados.git" ]]; then
581             for d in \
582                 "$GOPATH/src/git.curoverse.com/arvados.git/tmp/GOPATH" \
583                     "$GOPATH/src/git.curoverse.com/arvados.git/tmp" \
584                     "$GOPATH/src/git.curoverse.com/arvados.git"; do
585                 [[ -d "$d" ]] && rmdir "$d"
586             done
587         fi
588         for d in \
589             "$GOPATH/src/git.curoverse.com/arvados.git/arvados" \
590                 "$GOPATH/src/git.curoverse.com/arvados.git"; do
591             [[ -h "$d" ]] && rm "$d"
592         done
593         ln -vsfT "$WORKSPACE" "$GOPATH/src/git.curoverse.com/arvados.git"
594         go get -v github.com/kardianos/govendor
595         cd "$GOPATH/src/git.curoverse.com/arvados.git"
596         if [[ -n "$short" ]]; then
597             go get -v -d ...
598             "$GOPATH/bin/govendor" sync
599         else
600             # Remove cached source dirs in workdir. Otherwise, they will
601             # not qualify as +missing or +external below, and we won't be
602             # able to detect that they're missing from vendor/vendor.json.
603             rm -rf vendor/*/
604             go get -v -d ...
605             "$GOPATH/bin/govendor" sync
606             [[ -z $("$GOPATH/bin/govendor" list +unused +missing +external | tee /dev/stderr) ]] \
607                 || fatal "vendor/vendor.json has unused or missing dependencies -- try:
608
609 (export GOPATH=\"${GOPATH}\"; cd \$GOPATH/src/git.curoverse.com/arvados.git && \$GOPATH/bin/govendor add +missing +external && \$GOPATH/bin/govendor remove +unused)
610
611 ";
612         fi
613     ) || fatal "Go setup failed"
614
615     setup_virtualenv "$VENVDIR" --python python2.7
616     . "$VENVDIR/bin/activate"
617
618     # Needed for run_test_server.py which is used by certain (non-Python) tests.
619     pip install --no-cache-dir PyYAML \
620         || fatal "pip install PyYAML failed"
621
622     # Preinstall libcloud if using a fork; otherwise nodemanager "pip
623     # install" won't pick it up by default.
624     if [[ -n "$LIBCLOUD_PIN_SRC" ]]; then
625         pip freeze 2>/dev/null | egrep ^apache-libcloud==$LIBCLOUD_PIN \
626             || pip install --pre --ignore-installed --no-cache-dir "$LIBCLOUD_PIN_SRC" >/dev/null \
627             || fatal "pip install apache-libcloud failed"
628     fi
629
630     # Deactivate Python 2 virtualenv
631     deactivate
632
633     # If Python 3 is available, set up its virtualenv in $VENV3DIR.
634     # Otherwise, skip dependent tests.
635     PYTHON3=$(which python3)
636     if [[ ${?} = 0 ]]; then
637         setup_virtualenv "$VENV3DIR" --python python3
638     else
639         PYTHON3=
640         cat >&2 <<EOF
641
642 Warning: python3 could not be found. Python 3 tests will be skipped.
643
644 EOF
645     fi
646
647     if ! which bundler >/dev/null
648     then
649         gem install --user-install bundler || fatal 'Could not install bundler'
650     fi
651 }
652
653 retry() {
654     remain="${repeat}"
655     while :
656     do
657         if ${@}; then
658             if [[ "$remain" -gt 1 ]]; then
659                 remain=$((${remain}-1))
660                 title "(repeating ${remain} more times)"
661             else
662                 break
663             fi
664         elif [[ "$retry" == 1 ]]; then
665             read -p 'Try again? [Y/n] ' x
666             if [[ "$x" != "y" ]] && [[ "$x" != "" ]]
667             then
668                 break
669             fi
670         else
671             break
672         fi
673     done
674 }
675
676 do_test() {
677     case "${1}" in
678         apps/workbench_units | apps/workbench_functionals | apps/workbench_integration)
679             suite=apps/workbench
680             ;;
681         services/nodemanager | services/nodemanager_integration)
682             suite=services/nodemanager_suite
683             ;;
684         *)
685             suite="${1}"
686             ;;
687     esac
688     if [[ -n "${skip[$suite]}" || \
689               -n "${skip[$1]}" || \
690               (${#only[@]} -ne 0 && ${only[$suite]} -eq 0 && ${only[$1]} -eq 0) ]]; then
691         return 0
692     fi
693     case "${1}" in
694         services/api)
695             stop_services
696             ;;
697         doc | lib/cli | lib/cloud/azure | lib/cloud/ec2 | lib/cmd | lib/dispatchcloud/ssh_executor | lib/dispatchcloud/worker)
698             # don't care whether services are running
699             ;;
700         *)
701             if ! start_services; then
702                 title "test $1 -- failed to start services"
703                 return 1
704             fi
705             ;;
706     esac
707     retry do_test_once ${@}
708 }
709
710 do_test_once() {
711     unset result
712
713     title "test $1"
714     timer_reset
715
716     if which deactivate >/dev/null; then deactivate; fi
717     if ! . "$VENVDIR/bin/activate"
718     then
719         result=1
720     elif [[ "$2" == "go" ]]
721     then
722         covername="coverage-$(echo "$1" | sed -e 's/\//_/g')"
723         coverflags=("-covermode=count" "-coverprofile=$WORKSPACE/tmp/.$covername.tmp")
724         # We do "go get -t" here to catch compilation errors
725         # before trying "go test". Otherwise, coverage-reporting
726         # mode makes Go show the wrong line numbers when reporting
727         # compilation errors.
728         go get -ldflags "-X main.version=${ARVADOS_VERSION:-$(git log -n1 --format=%H)-dev}" -t "git.curoverse.com/arvados.git/$1" && \
729             cd "$GOPATH/src/git.curoverse.com/arvados.git/$1" && \
730             [[ -z "$(gofmt -e -d . | tee /dev/stderr)" ]] && \
731             if [[ -n "${testargs[$1]}" ]]
732         then
733             # "go test -check.vv giturl" doesn't work, but this
734             # does:
735             go test ${short:+-short} ${testargs[$1]}
736         else
737             # The above form gets verbose even when testargs is
738             # empty, so use this form in such cases:
739             go test ${short:+-short} ${coverflags[@]} "git.curoverse.com/arvados.git/$1"
740         fi
741         result=${result:-$?}
742         if [[ -f "$WORKSPACE/tmp/.$covername.tmp" ]]
743         then
744             go tool cover -html="$WORKSPACE/tmp/.$covername.tmp" -o "$WORKSPACE/tmp/$covername.html"
745             rm "$WORKSPACE/tmp/.$covername.tmp"
746         fi
747     elif [[ "$2" == "pip" ]]
748     then
749         tries=0
750         cd "$WORKSPACE/$1" && while :
751         do
752             tries=$((${tries}+1))
753             # $3 can name a path directory for us to use, including trailing
754             # slash; e.g., the bin/ subdirectory of a virtualenv.
755             "${3}python" setup.py ${short:+--short-tests-only} test ${testargs[$1]}
756             result=$?
757             if [[ ${tries} < 3 && ${result} == 137 ]]
758             then
759                 printf '\n*****\n%s tests killed -- retrying\n*****\n\n' "$1"
760                 continue
761             else
762                 break
763             fi
764         done
765     elif [[ "$2" != "" ]]
766     then
767         "test_$2"
768     else
769         "test_$1"
770     fi
771     result=${result:-$?}
772     checkexit $result "$1 tests"
773     title "test $1 -- `timer`"
774     return $result
775 }
776
777 do_install() {
778     if [[ -n "${skip[install]}" || ( -n "${only_install}" && "${only_install}" != "${1}" && "${only_install}" != "${2}" ) ]]; then
779         return 0
780     fi
781     retry do_install_once ${@}
782 }
783
784 do_install_once() {
785     title "install $1"
786     timer_reset
787
788     if which deactivate >/dev/null; then deactivate; fi
789     if [[ "$1" != "env" ]] && ! . "$VENVDIR/bin/activate"; then
790         result=1
791     elif [[ "$2" == "go" ]]
792     then
793         go get -ldflags "-X main.version=${ARVADOS_VERSION:-$(git log -n1 --format=%H)-dev}" -t "git.curoverse.com/arvados.git/$1"
794     elif [[ "$2" == "pip" ]]
795     then
796         # $3 can name a path directory for us to use, including trailing
797         # slash; e.g., the bin/ subdirectory of a virtualenv.
798
799         # Need to change to a different directory after creating
800         # the source dist package to avoid a pip bug.
801         # see https://arvados.org/issues/5766 for details.
802
803         # Also need to install twice, because if it believes the package is
804         # already installed, pip it won't install it.  So the first "pip
805         # install" ensures that the dependencies are met, the second "pip
806         # install" ensures that we've actually installed the local package
807         # we just built.
808         cd "$WORKSPACE/$1" \
809             && "${3}python" setup.py sdist rotate --keep=1 --match .tar.gz \
810             && cd "$WORKSPACE" \
811             && "${3}pip" install --no-cache-dir --quiet "$WORKSPACE/$1/dist"/*.tar.gz \
812             && "${3}pip" install --no-cache-dir --quiet --no-deps --ignore-installed "$WORKSPACE/$1/dist"/*.tar.gz
813     elif [[ "$2" != "" ]]
814     then
815         "install_$2"
816     else
817         "install_$1"
818     fi
819     result=${result:-$?}
820     checkexit $result "$1 install"
821     title "install $1 -- `timer`"
822     return $result
823 }
824
825 bundle_install_trylocal() {
826     (
827         set -e
828         echo "(Running bundle install --local. 'could not find package' messages are OK.)"
829         if ! bundle install --local --no-deployment; then
830             echo "(Running bundle install again, without --local.)"
831             bundle install --no-deployment
832         fi
833         bundle package --all
834     )
835 }
836
837 install_doc() {
838     cd "$WORKSPACE/doc" \
839         && bundle_install_trylocal \
840         && rm -rf .site
841 }
842
843 install_gem() {
844     gemname=$1
845     srcpath=$2
846     with_test_gemset gem_uninstall_if_exists "$gemname" \
847         && cd "$WORKSPACE/$srcpath" \
848         && bundle_install_trylocal \
849         && gem build "$gemname.gemspec" \
850         && with_test_gemset gem install --no-ri --no-rdoc $(ls -t "$gemname"-*.gem|head -n1)
851 }
852
853 install_sdk/ruby() {
854     install_gem arvados sdk/ruby
855 }
856
857 install_sdk/R() {
858   if [[ "$NEED_SDK_R" = true ]]; then
859     cd "$WORKSPACE/sdk/R" \
860        && Rscript --vanilla install_deps.R
861   fi
862 }
863
864 install_sdk/perl() {
865     cd "$WORKSPACE/sdk/perl" \
866         && perl Makefile.PL INSTALL_BASE="$PERLINSTALLBASE" \
867         && make install INSTALLDIRS=perl
868 }
869
870 install_sdk/cli() {
871     install_gem arvados-cli sdk/cli
872 }
873
874 install_services/login-sync() {
875     install_gem arvados-login-sync services/login-sync
876 }
877
878 install_services/api() {
879     cd "$WORKSPACE/services/api" \
880         && RAILS_ENV=test bundle_install_trylocal
881
882     rm -f config/environments/test.rb
883     cp config/environments/test.rb.example config/environments/test.rb
884
885     if [ -n "$CONFIGSRC" ]
886     then
887         for f in database.yml
888         do
889             cp "$CONFIGSRC/$f" config/ || fatal "$f"
890         done
891     fi
892
893     # Clear out any lingering postgresql connections to the test
894     # database, so that we can drop it. This assumes the current user
895     # is a postgresql superuser.
896     cd "$WORKSPACE/services/api" \
897         && test_database=$(python -c "import yaml; print yaml.load(file('config/database.yml'))['test']['database']") \
898         && psql "$test_database" -c "SELECT pg_terminate_backend (pg_stat_activity.pid::int) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$test_database';" 2>/dev/null
899
900     mkdir -p "$WORKSPACE/services/api/tmp/pids"
901
902     cert="$WORKSPACE/services/api/tmp/self-signed"
903     if [[ ! -e "$cert.pem" || "$(date -r "$cert.pem" +%s)" -lt 1512659226 ]]; then
904         (
905             dir="$WORKSPACE/services/api/tmp"
906             set -ex
907             openssl req -newkey rsa:2048 -nodes -subj '/C=US/ST=State/L=City/CN=localhost' -out "$cert.csr" -keyout "$cert.key" </dev/null
908             openssl x509 -req -in "$cert.csr" -signkey "$cert.key" -out "$cert.pem" -days 3650 -extfile <(printf 'subjectAltName=DNS:localhost,DNS:::1,DNS:0.0.0.0,DNS:127.0.0.1,IP:::1,IP:0.0.0.0,IP:127.0.0.1')
909         ) || return 1
910     fi
911
912     cd "$WORKSPACE/services/api" \
913         && rm -rf tmp/git \
914         && mkdir -p tmp/git \
915         && cd tmp/git \
916         && tar xf ../../test/test.git.tar \
917         && mkdir -p internal.git \
918         && git --git-dir internal.git init \
919             || return 1
920
921     cd "$WORKSPACE/services/api" \
922         && RAILS_ENV=test bundle exec rake db:drop \
923         && RAILS_ENV=test bundle exec rake db:setup \
924         && RAILS_ENV=test bundle exec rake db:fixtures:load
925 }
926
927 declare -a pythonstuff
928 pythonstuff=(
929     sdk/pam
930     sdk/python
931     sdk/python:py3
932     sdk/cwl
933     sdk/cwl:py3
934     services/dockercleaner:py3
935     services/fuse
936     services/nodemanager
937     tools/crunchstat-summary
938 )
939
940 declare -a gostuff
941 gostuff=(
942     cmd/arvados-client
943     cmd/arvados-server
944     lib/cli
945     lib/cmd
946     lib/controller
947     lib/crunchstat
948     lib/cloud
949     lib/cloud/azure
950     lib/cloud/ec2
951     lib/dispatchcloud
952     lib/dispatchcloud/container
953     lib/dispatchcloud/scheduler
954     lib/dispatchcloud/ssh_executor
955     lib/dispatchcloud/worker
956     sdk/go/arvados
957     sdk/go/arvadosclient
958     sdk/go/auth
959     sdk/go/blockdigest
960     sdk/go/dispatch
961     sdk/go/health
962     sdk/go/httpserver
963     sdk/go/manifest
964     sdk/go/asyncbuf
965     sdk/go/crunchrunner
966     sdk/go/stats
967     services/arv-git-httpd
968     services/crunchstat
969     services/health
970     services/keep-web
971     services/keepstore
972     sdk/go/keepclient
973     services/keep-balance
974     services/keepproxy
975     services/crunch-dispatch-local
976     services/crunch-dispatch-slurm
977     services/crunch-run
978     services/ws
979     tools/keep-block-check
980     tools/keep-exercise
981     tools/keep-rsync
982     tools/sync-groups
983 )
984
985 install_apps/workbench() {
986     cd "$WORKSPACE/apps/workbench" \
987         && mkdir -p tmp/cache \
988         && RAILS_ENV=test bundle_install_trylocal \
989         && RAILS_ENV=test RAILS_GROUPS=assets bundle exec rake npm:install
990 }
991
992 test_doc() {
993     (
994         set -e
995         cd "$WORKSPACE/doc"
996         ARVADOS_API_HOST=qr1hi.arvadosapi.com
997         # Make sure python-epydoc is installed or the next line won't
998         # do much good!
999         PYTHONPATH=$WORKSPACE/sdk/python/ bundle exec rake linkchecker baseurl=file://$WORKSPACE/doc/.site/ arvados_workbench_host=https://workbench.$ARVADOS_API_HOST arvados_api_host=$ARVADOS_API_HOST
1000     )
1001 }
1002
1003 test_services/api() {
1004     rm -f "$WORKSPACE/services/api/git-commit.version"
1005     cd "$WORKSPACE/services/api" \
1006         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test TESTOPTS=-v ${testargs[services/api]}
1007 }
1008
1009 test_sdk/ruby() {
1010     cd "$WORKSPACE/sdk/ruby" \
1011         && bundle exec rake test TESTOPTS=-v ${testargs[sdk/ruby]}
1012 }
1013
1014 test_sdk/R() {
1015   if [[ "$NEED_SDK_R" = true ]]; then
1016     cd "$WORKSPACE/sdk/R" \
1017         && Rscript --vanilla run_test.R
1018   fi
1019 }
1020
1021 test_sdk/cli() {
1022     cd "$WORKSPACE/sdk/cli" \
1023         && mkdir -p /tmp/keep \
1024         && KEEP_LOCAL_STORE=/tmp/keep bundle exec rake test TESTOPTS=-v ${testargs[sdk/cli]}
1025 }
1026
1027 test_services/login-sync() {
1028     cd "$WORKSPACE/services/login-sync" \
1029         && bundle exec rake test TESTOPTS=-v ${testargs[services/login-sync]}
1030 }
1031
1032 test_services/nodemanager_integration() {
1033     cd "$WORKSPACE/services/nodemanager" \
1034         && tests/integration_test.py ${testargs[services/nodemanager_integration]}
1035 }
1036
1037 test_apps/workbench_units() {
1038     cd "$WORKSPACE/apps/workbench" \
1039         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test:units TESTOPTS=-v ${testargs[apps/workbench]}
1040 }
1041
1042 test_apps/workbench_functionals() {
1043     cd "$WORKSPACE/apps/workbench" \
1044         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test:functionals TESTOPTS=-v ${testargs[apps/workbench]}
1045 }
1046
1047 test_apps/workbench_integration() {
1048     cd "$WORKSPACE/apps/workbench" \
1049         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test:integration TESTOPTS=-v ${testargs[apps/workbench]}
1050 }
1051
1052 test_apps/workbench_benchmark() {
1053     cd "$WORKSPACE/apps/workbench" \
1054         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test:benchmark ${testargs[apps/workbench_benchmark]}
1055 }
1056
1057 test_apps/workbench_profile() {
1058     cd "$WORKSPACE/apps/workbench" \
1059         && env RAILS_ENV=test ${short:+RAILS_TEST_SHORT=1} bundle exec rake test:profile ${testargs[apps/workbench_profile]}
1060 }
1061
1062 install_deps() {
1063     # Install parts needed by test suites
1064     do_install env
1065     do_install cmd/arvados-server go
1066     do_install sdk/cli
1067     do_install sdk/perl
1068     do_install sdk/python pip
1069     do_install sdk/ruby
1070     do_install services/api
1071     do_install services/arv-git-httpd go
1072     do_install services/keepproxy go
1073     do_install services/keepstore go
1074     do_install services/keep-web go
1075     do_install services/ws go
1076 }
1077
1078 install_all() {
1079     do_install env
1080     do_install doc
1081     do_install sdk/ruby
1082     do_install sdk/R
1083     do_install sdk/perl
1084     do_install sdk/cli
1085     do_install services/login-sync
1086     for p in "${pythonstuff[@]}"
1087     do
1088         dir=${p%:py3}
1089         if [[ ${dir} = ${p} ]]; then
1090             if [[ -z ${skip[python2]} ]]; then
1091                 do_install ${dir} pip
1092             fi
1093         elif [[ -n ${PYTHON3} ]]; then
1094             if [[ -z ${skip[python3]} ]]; then
1095                 do_install ${dir} pip "$VENV3DIR/bin/"
1096             fi
1097         fi
1098     done
1099     do_install services/api
1100     for g in "${gostuff[@]}"
1101     do
1102         do_install "$g" go
1103     done
1104     do_install apps/workbench
1105 }
1106
1107 test_all() {
1108     stop_services
1109     do_test services/api
1110
1111     # Shortcut for when we're only running apiserver tests. This saves a bit of time,
1112     # because we don't need to start up the api server for subsequent tests.
1113     if [ ! -z "$only" ] && [ "$only" == "services/api" ]; then
1114         rotate_logfile "$WORKSPACE/services/api/log/" "test.log"
1115         exit_cleanly
1116     fi
1117
1118     do_test doc
1119     do_test sdk/ruby
1120     do_test sdk/R
1121     do_test sdk/cli
1122     do_test services/login-sync
1123     do_test services/nodemanager_integration
1124     for p in "${pythonstuff[@]}"
1125     do
1126         dir=${p%:py3}
1127         if [[ ${dir} = ${p} ]]; then
1128             if [[ -z ${skip[python2]} ]]; then
1129                 do_test ${dir} pip
1130             fi
1131         elif [[ -n ${PYTHON3} ]]; then
1132             if [[ -z ${skip[python3]} ]]; then
1133                 do_test ${dir} pip "$VENV3DIR/bin/"
1134             fi
1135         fi
1136     done
1137
1138     for g in "${gostuff[@]}"
1139     do
1140         do_test "$g" go
1141     done
1142     do_test apps/workbench_units
1143     do_test apps/workbench_functionals
1144     do_test apps/workbench_integration
1145     do_test apps/workbench_benchmark
1146     do_test apps/workbench_profile
1147 }
1148
1149 help_interactive() {
1150     echo "== Interactive commands:"
1151     echo "TARGET                 (short for 'test DIR')"
1152     echo "test TARGET"
1153     echo "test TARGET:py3        (test with python3)"
1154     echo "test TARGET -check.vv  (pass arguments to test)"
1155     echo "install TARGET"
1156     echo "install env            (go/python libs)"
1157     echo "install deps           (go/python libs + arvados components needed for integration tests)"
1158     echo "reset                  (...services used by integration tests)"
1159     echo "exit"
1160     echo "== Test targets:"
1161     echo "${!testfuncargs[@]}" | tr ' ' '\n' | sort | column
1162 }
1163
1164 initialize
1165
1166 declare -A testfuncargs=()
1167 for g in "${gostuff[@]}"; do
1168     testfuncargs[$g]="$g go"
1169 done
1170 for p in "${pythonstuff[@]}"; do
1171     dir=${p%:py3}
1172     if [[ ${dir} = ${p} ]]; then
1173         testfuncargs[$p]="$dir pip $VENVDIR/bin/"
1174     else
1175         testfuncargs[$p]="$dir pip $VENV3DIR/bin/"
1176     fi
1177 done
1178
1179 if [[ -z ${interactive} ]]; then
1180     install_all
1181     test_all
1182 else
1183     skip=()
1184     only=()
1185     only_install=()
1186     if [[ -e "$VENVDIR/bin/activate" ]]; then stop_services; fi
1187     setnextcmd() {
1188         if [[ "$nextcmd" != "install deps" ]]; then
1189             :
1190         elif [[ -e "$VENVDIR/bin/activate" ]]; then
1191             nextcmd="test lib/cmd"
1192         else
1193             nextcmd="install deps"
1194         fi
1195     }
1196     echo
1197     help_interactive
1198     nextcmd="install deps"
1199     setnextcmd
1200     while read -p 'What next? ' -e -i "${nextcmd}" nextcmd; do
1201         read verb target opts <<<"${nextcmd}"
1202         case "${verb}" in
1203             "" | "help")
1204                 help_interactive
1205                 ;;
1206             "exit" | "quit")
1207                 exit_cleanly
1208                 ;;
1209             "reset")
1210                 stop_services
1211                 ;;
1212             *)
1213                 target="${target%/}"
1214                 testargs["$target"]="${opts}"
1215                 case "$target" in
1216                     all | deps)
1217                         ${verb}_${target}
1218                         ;;
1219                     *)
1220                         tt="${testfuncargs[${target}]}"
1221                         tt="${tt:-$target}"
1222                         do_$verb $tt
1223                         ;;
1224                 esac
1225                 ;;
1226         esac
1227         if [[ ${#successes[@]} -gt 0 || ${#failures[@]} -gt 0 ]]; then
1228             report_outcomes
1229             successes=()
1230             failures=()
1231         fi
1232         cd "$WORKSPACE"
1233         setnextcmd
1234     done
1235     echo
1236 fi
1237 exit_cleanly