4156: Move "bundle install" into separate install_ task like apiserver. Remove one...
[arvados.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 apiserver_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 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 EOF
47
48 # First make sure to remove any ARVADOS_ variables from the calling
49 # environment that could interfere with the tests.
50 unset $(env | cut -d= -f1 | grep \^ARVADOS_)
51
52 # Reset other variables that could affect our [tests'] behavior by
53 # accident.
54 GITDIR=
55 GOPATH=
56 VENVDIR=
57 PYTHONPATH=
58 GEMHOME=
59
60 COLUMNS=80
61
62 leave_temp=
63 skip_install=
64
65 declare -A leave_temp
66 clear_temp() {
67     leaving=""
68     for var in VENVDIR GOPATH GITDIR GEMHOME
69     do
70         if [[ -z "${leave_temp[$var]}" ]]
71         then
72             if [[ -n "${!var}" ]]
73             then
74                 rm -rf "${!var}"
75             fi
76         else
77             leaving+=" $var=\"${!var}\""
78         fi
79     done
80     if [[ -n "$leaving" ]]; then
81         echo "Leaving behind temp dirs: $leaving"
82     fi
83 }
84
85 fatal() {
86     clear_temp
87     echo >&2 "Fatal: $* in ${FUNCNAME[1]} at ${BASH_SOURCE[1]} line ${BASH_LINENO[0]}"
88     exit 1
89 }
90
91 report_outcomes() {
92     for x in "${successes[@]}"
93     do
94         echo "Pass: $x"
95     done
96
97     if [[ ${#failures[@]} == 0 ]]
98     then
99         echo "All test suites passed."
100     else
101         echo "Failures (${#failures[@]}):"
102         for x in "${failures[@]}"
103         do
104             echo "Fail: $x"
105         done
106     fi
107 }
108 declare -a failures
109 declare -A skip
110 declare -A testargs
111
112 while [[ -n "$1" ]]
113 do
114     arg="$1"; shift
115     case "$arg" in
116         --help)
117             echo >&2 "$helpmessage"
118             echo >&2
119             exit 1
120             ;;
121         --skip)
122             skipwhat="$1"; shift
123             skip[$skipwhat]=1
124             ;;
125         --only)
126             only="$1"; shift
127             ;;
128         --skip-install)
129             skip_install=1
130             ;;
131         --leave-temp)
132             leave_temp[VENVDIR]=1
133             leave_temp[GOPATH]=1
134             leave_temp[GEMHOME]=1
135             ;;
136         *_test=*)
137             suite="${arg%%_test=*}"
138             args="${arg#*=}"
139             testargs["$suite"]="$args"
140             ;;
141         *=*)
142             eval export $(echo $arg | cut -d= -f1)=\"$(echo $arg | cut -d= -f2-)\"
143             ;;
144         *)
145             echo >&2 "$0: Unrecognized option: '$arg'. Try: $0 --help"
146             exit 1
147             ;;
148     esac
149 done
150
151 # Sanity check
152 if ! [[ -n "$WORKSPACE" ]]; then
153   echo >&2 "$helpmessage"
154   echo >&2
155   echo >&2 "Error: WORKSPACE environment variable not set"
156   echo >&2
157   exit 1
158 fi
159
160 echo "WORKSPACE=$WORKSPACE"
161
162 if [[ -z "$CONFIGSRC" ]] && [[ -d "$HOME/arvados-api-server" ]]; then
163     # Jenkins expects us to use this by default.
164     CONFIGSRC="$HOME/arvados-api-server"
165 fi
166
167 # Clean up .pyc files that may exist in the workspace
168 cd "$WORKSPACE"
169 find -name '*.pyc' -delete
170
171 # Set up temporary install dirs (unless existing dirs were supplied)
172 for tmpdir in VENVDIR GOPATH GEMHOME
173 do
174     if [[ -n "${!tmpdir}" ]]; then
175         leave_temp[$tmpdir]=1
176     else
177         eval $tmpdir=$(mktemp -d)
178     fi
179 done
180
181 setup_ruby_environment() {
182     if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
183       source "$HOME/.rvm/scripts/rvm"
184       using_rvm=true
185     elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
186       source "/usr/local/rvm/scripts/rvm"
187       using_rvm=true
188     else
189       using_rvm=false
190     fi
191
192     if [[ "$using_rvm" == true ]]; then
193         # If rvm is in use, we can't just put separate "dependencies"
194         # and "gems-under-test" paths to GEM_PATH: passenger resets
195         # the environment to the "current gemset", which would lose
196         # our GEM_PATH and prevent our test suites from running ruby
197         # programs (for example, the Workbench test suite could not
198         # boot an API server or run arv). Instead, we have to make an
199         # rvm gemset and use it for everything.
200
201         [[ `type rvm | head -n1` == "rvm is a function" ]] \
202             || fatal 'rvm check'
203
204         # Put rvm's favorite path back in first place (overriding
205         # virtualenv, which just put itself there). Ignore rvm's
206         # complaint about not being in first place already.
207         rvm use @default 2>/dev/null
208
209         # Create (if needed) and switch to an @arvados-tests
210         # gemset. (Leave the choice of ruby to the caller.)
211         rvm use @arvados-tests --create \
212             || fatal 'rvm gemset setup'
213
214         rvm env
215
216         # Remove previously installed versions of our own gems. This
217         # ensures the test suites only have access to [a] published
218         # gems and [b] the gems we build and install right now --
219         # never unpublished gems left over from previous builds.
220         gem uninstall --all --executables arvados arvados-cli \
221             || fatal 'clean arvados gems'
222     else
223         echo "RVM not found. Will install gems-under-test into \"$GEM_HOME\"."
224
225         # When our "bundle install"s need to install new gems to satisfy
226         # dependencies, we want them to go where "gem install --user-install"
227         # would put them. If rvm is in use (or something else has set
228         # GEM_HOME) we assume "bundle install" already does something
229         # reasonable.
230
231         if [ -z "$GEM_HOME" ]; then
232             user_gempath="$(gem env gempath)"
233             export GEM_HOME="${user_gempath%%:*}"
234         fi
235         PATH="$(gem env gemdir)/bin:$PATH"
236
237         # When we build and install our own gems, we install them in our
238         # $GEMHOME tmpdir, and we want them to be at the front of GEM_PATH and
239         # PATH so integration tests prefer them over other versions that
240         # happen to be installed in $user_gempath, system dirs, etc.
241
242         tmpdir_gem_home="$(env - PATH="$PATH" HOME="$GEMHOME" gem env gempath | cut -f1 -d:)"
243         PATH="${tmpdir_gem_home%%:*}/bin:$PATH"
244         export GEM_PATH="$tmpdir_gem_home:$(gem env gempath)"
245
246         echo "Will install dependencies to $GEM_HOME"
247         echo "Will install arvados gems to $tmpdir_gem_home"
248         echo "Gem search path is GEM_PATH=$GEM_PATH"
249     fi
250 }
251 with_test_gemset() {
252     if [[ "$using_rvm" == true ]]; then
253         "$@"
254     else
255         GEM_HOME="$tmpdir_gem_home" "$@"
256     fi
257 }
258
259 export GOPATH
260 mkdir -p "$GOPATH/src/git.curoverse.com"
261 ln -sfn "$WORKSPACE" "$GOPATH/src/git.curoverse.com/arvados.git" \
262     || fatal "symlink failed"
263
264 virtualenv --setuptools "$VENVDIR" || fatal "virtualenv $VENVDIR failed"
265 . "$VENVDIR/bin/activate"
266
267 # Note: this must be the last time we change PATH, otherwise rvm will
268 # whine a lot.
269 setup_ruby_environment
270
271 echo "PATH is $PATH"
272
273 if ! which bundler >/dev/null
274 then
275     gem install --user-install bundler || fatal 'Could not install bundler'
276 fi
277
278 # Needed for run_test_server.py which is used by certain (non-Python) tests.
279 pip install PyYAML
280
281 checkexit() {
282     if [[ "$?" != "0" ]]; then
283         title "!!!!!! $1 FAILED !!!!!!"
284         failures+=("$1 (`timer`)")
285     else
286         successes+=("$1 (`timer`)")
287     fi
288 }
289
290 timer_reset() {
291     t0=$SECONDS
292 }
293
294 timer() {
295     echo -n "$(($SECONDS - $t0))s"
296 }
297
298 do_test() {
299     if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$1" ]] )
300     then
301         title "Running $1 tests"
302         timer_reset
303         if [[ "$2" == "go" ]]
304         then
305             go test "git.curoverse.com/arvados.git/$1"
306         elif [[ "$2" == "pip" ]]
307         then
308            # Other test suites can depend on tests_require
309            # dependencies of this package. For example, keepproxy runs
310            # run_test_server.py, which depends on the yaml package,
311            # which is in sdk/python's tests_require but not
312            # install_requires, and therefore does not get installed by
313            # setuptools until we run "setup.py test" *and* install the
314            # .egg files that setup.py downloads.
315            cd "$WORKSPACE/$1" \
316                 && python setup.py test ${testargs[$1]} \
317                 && (easy_install *.egg || true)
318         else
319             "test_$1"
320         fi
321         checkexit "$1 tests"
322         title "End of $1 tests (`timer`)"
323     else
324         title "Skipping $1 tests"
325     fi
326 }
327
328 do_install() {
329     if [[ -z "$skip_install" ]]
330     then
331         title "Running $1 install"
332         timer_reset
333         if [[ "$2" == "go" ]]
334         then
335             go get -t "git.curoverse.com/arvados.git/$1"
336         elif [[ "$2" == "pip" ]]
337         then
338             cd "$WORKSPACE/$1" \
339                 && python setup.py sdist rotate --keep=1 --match .tar.gz \
340                 && pip install --upgrade dist/*.tar.gz
341         else
342             "install_$1"
343         fi
344         checkexit "$1 install"
345         title "End of $1 install (`timer`)"
346     else
347         title "Skipping $1 install"
348     fi
349 }
350
351 title () {
352     txt="********** $1 **********"
353     printf "\n%*s%s\n\n" $((($COLUMNS-${#txt})/2)) "" "$txt"
354 }
355
356 install_docs() {
357     cd "$WORKSPACE/doc"
358     bundle install --no-deployment
359     rm -rf .site
360     # Make sure python-epydoc is installed or the next line won't do much good!
361     ARVADOS_API_HOST=qr1hi.arvadosapi.com
362     PYTHONPATH=$WORKSPACE/sdk/python/ bundle exec rake generate baseurl=file://$WORKSPACE/doc/.site/ arvados_workbench_host=workbench.$ARVADOS_API_HOST arvados_api_host=$ARVADOS_API_HOST
363     unset ARVADOS_API_HOST
364 }
365 do_install docs
366
367 install_ruby_sdk() {
368     cd "$WORKSPACE/sdk/ruby" \
369         && bundle install --no-deployment \
370         && gem build arvados.gemspec \
371         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-*.gem|head -n1`
372 }
373 do_install ruby_sdk
374
375 install_cli() {
376     cd "$WORKSPACE/sdk/cli" \
377         && bundle install --no-deployment \
378         && gem build arvados-cli.gemspec \
379         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-cli-*.gem|head -n1`
380 }
381 do_install cli
382
383 # Install the Python SDK early. Various other test suites (like
384 # keepproxy) bring up run_test_server.py, which imports the arvados
385 # module. We can't actually *test* the Python SDK yet though, because
386 # its own test suite brings up some of those other programs (like
387 # keepproxy).
388 declare -a pythonstuff
389 pythonstuff=(
390     sdk/python
391     services/fuse
392     services/nodemanager
393     )
394 for p in "${pythonstuff[@]}"
395 do
396     do_install "$p" pip
397 done
398
399 install_apiserver() {
400     cd "$WORKSPACE/services/api"
401     export RAILS_ENV=test
402     bundle install --no-deployment
403
404     rm -f config/environments/test.rb
405     cp config/environments/test.rb.example config/environments/test.rb
406
407     if [ -n "$CONFIGSRC" ]
408     then
409         for f in database.yml application.yml
410         do
411             cp "$CONFIGSRC/$f" config/ || fatal "$f"
412         done
413     fi
414
415     # Fill in a random secret_token and blob_signing_key for testing
416     SECRET_TOKEN=`echo 'puts rand(2**512).to_s(36)' |ruby`
417     BLOB_SIGNING_KEY=`echo 'puts rand(2**512).to_s(36)' |ruby`
418
419     sed -i'' -e "s:SECRET_TOKEN:$SECRET_TOKEN:" config/application.yml
420     sed -i'' -e "s:BLOB_SIGNING_KEY:$BLOB_SIGNING_KEY:" config/application.yml
421
422     # Set up empty git repo (for git tests)
423     GITDIR=$(mktemp -d)
424     sed -i'' -e "s:/var/cache/git:$GITDIR:" config/application.default.yml
425
426     rm -rf $GITDIR
427     mkdir -p $GITDIR/test
428     cd $GITDIR/test \
429         && git init \
430         && git config user.email "jenkins@ci.curoverse.com" \
431         && git config user.name "Jenkins, CI" \
432         && touch tmp \
433         && git add tmp \
434         && git commit -m 'initial commit'
435
436     # Clear out any lingering postgresql connections to arvados_test, so that we can drop it
437     # This assumes the current user is a postgresql superuser
438     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
439
440     cd "$WORKSPACE/services/api" \
441         && bundle exec rake db:drop \
442         && bundle exec rake db:create \
443         && bundle exec rake db:setup
444 }
445 do_install apiserver
446
447 declare -a gostuff
448 gostuff=(
449     services/crunchstat
450     services/keepstore
451     services/keepproxy
452     sdk/go/arvadosclient
453     sdk/go/keepclient
454     sdk/go/streamer
455     )
456 for g in "${gostuff[@]}"
457 do
458     do_install "$g" go
459 done
460
461 install_workbench() {
462     cd "$WORKSPACE/apps/workbench" \
463         && RAILS_ENV=test bundle install --no-deployment
464 }
465 do_install workbench
466
467 test_doclinkchecker() {
468     cd "$WORKSPACE/doc"
469     bundle exec rake linkchecker baseurl=file://$WORKSPACE/doc/.site/
470 }
471 do_test doclinkchecker
472
473 test_ruby_sdk() {
474     cd "$WORKSPACE/sdk/ruby" \
475         && bundle install --no-deployment \
476         && bundle exec rake test ${testargs[sdk/ruby]}
477 }
478 do_test ruby_sdk
479
480 test_cli() {
481     title "Starting SDK CLI tests"
482     cd "$WORKSPACE/sdk/cli" \
483         && bundle install --no-deployment \
484         && mkdir -p /tmp/keep \
485         && KEEP_LOCAL_STORE=/tmp/keep bundle exec rake test ${testargs[sdk/cli]}
486 }
487 do_test cli
488
489 test_apiserver() {
490     cd "$WORKSPACE/services/api"
491     bundle exec rake test ${testargs[apiserver]}
492 }
493 do_test apiserver
494
495 # We must test sdk/python before testing services/keepproxy, because
496 # keepproxy depends on sdk/python's test dependencies.
497 for p in "${pythonstuff[@]}"
498 do
499     do_test "$p" pip
500 done
501
502 for g in "${gostuff[@]}"
503 do
504     do_test "$g" go
505 done
506
507 test_workbench() {
508     cd "$WORKSPACE/apps/workbench" \
509         && bundle exec rake test ${testargs[workbench]}
510 }
511 do_test workbench
512
513 test_workbench_performance() {
514     cd "$WORKSPACE/apps/workbench" \
515         && bundle exec rake test:benchmark
516 }
517 do_test workbench_performance
518
519 report_outcomes
520 clear_temp
521
522 exit ${#failures}