diff --git a/scripts/singularity_cmd b/scripts/singularity_cmd index 5dd6e123..f8c36325 100755 --- a/scripts/singularity_cmd +++ b/scripts/singularity_cmd @@ -34,7 +34,7 @@ set -eu function info() { - : # echo -e "I: $@" >&2 + : # echo -e "I: " "$@" >&2 } # @@ -72,17 +72,52 @@ pass_git_config "user.name" "ReproNim User" pass_git_config "user.email" "nobody@example.com" # Common arguments for the singularity run -SARGS=( -e -c -B "$PWD" -H "$BHOME" --pwd "$PWD" "$@" ) +SARGS=( -e -B "$PWD" -H "$BHOME" --pwd "$PWD" "$@" ) +# Due to https://github.com/sylabs/singularity/issues/3949 +# which was fixed only in v3.3.0-rc.1-431-g40331a5b1 +# for now we need to avoid using `-c` in such cases and +# just create those needed bind points manually and bind mount +# /tmp manually (apparently -W is not in effect really without -c). +# TODO: make it also check/depend on current version of singularity +need_no_c= +for d in "$PWD" "$updir"; do + if [ "${d##/tmp/}" != "$d" ]; then + # we should create that one within our $tmpdir + info "Creating $d under $tmpdir" + mkdir -p "$tmpdir/$d" "$tmpdir/var/tmp" + need_no_c=1 + fi +done + +if [ -z "$need_no_c" ]; then + SARGS=( -c "${SARGS[@]}" ) +else + SARGS=( -B "$tmpdir/tmp:/tmp" -B "$tmpdir/var/tmp:/var/tmp" "${SARGS[@]}" ) +fi + +# set -x if [ "$OSTYPE" == "linux-gnu" ] && [ -z "${REPRONIM_USE_DOCKER:-}" ]; then singularity "$cmd" -W "$tmpdir" "${SARGS[@]}" else - docker run --privileged --rm \ - -e "UID=$(id -u)" \ - -e "GID=$(id -g)" \ - -v "$PWD:$PWD" \ - -v "$BHOME:$BHOME" \ - -w "$PWD" \ - mjtravers/singularity-shim:latest \ - "$cmd" "${SARGS[@]}" + DARGS=( + --privileged + --rm + -e "UID=$(id -u)" + -e "GID=$(id -g)" + -v "$PWD:$PWD" + -v "$updir:$updir" + -v "$BHOME:$BHOME" + -w "$PWD") + + # Bind mount present /etc/localtime inside the docker container to be + # passed into singularity env + if [ -e "/etc/localtime" ]; then + DARGS=( "${DARGS[@]}" -v /etc/localtime ) + fi + + docker run \ + "${DARGS[@]}" \ + mjtravers/singularity-shim:latest \ + "$cmd" "${SARGS[@]}" fi diff --git a/scripts/tests/test_helpers.bash b/scripts/tests/test_helpers.bash new file mode 100644 index 00000000..ab0b36cf --- /dev/null +++ b/scripts/tests/test_helpers.bash @@ -0,0 +1,62 @@ +# Messaging +debug () { + [ -z "$DEBUG_BATS" ] || echo " DEBUG: $@" >&3 +} + +debug_run () { + debug "> lines=${lines[@]}" + debug "> STATUS=$status" +} + +fail_msg () { + echo -e "FAIL: $@" >&3 +} + +fail () { + fail_msg "$@" + exit 1 +} + +error () { + echo -e "ERROR: $@" >&3 + exit 2 +} + +# Assertion helpers + +assert_equal () { + if [ "$#" != 2 ]; then + error "Got $# arguments to eq whenever expected 2" + fi + if [ "$1" != "$2" ]; then + fail "Arguments are not equal.\n #1=<<$1>>\n #2=<<$2>>" + fi +} + +assert_python_re_match () { + if ! python -c 'import re, sys; assert re.match(sys.argv[1], sys.argv[2], flags=re.DOTALL)' "$1" "$2"; then + fail "<<$2>>\ndid not match\n<<$1>>" + fi +} + +assert_clean_exit () { + assert_equal "$status" 0 +} + + +# Misc helpers + +pull_singularity_shim () { + # make sure that we have our shim docker image so its pulling does not + # leak into output of scripts/singularity_cmd + if ! docker pull mjtravers/singularity-shim:latest; then + skip "Failed to pull singularity shim" + fi +} + +skip_if_travis_osx() { + if [ "$TRAVIS_OS_NAME" = osx ] + then + skip "$@" + fi +} diff --git a/scripts/tests/test_singularity_cmd.bats b/scripts/tests/test_singularity_cmd.bats index 3f6c6c82..c9f3e217 100755 --- a/scripts/tests/test_singularity_cmd.bats +++ b/scripts/tests/test_singularity_cmd.bats @@ -13,27 +13,96 @@ # bats test_singularity_cmd.bats # bats . +load test_helpers + +arg_test_img="$BATS_TEST_DIRNAME/arg-test.simg" +topdir="$BATS_TEST_DIRNAME/../.." + +cd "$BATS_TEST_DIRNAME" +git annex get "$arg_test_img" + + @test "verifying arguments passed to singularity_cmd Docker shim" { - # make sure that we have the singularity image - img="$BATS_TEST_DIRNAME/arg-test.simg" - cd "$BATS_TEST_DIRNAME" - git annex get "$img" - cd ../.. - # make sure that we have our shim docker image so its pulling does not - # leak into output of scripts/singularity_cmd - docker pull mjtravers/singularity-shim:latest - - export REPRONIM_USE_DOCKER=1 - run scripts/singularity_cmd \ - exec "$img" /singularity "foo bar" blah 45.5 /dir "bar;" "foo&" '${foo}' - echo "> STATUS=$status" >&3 - echo "> lines=${lines[@]}" >&3 - [ "$status" -eq 0 ] - [ "${lines[0]}" = 'arg #1=' ] - [ "${lines[1]}" = 'arg #2=' ] - [ "${lines[2]}" = 'arg #3=<45.5>' ] - [ "${lines[3]}" = 'arg #4=' ] - [ "${lines[4]}" = 'arg #5=' ] - [ "${lines[5]}" = 'arg #6=' ] - [ "${lines[6]}" = 'arg #7=<${foo}>' ] + pull_singularity_shim + + cd "$topdir" + export REPRONIM_USE_DOCKER=1 + run scripts/singularity_cmd \ + exec "$arg_test_img" /singularity "foo bar" blah 45.5 /dir "bar;" "foo&" '${foo}' + + debug_run + + assert_clean_exit + assert_equal "${lines[0]}" 'arg #1=' + assert_equal "${lines[1]}" 'arg #2=' + assert_equal "${lines[2]}" 'arg #3=<45.5>' + assert_equal "${lines[3]}" 'arg #4=' + assert_equal "${lines[4]}" 'arg #5=' + assert_equal "${lines[5]}" 'arg #6=' + assert_equal "${lines[6]}" 'arg #7=<${foo}>' +} + + +@test "verifying ability to singularity exec under /tmp subdir" { + skip_if_travis_osx "skipping Singularity directory test on Travis OSX" + check_subdir "$(_mktemp_dir_under /tmp)" +} + +@test "verifying ability to singularity exec under /tmp subdir (explicit use of docker)" { + skip_if_travis_osx "skipping Singularity directory test on Travis OSX" + export REPRONIM_USE_DOCKER=1 + check_subdir "$(_mktemp_dir_under /tmp)" +} + +@test "verifying ability to singularity exec under $HOME subdir" { + skip_if_travis_osx "skipping Singularity directory test on Travis OSX" + check_subdir "$(_mktemp_dir_under $HOME)" +} + +@test "verifying ability to singularity exec under $HOME subdir (explicit use of docker)" { + skip_if_travis_osx "skipping Singularity directory test on Travis OSX" + export REPRONIM_USE_DOCKER=1 + check_subdir "$(_mktemp_dir_under $HOME)" +} + + +_mktemp_dir_under () { + subdir="$1/$(mktemp -t "tmp dir.XXXXXX" -u | sed -e 's,.*/,,g')" + mkdir -p "$subdir" + debug "subdir: $subdir" + echo "$subdir" +} + +check_subdir () { + subdir="$1" + cd "$subdir" + echo "content" > "$subdir/file" + + # Our arg_test image has no /etc/localtime so singularity might complain + # about inability to bind mount that one + # \S* to swallow ANSI coloring + target_out=( '(\S*WARNING:\S* skipping mount of /etc/localtime: no such file or directory\n)?/tmp' +"$subdir" +"$subdir/file" +/var/tmp +content ) + + if [ -n "${REPRONIM_USE_DOCKER:-}" ]; then + pull_singularity_shim + fi + run_cmd="find /tmp" + if [ "${subdir##/tmp/}" = "$subdir" ]; then + # We are not under /tmp so find will not find our directory, add it explicitly to the list + # for find + run_cmd+=" \"$subdir\"" + fi + run_cmd+=" /var/tmp && cat \"$subdir/file\"" + run "$topdir/scripts/singularity_cmd" exec "$arg_test_img" sh -c "$run_cmd" + + rm -rf "$subdir" # cleanup asap + + debug_run + + assert_clean_exit + assert_python_re_match "${target_out[*]}" "${lines[*]}" } diff --git a/travis/dummy_docker b/travis/dummy_docker index d813f84d..6f7ed8d2 100755 --- a/travis/dummy_docker +++ b/travis/dummy_docker @@ -28,21 +28,25 @@ elif [ "$1" = "run" -a \ "$7" = "GID=20" -a \ "$8" = "-v" -a \ "${10}" = "-v" -a \ - "${12}" = "-w" -a \ - "${15}" = "exec" -a \ - "${16}" = "-e" -a \ - "${17}" = "-c" -a \ - "${18}" = "-B" -a \ - "${20}" = "-H" -a \ - "${22}" = "--pwd" -a \ - "${25}" = "/singularity" -a \ - "${26}" = "foo bar" -a \ - "${27}" = "blah" -a \ - "${28}" = "45.5" -a \ - "${29}" = "/dir" -a \ - "${30}" = "bar;" -a \ - "${31}" = "foo&" -a \ - "${32}" = "\${foo}" ] + "${12}" = "-v" -a \ + "${14}" = "-w" -a \ + "${16}" = "-v" -a \ + "${17}" = "/etc/localtime" -a \ + "${18}" = "mjtravers/singularity-shim:latest" -a \ + "${19}" = "exec" -a \ + "${20}" = "-c" -a \ + "${21}" = "-e" -a \ + "${22}" = "-B" -a \ + "${24}" = "-H" -a \ + "${26}" = "--pwd" -a \ + "${29}" = "/singularity" -a \ + "${30}" = "foo bar" -a \ + "${31}" = "blah" -a \ + "${32}" = "45.5" -a \ + "${33}" = "/dir" -a \ + "${34}" = "bar;" -a \ + "${35}" = "foo&" -a \ + "${36}" = "\${foo}" ] then cat << EOF arg #1=