11308: Merge branch 'master' into 11308-python3
[arvados.git] / build / run-tests.sh
index 2c288179529bcdfa15d44f1c10fd68a409908012..9649d7bb838b04d5351d75f20b8de0e4e28b319d 100755 (executable)
@@ -22,6 +22,8 @@ Options:
 --leave-temp   Do not remove GOPATH, virtualenv, and other temp dirs at exit.
                Instead, show the path to give as --temp to reuse them in
                subsequent invocations.
+--repeat N     Repeat each install/test step until it succeeds N times.
+--retry        Prompt to retry if an install or test suite fails.
 --skip-install Do not run any install steps. Just run tests.
                You should provide GOPATH, GEMHOME, and VENVDIR options
                from a previous invocation if you use this option.
@@ -83,14 +85,17 @@ services/ws
 sdk/cli
 sdk/pam
 sdk/python
+sdk/python:py3
 sdk/ruby
 sdk/go/arvados
 sdk/go/arvadosclient
+sdk/go/dispatch
 sdk/go/keepclient
 sdk/go/httpserver
 sdk/go/manifest
 sdk/go/blockdigest
 sdk/go/streamer
+sdk/go/stats
 sdk/go/crunchrunner
 sdk/cwl
 tools/crunchstat-summary
@@ -118,7 +123,7 @@ GEMHOME=
 PERLINSTALLBASE=
 
 short=
-skip_install=
+only_install=
 temp=
 temp_preserve=
 
@@ -157,6 +162,12 @@ sanity_checks() {
     echo -n 'virtualenv: '
     virtualenv --version \
         || fatal "No virtualenv. Try: apt-get install virtualenv (on ubuntu: python-virtualenv)"
+    echo -n 'ruby: '
+    ruby -v \
+        || fatal "No ruby. Install >=2.1.9 (using rbenv, rvm, or source)"
+    echo -n 'bundler: '
+    bundle version \
+        || fatal "No bundler. Try: gem install bundler"
     echo -n 'go: '
     go version \
         || fatal "No go binary. See http://golang.org/doc/install"
@@ -168,6 +179,9 @@ sanity_checks() {
     echo -n 'fuse.h: '
     find /usr/include -wholename '*fuse/fuse.h' \
         || fatal "No fuse/fuse.h. Try: apt-get install libfuse-dev"
+    echo -n 'gnutls.h: '
+    find /usr/include -wholename '*gnutls/gnutls.h' \
+        || fatal "No gnutls/gnutls.h. Try: apt-get install libgnutls28-dev"
     echo -n 'pyconfig.h: '
     find /usr/include -name pyconfig.h | egrep --max-count=1 . \
         || fatal "No pyconfig.h. Try: apt-get install python-dev"
@@ -212,14 +226,7 @@ do
             exit 1
             ;;
         --skip)
-            skipwhat="$1"; shift
-            if [[ "$skipwhat" == "apps/workbench" ]]; then
-              skip["apps/workbench_units"]=1
-              skip["apps/workbench_functionals"]=1
-              skip["apps/workbench_integration"]=1
-            else
-              skip[$skipwhat]=1
-            fi
+            skip[$1]=1; shift
             ;;
         --only)
             only="$1"; skip[$1]=""; shift
@@ -228,10 +235,9 @@ do
             short=1
             ;;
         --skip-install)
-            skip_install=1
+            only_install=nothing
             ;;
         --only-install)
-            skip_install=1
             only_install="$1"; shift
             ;;
         --temp)
@@ -241,6 +247,9 @@ do
         --leave-temp)
             temp_preserve=1
             ;;
+        --repeat)
+            repeat=$((${1}+0)); shift
+            ;;
         --retry)
             retry=1
             ;;
@@ -450,30 +459,37 @@ pip freeze 2>/dev/null | egrep ^apache-libcloud==$LIBCLOUD_PIN \
     || pip install --pre --ignore-installed https://github.com/curoverse/libcloud/archive/apache-libcloud-$LIBCLOUD_PIN.zip >/dev/null \
     || fatal "pip install apache-libcloud failed"
 
-# This will help people who reuse --temp dirs when we upgrade to llfuse 0.42
-if egrep -q 'llfuse.*>= *0\.42' "$WORKSPACE/services/fuse/setup.py"; then
-    # Uninstall old llfuse, because services/fuse "pip install" won't
-    # upgrade it by default.
-    if pip freeze | egrep '^llfuse==0\.41\.'; then
-        yes | pip uninstall 'llfuse<0.42'
-    fi
+# Uninstall old llfuse (<1.0), because services/fuse "pip install"
+# won't upgrade it by default.
+if pip freeze | egrep '^llfuse==0'; then
+    yes | pip uninstall 'llfuse<1.0'
 fi
 
 # Deactivate Python 2 virtualenv
 deactivate
 
+declare -a pythonstuff
+pythonstuff=(
+    sdk/pam
+    sdk/python
+    sdk/python:py3
+    sdk/cwl
+    services/dockercleaner:py3
+    services/fuse
+    services/nodemanager
+    tools/crunchstat-summary
+    )
+
 # If Python 3 is available, set up its virtualenv in $VENV3DIR.
 # Otherwise, skip dependent tests.
 PYTHON3=$(which python3)
-if [ "0" = "$?" ]; then
+if [[ ${?} = 0 ]]; then
     setup_virtualenv "$VENV3DIR" --python python3
 else
     PYTHON3=
-    skip[services/dockercleaner]=1
     cat >&2 <<EOF
 
-Warning: python3 could not be found
-services/dockercleaner install and tests will be skipped
+Warning: python3 could not be found. Python 3 tests will be skipped.
 
 EOF
 fi
@@ -493,136 +509,151 @@ then
 fi
 
 retry() {
-    while ! ${@} && [[ "$retry" == 1 ]]
+    remain="${repeat}"
+    while :
     do
-        read -p 'Try again? [Y/n] ' x
-        if [[ "$x" != "y" ]] && [[ "$x" != "" ]]
-        then
+        if ${@}; then
+            if [[ "$remain" -gt 1 ]]; then
+                remain=$((${remain}-1))
+                title "Repeating ${remain} more times"
+            else
+                break
+            fi
+        elif [[ "$retry" == 1 ]]; then
+            read -p 'Try again? [Y/n] ' x
+            if [[ "$x" != "y" ]] && [[ "$x" != "" ]]
+            then
+                break
+            fi
+        else
             break
         fi
     done
 }
 
 do_test() {
-    retry do_test_once ${@}
+    case "${1}" in
+        apps/workbench_units | apps/workbench_functionals | apps/workbench_integration)
+            suite=apps/workbench
+            ;;
+        *)
+            suite="${1}"
+            ;;
+    esac
+    if [[ -z "${skip[$suite]}" && -z "${skip[$1]}" && \
+                (-z "${only}" || "${only}" == "${suite}" || \
+                 "${only}" == "${1}") ]]; then
+        retry do_test_once ${@}
+    else
+        title "Skipping ${1} tests"
+    fi
 }
 
 do_test_once() {
     unset result
-    to_test=$1
-    if (( [[ "$only" == "apps/workbench" ]] ) &&
-       ( [[ "$to_test" == "apps/workbench_units" ]] || [[ "$to_test" == "apps/workbench_functionals" ]] ||
-         [[ "$to_test" == "apps/workbench_integration" ]])); then
-      to_test="apps/workbench"
-    fi
-    if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$to_test" ]] )
+
+    title "Running $1 tests"
+    timer_reset
+    if [[ "$2" == "go" ]]
     then
-        title "Running $1 tests"
-        timer_reset
-        if [[ "$2" == "go" ]]
-        then
-            covername="coverage-$(echo "$1" | sed -e 's/\//_/g')"
-            coverflags=("-covermode=count" "-coverprofile=$WORKSPACE/tmp/.$covername.tmp")
-            # We do "go get -t" here to catch compilation errors
-            # before trying "go test". Otherwise, coverage-reporting
-            # mode makes Go show the wrong line numbers when reporting
-            # compilation errors.
-            go get -t "git.curoverse.com/arvados.git/$1" || return 1
-            cd "$WORKSPACE/$1" || return 1
-            gofmt -e -d . | egrep . && result=1
+        covername="coverage-$(echo "$1" | sed -e 's/\//_/g')"
+        coverflags=("-covermode=count" "-coverprofile=$WORKSPACE/tmp/.$covername.tmp")
+        # We do "go get -t" here to catch compilation errors
+        # before trying "go test". Otherwise, coverage-reporting
+        # mode makes Go show the wrong line numbers when reporting
+        # compilation errors.
+        go get -t "git.curoverse.com/arvados.git/$1" && \
+            cd "$WORKSPACE/$1" && \
+            [[ -z "$(gofmt -e -d . | tee /dev/stderr)" ]] && \
             if [[ -n "${testargs[$1]}" ]]
-            then
-                # "go test -check.vv giturl" doesn't work, but this
-                # does:
-                cd "$WORKSPACE/$1" && go test ${short:+-short} ${testargs[$1]}
-            else
-                # The above form gets verbose even when testargs is
-                # empty, so use this form in such cases:
-                go test ${short:+-short} ${coverflags[@]} "git.curoverse.com/arvados.git/$1"
-            fi
-            result=${result:-$?}
-            if [[ -f "$WORKSPACE/tmp/.$covername.tmp" ]]
-            then
-                go tool cover -html="$WORKSPACE/tmp/.$covername.tmp" -o "$WORKSPACE/tmp/$covername.html"
-                rm "$WORKSPACE/tmp/.$covername.tmp"
-            fi
-        elif [[ "$2" == "pip" ]]
         then
-            tries=0
-            cd "$WORKSPACE/$1" && while :
-            do
-                tries=$((${tries}+1))
-                # $3 can name a path directory for us to use, including trailing
-                # slash; e.g., the bin/ subdirectory of a virtualenv.
-                "${3}python" setup.py ${short:+--short-tests-only} test ${testargs[$1]}
-                result=$?
-                if [[ ${tries} < 3 && ${result} == 137 ]]
-                then
-                    printf '\n*****\n%s tests killed -- retrying\n*****\n\n' "$1"
-                    continue
-                else
-                    break
-                fi
-            done
-        elif [[ "$2" != "" ]]
-        then
-            "test_$2"
+            # "go test -check.vv giturl" doesn't work, but this
+            # does:
+            go test ${short:+-short} ${testargs[$1]}
         else
-            "test_$1"
+            # The above form gets verbose even when testargs is
+            # empty, so use this form in such cases:
+            go test ${short:+-short} ${coverflags[@]} "git.curoverse.com/arvados.git/$1"
         fi
         result=${result:-$?}
-        checkexit $result "$1 tests"
-        title "End of $1 tests (`timer`)"
-        return $result
+        if [[ -f "$WORKSPACE/tmp/.$covername.tmp" ]]
+        then
+            go tool cover -html="$WORKSPACE/tmp/.$covername.tmp" -o "$WORKSPACE/tmp/$covername.html"
+            rm "$WORKSPACE/tmp/.$covername.tmp"
+        fi
+    elif [[ "$2" == "pip" ]]
+    then
+        tries=0
+        cd "$WORKSPACE/$1" && while :
+        do
+            tries=$((${tries}+1))
+            # $3 can name a path directory for us to use, including trailing
+            # slash; e.g., the bin/ subdirectory of a virtualenv.
+            "${3}python" setup.py ${short:+--short-tests-only} test ${testargs[$1]}
+            result=$?
+            if [[ ${tries} < 3 && ${result} == 137 ]]
+            then
+                printf '\n*****\n%s tests killed -- retrying\n*****\n\n' "$1"
+                continue
+            else
+                break
+            fi
+        done
+    elif [[ "$2" != "" ]]
+    then
+        "test_$2"
     else
-        title "Skipping $1 tests"
+        "test_$1"
     fi
+    result=${result:-$?}
+    checkexit $result "$1 tests"
+    title "End of $1 tests (`timer`)"
+    return $result
 }
 
 do_install() {
-    retry do_install_once ${@}
+    if [[ -z "${only_install}" || "${only_install}" == "${1}" ]]; then
+        retry do_install_once ${@}
+    else
+        title "Skipping $1 install"
+    fi
 }
 
 do_install_once() {
-    if [[ -z "$skip_install" || (-n "$only_install" && "$only_install" == "$1") ]]
+    title "Running $1 install"
+    timer_reset
+    if [[ "$2" == "go" ]]
     then
-        title "Running $1 install"
-        timer_reset
-        if [[ "$2" == "go" ]]
-        then
-            go get -t "git.curoverse.com/arvados.git/$1"
-        elif [[ "$2" == "pip" ]]
-        then
-            # $3 can name a path directory for us to use, including trailing
-            # slash; e.g., the bin/ subdirectory of a virtualenv.
-
-            # Need to change to a different directory after creating
-            # the source dist package to avoid a pip bug.
-            # see https://arvados.org/issues/5766 for details.
-
-            # Also need to install twice, because if it believes the package is
-            # already installed, pip it won't install it.  So the first "pip
-            # install" ensures that the dependencies are met, the second "pip
-            # install" ensures that we've actually installed the local package
-            # we just built.
-            cd "$WORKSPACE/$1" \
-                && "${3}python" setup.py sdist rotate --keep=1 --match .tar.gz \
-                && cd "$WORKSPACE" \
-                && "${3}pip" install --quiet "$WORKSPACE/$1/dist"/*.tar.gz \
-                && "${3}pip" install --quiet --no-deps --ignore-installed "$WORKSPACE/$1/dist"/*.tar.gz
-        elif [[ "$2" != "" ]]
-        then
-            "install_$2"
-        else
-            "install_$1"
-        fi
-        result=$?
-        checkexit $result "$1 install"
-        title "End of $1 install (`timer`)"
-        return $result
+        go get -t "git.curoverse.com/arvados.git/$1"
+    elif [[ "$2" == "pip" ]]
+    then
+        # $3 can name a path directory for us to use, including trailing
+        # slash; e.g., the bin/ subdirectory of a virtualenv.
+
+        # Need to change to a different directory after creating
+        # the source dist package to avoid a pip bug.
+        # see https://arvados.org/issues/5766 for details.
+
+        # Also need to install twice, because if it believes the package is
+        # already installed, pip it won't install it.  So the first "pip
+        # install" ensures that the dependencies are met, the second "pip
+        # install" ensures that we've actually installed the local package
+        # we just built.
+        cd "$WORKSPACE/$1" \
+            && "${3}python" setup.py sdist rotate --keep=1 --match .tar.gz \
+            && cd "$WORKSPACE" \
+            && "${3}pip" install --quiet "$WORKSPACE/$1/dist"/*.tar.gz \
+            && "${3}pip" install --quiet --no-deps --ignore-installed "$WORKSPACE/$1/dist"/*.tar.gz
+    elif [[ "$2" != "" ]]
+    then
+        "install_$2"
     else
-        title "Skipping $1 install"
+        "install_$1"
     fi
+    result=$?
+    checkexit $result "$1 install"
+    title "End of $1 install (`timer`)"
+    return $result
 }
 
 bundle_install_trylocal() {
@@ -681,22 +712,19 @@ do_install services/login-sync login-sync
 # module. We can't actually *test* the Python SDK yet though, because
 # its own test suite brings up some of those other programs (like
 # keepproxy).
-declare -a pythonstuff
-pythonstuff=(
-    sdk/pam
-    sdk/python
-    sdk/cwl
-    services/fuse
-    services/nodemanager
-    tools/crunchstat-summary
-    )
 for p in "${pythonstuff[@]}"
 do
-    do_install "$p" pip
+    dir=${p%:py3}
+    if [[ ${dir} = ${p} ]]; then
+        if [[ -z ${skip[python2]} ]]; then
+            do_install ${dir} pip
+        fi
+    elif [[ -n ${PYTHON3} ]]; then
+        if [[ -z ${skip[python3]} ]]; then
+            do_install ${dir} pip "$VENV3DIR/bin/"
+        fi
+    fi
 done
-if [ -n "$PYTHON3" ]; then
-    do_install services/dockercleaner pip "$VENV3DIR/bin/"
-fi
 
 install_apiserver() {
     cd "$WORKSPACE/services/api" \
@@ -707,33 +735,12 @@ install_apiserver() {
 
     if [ -n "$CONFIGSRC" ]
     then
-        for f in database.yml application.yml
+        for f in database.yml
         do
             cp "$CONFIGSRC/$f" config/ || fatal "$f"
         done
     fi
 
-    # Fill in a random secret_token and blob_signing_key for testing
-    SECRET_TOKEN=`echo 'puts rand(2**512).to_s(36)' |ruby`
-    BLOB_SIGNING_KEY=`echo 'puts rand(2**512).to_s(36)' |ruby`
-
-    sed -i'' -e "s:SECRET_TOKEN:$SECRET_TOKEN:" config/application.yml
-    sed -i'' -e "s:BLOB_SIGNING_KEY:$BLOB_SIGNING_KEY:" config/application.yml
-
-    # Set up empty git repo (for git tests)
-    GITDIR=$(mktemp -d)
-    sed -i'' -e "s:/var/cache/git:$GITDIR:" config/application.default.yml
-
-    rm -rf $GITDIR
-    mkdir -p $GITDIR/test
-    cd $GITDIR/test \
-        && git init \
-        && git config user.email "jenkins@ci.curoverse.com" \
-        && git config user.name "Jenkins, CI" \
-        && touch tmp \
-        && git add tmp \
-        && git commit -m 'initial commit'
-
     # Clear out any lingering postgresql connections to the test
     # database, so that we can drop it. This assumes the current user
     # is a postgresql superuser.
@@ -741,6 +748,8 @@ install_apiserver() {
         && test_database=$(python -c "import yaml; print yaml.load(file('config/database.yml'))['test']['database']") \
         && psql "$test_database" -c "SELECT pg_terminate_backend (pg_stat_activity.procpid::int) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$test_database';" 2>/dev/null
 
+    mkdir -p "$WORKSPACE/services/api/tmp/pids"
+
     cd "$WORKSPACE/services/api" \
         && RAILS_ENV=test bundle exec rake db:drop \
         && RAILS_ENV=test bundle exec rake db:setup \
@@ -753,10 +762,12 @@ gostuff=(
     sdk/go/arvados
     sdk/go/arvadosclient
     sdk/go/blockdigest
+    sdk/go/dispatch
     sdk/go/httpserver
     sdk/go/manifest
     sdk/go/streamer
     sdk/go/crunchrunner
+    sdk/go/stats
     lib/crunchstat
     services/arv-git-httpd
     services/crunchstat
@@ -765,10 +776,6 @@ gostuff=(
     sdk/go/keepclient
     services/keep-balance
     services/keepproxy
-    services/datamanager/summary
-    services/datamanager/collection
-    services/datamanager/keep
-    services/datamanager
     services/crunch-dispatch-local
     services/crunch-dispatch-slurm
     services/crunch-run
@@ -789,6 +796,8 @@ install_workbench() {
 }
 do_install apps/workbench workbench
 
+unset http_proxy https_proxy no_proxy
+
 test_doclinkchecker() {
     (
         set -e
@@ -840,9 +849,17 @@ do_test services/login-sync login-sync
 
 for p in "${pythonstuff[@]}"
 do
-    do_test "$p" pip
+    dir=${p%:py3}
+    if [[ ${dir} = ${p} ]]; then
+        if [[ -z ${skip[python2]} ]]; then
+            do_test ${dir} pip
+        fi
+    elif [[ -n ${PYTHON3} ]]; then
+        if [[ -z ${skip[python3]} ]]; then
+            do_test ${dir} pip "$VENV3DIR/bin/"
+        fi
+    fi
 done
-do_test services/dockercleaner pip "$VENV3DIR/bin/"
 
 for g in "${gostuff[@]}"
 do