3021: Cache packages with bundle package --all
[arvados-dev.git] / jenkins / run-tests.sh
1 #!/bin/bash
2
3 read -rd "\000" helpmessage <<EOF
4 $(basename $0): Install and test Arvados components.
5
6 Exit non-zero if any tests fail.
7
8 Syntax:
9         $(basename $0) WORKSPACE=/path/to/arvados [options]
10
11 Options:
12
13 --skip FOO     Do not test the FOO component.
14 --only FOO     Do not test anything except the FOO component.
15 --leave-temp   Do not remove GOPATH, virtualenv, and other temp dirs at exit.
16                Instead, show which directories were used this time so they
17                can be reused in subsequent invocations.
18 --skip-install Do not run any install steps. Just run tests.
19                You should provide GOPATH, GEMHOME, and VENVDIR options
20                from a previous invocation if you use this option.
21 WORKSPACE=path Arvados source tree to test.
22 CONFIGSRC=path Dir with api server config files to copy into source tree.
23                (If none given, leave config files alone in source tree.)
24 services/api_test="TEST=test/functional/arvados/v1/collections_controller_test.rb"
25                Restrict apiserver tests to the given file
26 sdk/python_test="--test-suite test.test_keep_locator"
27                Restrict Python SDK tests to the given class
28 apps/workbench_test="TEST=test/integration/pipeline_instances_test.rb"
29                Restrict Workbench tests to the given file
30 ARVADOS_DEBUG=1
31                Print more debug messages
32 envvar=value   Set \$envvar to value. Primarily useful for WORKSPACE,
33                *_test, and other examples shown above.
34
35 Assuming --skip-install is not given, all components are installed
36 into \$GOPATH, \$VENDIR, and \$GEMHOME before running any tests. Many
37 test suites depend on other components being installed, and installing
38 everything tends to be quicker than debugging dependencies.
39
40 As a special concession to the current CI server config, CONFIGSRC
41 defaults to $HOME/arvados-api-server if that directory exists.
42
43 More information and background:
44
45 https://arvados.org/projects/arvados/wiki/Running_tests
46
47 Available tests:
48
49 apps/workbench
50 apps/workbench_benchmark
51 apps/workbench_profile
52 doc
53 services/api
54 services/crunchstat
55 services/fuse
56 services/keepproxy
57 services/keepstore
58 services/nodemanager
59 sdk/cli
60 sdk/python
61 sdk/ruby
62 sdk/go/arvadosclient
63 sdk/go/keepclient
64 sdk/go/streamer
65
66 EOF
67
68 # First make sure to remove any ARVADOS_ variables from the calling
69 # environment that could interfere with the tests.
70 unset $(env | cut -d= -f1 | grep \^ARVADOS_)
71
72 # Reset other variables that could affect our [tests'] behavior by
73 # accident.
74 GITDIR=
75 GOPATH=
76 VENVDIR=
77 PYTHONPATH=
78 GEMHOME=
79
80 COLUMNS=80
81
82 leave_temp=
83 skip_install=
84
85 declare -A leave_temp
86 clear_temp() {
87     leaving=""
88     for var in VENVDIR GOPATH GITDIR GEMHOME
89     do
90         if [[ -z "${leave_temp[$var]}" ]]
91         then
92             if [[ -n "${!var}" ]]
93             then
94                 rm -rf "${!var}"
95             fi
96         else
97             leaving+=" $var=\"${!var}\""
98         fi
99     done
100     if [[ -n "$leaving" ]]; then
101         echo "Leaving behind temp dirs: $leaving"
102     fi
103 }
104
105 fatal() {
106     clear_temp
107     echo >&2 "Fatal: $* in ${FUNCNAME[1]} at ${BASH_SOURCE[1]} line ${BASH_LINENO[0]}"
108     exit 1
109 }
110
111 report_outcomes() {
112     for x in "${successes[@]}"
113     do
114         echo "Pass: $x"
115     done
116
117     if [[ ${#failures[@]} == 0 ]]
118     then
119         echo "All test suites passed."
120     else
121         echo "Failures (${#failures[@]}):"
122         for x in "${failures[@]}"
123         do
124             echo "Fail: $x"
125         done
126     fi
127 }
128
129 sanity_checks() {
130   # Make sure WORKSPACE is set
131   if ! [[ -n "$WORKSPACE" ]]; then
132     echo >&2 "$helpmessage"
133     echo >&2
134     echo >&2 "Error: WORKSPACE environment variable not set"
135     echo >&2
136     exit 1
137   fi
138
139   # Make sure virtualenv is installed
140   `virtualenv --help >/dev/null 2>&1`
141
142   if [[ "$?" != "0" ]]; then
143     echo >&2
144     echo >&2 "Error: virtualenv could not be found"
145     echo >&2
146     exit 1
147   fi
148
149   # Make sure go is installed
150   `go env >/dev/null 2>&1`
151
152   if [[ "$?" != "0" ]]; then
153     echo >&2
154     echo >&2 "Error: go could not be found"
155     echo >&2
156     exit 1
157   fi
158
159   # Make sure gcc is installed
160   `gcc --help >/dev/null 2>&1`
161
162   if [[ "$?" != "0" ]]; then
163     echo >&2
164     echo >&2 "Error: gcc could not be found"
165     echo >&2
166     exit 1
167   fi
168
169 }
170
171 rotate_logfile() {
172   # $BUILD_NUMBER is set by Jenkins if this script is being called as part of a Jenkins run
173   if [[ -f "$1/$2" ]]; then
174     THEDATE=`date +%Y%m%d%H%M%S`
175     mv "$1/$2" "$1/$THEDATE-$BUILD_NUMBER-$2"
176     gzip "$1/$THEDATE-$BUILD_NUMBER-$2"
177   fi
178 }
179
180 declare -a failures
181 declare -A skip
182 declare -A testargs
183 skip[apps/workbench_profile]=1
184
185 while [[ -n "$1" ]]
186 do
187     arg="$1"; shift
188     case "$arg" in
189         --help)
190             echo >&2 "$helpmessage"
191             echo >&2
192             exit 1
193             ;;
194         --skip)
195             skipwhat="$1"; shift
196             skip[$skipwhat]=1
197             ;;
198         --only)
199             only="$1"; skip[$1]=""; shift
200             ;;
201         --skip-install)
202             skip_install=1
203             ;;
204         --leave-temp)
205             leave_temp[VENVDIR]=1
206             leave_temp[GOPATH]=1
207             leave_temp[GEMHOME]=1
208             ;;
209         *_test=*)
210             suite="${arg%%_test=*}"
211             args="${arg#*=}"
212             testargs["$suite"]="$args"
213             ;;
214         *=*)
215             eval export $(echo $arg | cut -d= -f1)=\"$(echo $arg | cut -d= -f2-)\"
216             ;;
217         *)
218             echo >&2 "$0: Unrecognized option: '$arg'. Try: $0 --help"
219             exit 1
220             ;;
221     esac
222 done
223
224 sanity_checks
225
226 echo "WORKSPACE=$WORKSPACE"
227
228 if [[ -z "$CONFIGSRC" ]] && [[ -d "$HOME/arvados-api-server" ]]; then
229     # Jenkins expects us to use this by default.
230     CONFIGSRC="$HOME/arvados-api-server"
231 fi
232
233 # Clean up .pyc files that may exist in the workspace
234 cd "$WORKSPACE"
235 find -name '*.pyc' -delete
236
237 # Set up temporary install dirs (unless existing dirs were supplied)
238 for tmpdir in VENVDIR GOPATH GEMHOME
239 do
240     if [[ -n "${!tmpdir}" ]]; then
241         leave_temp[$tmpdir]=1
242     else
243         eval $tmpdir=$(mktemp -d)
244     fi
245 done
246
247 setup_ruby_environment() {
248     if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
249       source "$HOME/.rvm/scripts/rvm"
250       using_rvm=true
251     elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
252       source "/usr/local/rvm/scripts/rvm"
253       using_rvm=true
254     else
255       using_rvm=false
256     fi
257
258     if [[ "$using_rvm" == true ]]; then
259         # If rvm is in use, we can't just put separate "dependencies"
260         # and "gems-under-test" paths to GEM_PATH: passenger resets
261         # the environment to the "current gemset", which would lose
262         # our GEM_PATH and prevent our test suites from running ruby
263         # programs (for example, the Workbench test suite could not
264         # boot an API server or run arv). Instead, we have to make an
265         # rvm gemset and use it for everything.
266
267         [[ `type rvm | head -n1` == "rvm is a function" ]] \
268             || fatal 'rvm check'
269
270         # Put rvm's favorite path back in first place (overriding
271         # virtualenv, which just put itself there). Ignore rvm's
272         # complaint about not being in first place already.
273         rvm use @default 2>/dev/null
274
275         # Create (if needed) and switch to an @arvados-tests
276         # gemset. (Leave the choice of ruby to the caller.)
277         rvm use @arvados-tests --create \
278             || fatal 'rvm gemset setup'
279
280         rvm env
281     else
282         # When our "bundle install"s need to install new gems to
283         # satisfy dependencies, we want them to go where "gem install
284         # --user-install" would put them. (However, if the caller has
285         # already set GEM_HOME, we assume that's where dependencies
286         # should be installed, and we should leave it alone.)
287
288         if [ -z "$GEM_HOME" ]; then
289             user_gempath="$(gem env gempath)"
290             export GEM_HOME="${user_gempath%%:*}"
291         fi
292         PATH="$(gem env gemdir)/bin:$PATH"
293
294         # When we build and install our own gems, we install them in our
295         # $GEMHOME tmpdir, and we want them to be at the front of GEM_PATH and
296         # PATH so integration tests prefer them over other versions that
297         # happen to be installed in $user_gempath, system dirs, etc.
298
299         tmpdir_gem_home="$(env - PATH="$PATH" HOME="$GEMHOME" gem env gempath | cut -f1 -d:)"
300         PATH="$tmpdir_gem_home/bin:$PATH"
301         export GEM_PATH="$tmpdir_gem_home:$(gem env gempath)"
302
303         echo "Will install dependencies to $(gem env gemdir)"
304         echo "Will install arvados gems to $tmpdir_gem_home"
305         echo "Gem search path is GEM_PATH=$GEM_PATH"
306     fi
307 }
308
309 with_test_gemset() {
310     if [[ "$using_rvm" == true ]]; then
311         "$@"
312     else
313         GEM_HOME="$tmpdir_gem_home" "$@"
314     fi
315 }
316
317 export GOPATH
318 mkdir -p "$GOPATH/src/git.curoverse.com"
319 ln -sfn "$WORKSPACE" "$GOPATH/src/git.curoverse.com/arvados.git" \
320     || fatal "symlink failed"
321
322 virtualenv --setuptools "$VENVDIR" || fatal "virtualenv $VENVDIR failed"
323 . "$VENVDIR/bin/activate"
324
325 # Note: this must be the last time we change PATH, otherwise rvm will
326 # whine a lot.
327 setup_ruby_environment
328
329 echo "PATH is $PATH"
330
331 if ! which bundler >/dev/null
332 then
333     gem install --user-install bundler || fatal 'Could not install bundler'
334 fi
335
336 # Needed for run_test_server.py which is used by certain (non-Python) tests.
337 pip install PyYAML || fatal "pip install PyYAML failed"
338
339 # Needed for python-daemon 2.0.2, which breaks otherwise with
340 # "ImportError: No module named docutils.core"
341 pip install docutils || fatal "pip install docutils failed"
342
343 checkexit() {
344     if [[ "$?" != "0" ]]; then
345         title "!!!!!! $1 FAILED !!!!!!"
346         failures+=("$1 (`timer`)")
347     else
348         successes+=("$1 (`timer`)")
349     fi
350 }
351
352 timer_reset() {
353     t0=$SECONDS
354 }
355
356 timer() {
357     echo -n "$(($SECONDS - $t0))s"
358 }
359
360 do_test() {
361     if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$1" ]] )
362     then
363         title "Running $1 tests"
364         timer_reset
365         if [[ "$2" == "go" ]]
366         then
367             go test ${testargs[$1]} "git.curoverse.com/arvados.git/$1"
368         elif [[ "$2" == "pip" ]]
369         then
370            cd "$WORKSPACE/$1" \
371                 && python setup.py test ${testargs[$1]}
372         elif [[ "$2" != "" ]]
373         then
374             "test_$2"
375         else
376             "test_$1"
377         fi
378         checkexit "$1 tests"
379         title "End of $1 tests (`timer`)"
380     else
381         title "Skipping $1 tests"
382     fi
383 }
384
385 do_install() {
386     if [[ -z "$skip_install" ]]
387     then
388         title "Running $1 install"
389         timer_reset
390         if [[ "$2" == "go" ]]
391         then
392             go get -t "git.curoverse.com/arvados.git/$1"
393         elif [[ "$2" == "pip" ]]
394         then
395             cd "$WORKSPACE/$1" \
396                 && python setup.py sdist rotate --keep=1 --match .tar.gz \
397                 && pip install --upgrade dist/*.tar.gz
398         elif [[ "$2" != "" ]]
399         then
400             "install_$2"
401         else
402             "install_$1"
403         fi
404         checkexit "$1 install"
405         title "End of $1 install (`timer`)"
406     else
407         title "Skipping $1 install"
408     fi
409 }
410
411 title () {
412     txt="********** $1 **********"
413     printf "\n%*s%s\n\n" $((($COLUMNS-${#txt})/2)) "" "$txt"
414 }
415
416 install_doc() {
417     cd "$WORKSPACE/doc" \
418         && (bundle install --local --no-deployment \
419         || bundle install --no-deployment) \
420         && bundle package --all \
421         && rm -rf .site
422 }
423 do_install doc
424
425 install_ruby_sdk() {
426     with_test_gemset gem uninstall --force --all --executables arvados \
427         && cd "$WORKSPACE/sdk/ruby" \
428         && (bundle install --local --no-deployment \
429         || bundle install --no-deployment) \
430         && bundle package --all \
431         && gem build arvados.gemspec \
432         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-*.gem|head -n1`
433 }
434 do_install sdk/ruby ruby_sdk
435
436 install_cli() {
437     with_test_gemset gem uninstall --force --all --executables arvados-cli \
438         && cd "$WORKSPACE/sdk/cli" \
439         && (bundle install --local --no-deployment \
440         || bundle install --no-deployment) \
441         && bundle package --all \
442         && gem build arvados-cli.gemspec \
443         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-cli-*.gem|head -n1`
444 }
445 do_install sdk/cli cli
446
447 # Install the Python SDK early. Various other test suites (like
448 # keepproxy) bring up run_test_server.py, which imports the arvados
449 # module. We can't actually *test* the Python SDK yet though, because
450 # its own test suite brings up some of those other programs (like
451 # keepproxy).
452 declare -a pythonstuff
453 pythonstuff=(
454     sdk/python
455     services/fuse
456     services/nodemanager
457     )
458 for p in "${pythonstuff[@]}"
459 do
460     do_install "$p" pip
461 done
462
463 install_apiserver() {
464     cd "$WORKSPACE/services/api" \
465         && (RAILS_ENV=test bundle install --local --no-deployment \
466         || RAILS_ENV=test bundle install --no-deployment) \
467         && bundle package --all
468
469     rm -f config/environments/test.rb
470     cp config/environments/test.rb.example config/environments/test.rb
471
472     if [ -n "$CONFIGSRC" ]
473     then
474         for f in database.yml application.yml
475         do
476             cp "$CONFIGSRC/$f" config/ || fatal "$f"
477         done
478     fi
479
480     # Fill in a random secret_token and blob_signing_key for testing
481     SECRET_TOKEN=`echo 'puts rand(2**512).to_s(36)' |ruby`
482     BLOB_SIGNING_KEY=`echo 'puts rand(2**512).to_s(36)' |ruby`
483
484     sed -i'' -e "s:SECRET_TOKEN:$SECRET_TOKEN:" config/application.yml
485     sed -i'' -e "s:BLOB_SIGNING_KEY:$BLOB_SIGNING_KEY:" config/application.yml
486
487     # Set up empty git repo (for git tests)
488     GITDIR=$(mktemp -d)
489     sed -i'' -e "s:/var/cache/git:$GITDIR:" config/application.default.yml
490
491     rm -rf $GITDIR
492     mkdir -p $GITDIR/test
493     cd $GITDIR/test \
494         && git init \
495         && git config user.email "jenkins@ci.curoverse.com" \
496         && git config user.name "Jenkins, CI" \
497         && touch tmp \
498         && git add tmp \
499         && git commit -m 'initial commit'
500
501     # Clear out any lingering postgresql connections to arvados_test, so that we can drop it
502     # This assumes the current user is a postgresql superuser
503     psql arvados_test -c "SELECT pg_terminate_backend (pg_stat_activity.procpid::int) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'arvados_test';" 2>/dev/null
504
505     cd "$WORKSPACE/services/api" \
506         && RAILS_ENV=test bundle exec rake db:drop \
507         && RAILS_ENV=test bundle exec rake db:setup \
508         && RAILS_ENV=test bundle exec rake db:fixtures:load
509 }
510 do_install services/api apiserver
511
512 declare -a gostuff
513 gostuff=(
514     services/crunchstat
515     services/keepstore
516     services/keepproxy
517     sdk/go/arvadosclient
518     sdk/go/keepclient
519     sdk/go/streamer
520     )
521 for g in "${gostuff[@]}"
522 do
523     do_install "$g" go
524 done
525
526 install_workbench() {
527     cd "$WORKSPACE/apps/workbench" \
528         && (RAILS_ENV=test bundle install --local --no-deployment \
529         || RAILS_ENV=test bundle install --no-deployment) \
530         && bundle package --all
531 }
532 do_install apps/workbench workbench
533
534 start_api() {
535     echo 'Starting API server...'
536     cd "$WORKSPACE" && \
537         eval $(python sdk/python/tests/run_test_server.py start --auth admin) && \
538         export ARVADOS_TEST_API_HOST="$ARVADOS_API_HOST" && \
539         export ARVADOS_TEST_API_INSTALLED="$$" && \
540         (env | egrep ^ARVADOS)
541 }
542
543 stop_api() {
544     unset ARVADOS_TEST_API_HOST
545     cd "$WORKSPACE" && \
546         python sdk/python/tests/run_test_server.py stop
547 }
548
549 test_doclinkchecker() {
550     (
551         set -e
552         cd "$WORKSPACE/doc"
553         ARVADOS_API_HOST=qr1hi.arvadosapi.com
554         # Make sure python-epydoc is installed or the next line won't
555         # do much good!
556         PYTHONPATH=$WORKSPACE/sdk/python/ bundle exec rake linkchecker baseurl=file://$WORKSPACE/doc/.site/ arvados_workbench_host=workbench.$ARVADOS_API_HOST arvados_api_host=$ARVADOS_API_HOST
557     )
558 }
559 do_test doc doclinkchecker
560
561 stop_api
562
563 test_apiserver() {
564     cd "$WORKSPACE/services/api"
565     RAILS_ENV=test bundle exec rake test ${testargs[services/api]}
566 }
567 do_test services/api apiserver
568
569 start_api
570
571 test_ruby_sdk() {
572     cd "$WORKSPACE/sdk/ruby" \
573         && bundle exec rake test ${testargs[sdk/ruby]}
574 }
575 do_test sdk/ruby ruby_sdk
576
577 test_cli() {
578     cd "$WORKSPACE/sdk/cli" \
579         && mkdir -p /tmp/keep \
580         && KEEP_LOCAL_STORE=/tmp/keep bundle exec rake test ${testargs[sdk/cli]}
581 }
582 do_test sdk/cli cli
583
584 for p in "${pythonstuff[@]}"
585 do
586     do_test "$p" pip
587 done
588
589 for g in "${gostuff[@]}"
590 do
591     do_test "$g" go
592 done
593
594 test_workbench() {
595     cd "$WORKSPACE/apps/workbench" \
596         && RAILS_ENV=test bundle exec rake test ${testargs[apps/workbench]}
597 }
598 do_test apps/workbench workbench
599
600 test_workbench_benchmark() {
601     cd "$WORKSPACE/apps/workbench" \
602         && RAILS_ENV=test bundle exec rake test:benchmark ${testargs[apps/workbench_benchmark]}
603 }
604 do_test apps/workbench_benchmark workbench_benchmark
605
606 test_workbench_profile() {
607     cd "$WORKSPACE/apps/workbench" \
608         && RAILS_ENV=test bundle exec rake test:profile ${testargs[apps/workbench_profile]}
609 }
610 do_test apps/workbench_profile workbench_profile
611
612 rotate_logfile "$WORKSPACE/apps/workbench/log/" "test.log"
613
614 stop_api
615 rotate_logfile "$WORKSPACE/services/api/log/" "test.log"
616
617 report_outcomes
618 clear_temp
619
620 exit ${#failures}