diff --git a/test/clean.sh b/test/clean.sh index 06eb492766c74..8147a6e813ee4 100755 --- a/test/clean.sh +++ b/test/clean.sh @@ -1,4 +1,12 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -Eeuo pipefail -docker images 'librarytest/*' | awk 'NR>1 { print $1":"$2 }' | xargs -r docker rmi +docker image ls --digests --no-trunc --format ' + {{- if ne .Tag "" -}} + {{- .Repository -}} : {{- .Tag -}} + {{- else if ne .Digest "" -}} + {{- .Repository -}} @ {{- .Digest -}} + {{- else -}} + {{- .ID -}} + {{- end -}} +' 'librarytest/*' | xargs -rt docker image rm diff --git a/test/config.sh b/test/config.sh index a4f78b76b3d97..e95a6444c567b 100644 --- a/test/config.sh +++ b/test/config.sh @@ -11,8 +11,6 @@ globalTests+=( # for "explicit" images, only run tests that are explicitly specified for that image/variant explicitTests+=( [:onbuild]=1 - [:nanoserver]=1 - [:windowsservercore]=1 ) imageTests[:onbuild]+=' override-cmd @@ -272,6 +270,14 @@ globalExcludeTests+=( [swarm_utc]=1 [traefik_utc]=1 + # windows! + [:nanoserver_cve-2014--shellshock]=1 + [:nanoserver_no-hard-coded-passwords]=1 + [:nanoserver_utc]=1 + [:windowsservercore_cve-2014--shellshock]=1 + [:windowsservercore_no-hard-coded-passwords]=1 + [:windowsservercore_utc]=1 + [hello-world_no-hard-coded-passwords]=1 [nats_no-hard-coded-passwords]=1 [nats-streaming_no-hard-coded-passwords]=1 @@ -282,9 +288,10 @@ globalExcludeTests+=( # https://github.com/docker-library/official-images/pull/1721#issuecomment-234128477 [clearlinux_no-hard-coded-passwords]=1 - # alpine/slim openjdk images are headless and so can't do font stuff + # alpine/slim/nanoserver openjdk images are headless and so can't do font stuff [openjdk:alpine_java-uimanager-font]=1 [openjdk:slim_java-uimanager-font]=1 + [openjdk:nanoserver_java-uimanager-font]=1 # and adoptopenjdk has opted not to [adoptopenjdk_java-uimanager-font]=1 @@ -299,4 +306,10 @@ globalExcludeTests+=( # the Swift slim images are not expected to be able to run the swift-hello-world test because it involves compiling Swift code. The slim images are for running an already built binary. # https://github.com/docker-library/official-images/pull/6302#issuecomment-512181863 [swift:slim_swift-hello-world]=1 + + # TODO adjust MongoDB tests to use docker networks instead of links so they can work on Windows (and consider using PowerShell to generate appropriate certificates for TLS tests instead of openssl) + [mongo:windowsservercore_mongo-basics]=1 + [mongo:windowsservercore_mongo-auth-basics]=1 + [mongo:windowsservercore_mongo-tls-basics]=1 + [mongo:windowsservercore_mongo-tls-auth]=1 ) diff --git a/test/run.sh b/test/run.sh index 306c1f35f5633..1ebc9fcffd2a6 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -Eeuo pipefail dir="$(dirname "$(readlink -f "$BASH_SOURCE")")" @@ -46,7 +46,7 @@ while true; do esac done -if [ $# -eq 0 ]; then +if [ "$#" -eq 0 ]; then usage >&2 exit 1 fi @@ -59,10 +59,21 @@ declare -A globalExcludeTests=() declare -A explicitTests=() # if there are no user-specified configs, use the default config -if [ ${#configs} -eq 0 ]; then - configs+=("$dir/config.sh") +if [ "${#configs[@]}" -eq 0 ]; then + configs+=( "$dir/config.sh" ) fi +case "$(uname -o)" in + Msys) + # https://stackoverflow.com/a/34386471/433558 + # https://github.com/docker/toolbox/blob/6960f28d5b01857d69b2095a02949264f09d3e57/windows/start.sh#L104-L107 + docker() { + MSYS_NO_PATHCONV=1 command docker "$@" + } + export -f docker + ;; +esac + # load the configs declare -A testPaths=() for conf in "${configs[@]}"; do @@ -72,7 +83,7 @@ for conf in "${configs[@]}"; do confDir="$(dirname "$conf")" for testName in ${globalTests[@]} ${imageTests[@]}; do - [ "${testPaths[$testName]}" ] && continue + [ -n "${testPaths[$testName]:-}" ] && continue if [ -d "$confDir/tests/$testName" ]; then # Test directory found relative to the conf file @@ -116,11 +127,11 @@ for dockerImage in "$@"; do variant='psmdb' ;; *nanoserver*) - # all nanoserver variants are windows and should have explict tests + # all nanoserver variants are windows variant='nanoserver' ;; *windowsservercore*) - # all servercore variants are windows and should have explict tests + # all servercore variants are windows variant='windowsservercore' ;; esac @@ -131,10 +142,10 @@ for dockerImage in "$@"; do fi for possibleAlias in \ - "${testAlias[$repo:$variant]}" \ - "${testAlias[$repo]}" \ - "${testAlias[$testRepo:$variant]}" \ - "${testAlias[$testRepo]}" \ + "${testAlias[$repo:$variant]:-}" \ + "${testAlias[$repo]:-}" \ + "${testAlias[$testRepo:$variant]:-}" \ + "${testAlias[$testRepo]:-}" \ ; do if [ -n "$possibleAlias" ]; then testRepo="$possibleAlias" @@ -144,9 +155,9 @@ for dockerImage in "$@"; do explicitVariant= if [ \ - "${explicitTests[:$variant]}" \ - -o "${explicitTests[$repo:$variant]}" \ - -o "${explicitTests[$testRepo:$variant]}" \ + "${explicitTests[:$variant]:-}" \ + -o "${explicitTests[$repo:$variant]:-}" \ + -o "${explicitTests[$testRepo:$variant]:-}" \ ]; then explicitVariant=1 fi @@ -156,41 +167,41 @@ for dockerImage in "$@"; do testCandidates+=( "${globalTests[@]}" ) fi testCandidates+=( - ${imageTests[:$variant]} + ${imageTests[:$variant]:-} ) if [ -z "$explicitVariant" ]; then testCandidates+=( - ${imageTests[$testRepo]} + ${imageTests[$testRepo]:-} ) fi testCandidates+=( - ${imageTests[$testRepo:$variant]} + ${imageTests[$testRepo:$variant]:-} ) if [ "$testRepo" != "$repo" ]; then if [ -z "$explicitVariant" ]; then testCandidates+=( - ${imageTests[$repo]} + ${imageTests[$repo]:-} ) fi testCandidates+=( - ${imageTests[$repo:$variant]} + ${imageTests[$repo:$variant]:-} ) fi tests=() for t in "${testCandidates[@]}"; do - if [ ${#argTests[@]} -gt 0 -a -z "${argTests[$t]}" ]; then + if [ "${#argTests[@]}" -gt 0 ] && [ -z "${argTests[$t]:-}" ]; then # skipping due to -t continue fi if [ \ - ! -z "${globalExcludeTests[${testRepo}_$t]}" \ - -o ! -z "${globalExcludeTests[${testRepo}:${variant}_$t]}" \ - -o ! -z "${globalExcludeTests[:${variant}_$t]}" \ - -o ! -z "${globalExcludeTests[${repo}_$t]}" \ - -o ! -z "${globalExcludeTests[${repo}:${variant}_$t]}" \ - -o ! -z "${globalExcludeTests[:${variant}_$t]}" \ + ! -z "${globalExcludeTests[${testRepo}_$t]:-}" \ + -o ! -z "${globalExcludeTests[${testRepo}:${variant}_$t]:-}" \ + -o ! -z "${globalExcludeTests[:${variant}_$t]:-}" \ + -o ! -z "${globalExcludeTests[${repo}_$t]:-}" \ + -o ! -z "${globalExcludeTests[${repo}:${variant}_$t]:-}" \ + -o ! -z "${globalExcludeTests[:${variant}_$t]:-}" \ ]; then # skipping due to exclude continue @@ -223,10 +234,11 @@ for dockerImage in "$@"; do scriptDir="${testPaths[$t]}" if [ -d "$scriptDir" ]; then script="$scriptDir/run.sh" - if [ -x "$script" -a ! -d "$script" ]; then + if [ -x "$script" ] && [ ! -d "$script" ]; then # TODO dryRun logic - if output="$("$script" $dockerImage)"; then - if [ -f "$scriptDir/expected-std-out.txt" ] && ! d="$(echo "$output" | diff -u "$scriptDir/expected-std-out.txt" - 2>/dev/null)"; then + if output="$("$script" "$dockerImage")"; then + output="$(tr -d '\r' <<<"$output")" # Windows gives us \r\n ... :D + if [ -f "$scriptDir/expected-std-out.txt" ] && ! d="$(diff -u "$scriptDir/expected-std-out.txt" - <<<"$output" 2>/dev/null)"; then echo 'failed; unexpected output:' echo "$d" didFail=1 @@ -252,6 +264,6 @@ for dockerImage in "$@"; do done done -if [ "$didFail" ]; then +if [ -n "$didFail" ]; then exit 1 fi diff --git a/test/tests/docker-build.sh b/test/tests/docker-build.sh index 0f4f9762ecbbf..a3c2c73d1330c 100755 --- a/test/tests/docker-build.sh +++ b/test/tests/docker-build.sh @@ -30,4 +30,4 @@ fi cp -RL "$dir" "$tmp/dir" -docker build -t "$imageTag" "$tmp" > /dev/null +command docker build -t "$imageTag" "$tmp" > /dev/null diff --git a/test/tests/hylang-sh/container.hy b/test/tests/hylang-sh/container.hy index 0417d6fa5ce3e..4ef8e3e8fac3e 100644 --- a/test/tests/hylang-sh/container.hy +++ b/test/tests/hylang-sh/container.hy @@ -4,8 +4,14 @@ (import subprocess sys) (subprocess.check_call [sys.executable "-m" "pip" "install" "-q" "sh"]) +(import platform) + +(comment Windows is not supported by sh (sad day)) +(comment https://github.com/amoffat/sh/blob/608f4c3bf5ad75ad40035d03a9c5ffcce0898f07/sh.py#L33-L36) +(if (= (.system platform) "Windows") + (defn echo [dashn num] (return num)) + (import [sh [echo]])) -(import [sh [echo]]) (-> (+ (int (echo "-n" 21)) diff --git a/test/tests/mongo-basics/run.sh b/test/tests/mongo-basics/run.sh index 37ae447064bcd..e8d488a1868c2 100755 --- a/test/tests/mongo-basics/run.sh +++ b/test/tests/mongo-basics/run.sh @@ -22,31 +22,33 @@ if docker info --format '{{ join .SecurityOptions "\n" }}' 2>/dev/null |tac|tac| # need set_mempolicy syscall to be able to do numactl for mongodb # if "set_mempolicy" is not in the always allowed list, add it - extraSeccomp="$(echo "$seccomp" | docker run -i --rm "$jqImage" --tab ' - .syscalls[] |= if ( - .action == "SCMP_ACT_ALLOW" - and .args == [] - and .comment == "" - and .includes == {} - and .excludes == {} - ) then ( - if ( .names | index("set_mempolicy") ) > 0 then + extraSeccomp="$( + docker run -i --rm "$jqImage" --tab ' + .syscalls[] |= if ( + .action == "SCMP_ACT_ALLOW" + and .args == [] + and .comment == "" + and .includes == {} + and .excludes == {} + ) then ( + if ( .names | index("set_mempolicy") ) > 0 then + . + else ( + .names |= . + ["set_mempolicy"] + ) end + ) + else . - else ( - .names |= . + ["set_mempolicy"] - ) end - ) - else - . - end - ')" + end + ' <<<"$seccomp" + )" else echo >&2 'warning: the current Docker daemon does not appear to support seccomp' fi docker_run_seccomp() { if [ "$haveSeccomp" ]; then - docker run --security-opt seccomp=<(echo "$extraSeccomp") "$@" + docker run --security-opt seccomp=<(cat <<<"$extraSeccomp") "$@" else docker run "$@" fi diff --git a/test/tests/override-cmd/run.sh b/test/tests/override-cmd/run.sh index 1feba2cb7f948..e49da46ad1517 100755 --- a/test/tests/override-cmd/run.sh +++ b/test/tests/override-cmd/run.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -eo pipefail +#!/usr/bin/env bash +set -Eeuo pipefail image="$1" @@ -8,13 +8,22 @@ image="$1" hello="world-$RANDOM-$RANDOM" +cmd=( echo "Hello $hello" ) +case "$image" in + *windowsservercore* | *nanoserver*) + cmd=( cmd /Q /S /C "${cmd[*]}" ) + ;; +esac + # test first with --entrypoint to verify that we even have echo (tests for single-binary images FROM scratch, essentially) -if ! testOutput="$(docker run --rm --entrypoint echo "$image" "Hello $hello" 2>/dev/null)"; then +if ! testOutput="$(docker run --rm --entrypoint "${cmd[0]}" "$image" "${cmd[@]:1}" 2>/dev/null)"; then echo >&2 'image does not appear to contain "echo" -- assuming single-binary image' exit fi +testOutput="$(tr -d '\r' <<<"$testOutput")" # Windows gives us \r\n ... :D [ "$testOutput" = "Hello $hello" ] # now test with normal command to verify the default entrypoint is OK -output="$(docker run --rm "$image" echo "Hello $hello")" +output="$(docker run --rm "$image" "${cmd[@]}")" +output="$(tr -d '\r' <<<"$output")" # Windows gives us \r\n ... :D [ "$output" = "Hello $hello" ] diff --git a/test/tests/python-hy/container.cmd b/test/tests/python-hy/container.cmd new file mode 100644 index 0000000000000..2a15514ab5d74 --- /dev/null +++ b/test/tests/python-hy/container.cmd @@ -0,0 +1,28 @@ +@echo off + +for %%p in ( pypy3 pypy python3 python ) do ( + %%p --version >nul 2>&1 && ( + set python=%%p + goto found + ) +) +echo unable to run Hy test -- seems this image does not contain Python? >&2 +exit /b 1 + +:found +%python% --version >nul 2>&1 || exit /b %errorlevel% + +rem Hy is complicated, and uses Python's internal AST representation directly, and thus Hy releases usually lag behind a little on major Python releases (and we don't want that to gum up our tests) +rem see https://github.com/hylang/hy/issues/1111 for example breakage +%python% -c "import sys; exit((sys.version_info[0] == 3 and sys.version_info[1] >= 8) or sys.version_info[0] > 3)" || ( + echo skipping Hy test -- not allowed on Python 3.8+ ^(yet!^) >&2 + rem cheaters gunna cheat + type expected-std-out.txt + exit /b 0 +) + +pip install -q "hy==0.17.0" || exit /b %errorlevel% + +hy ./container.hy || exit /b %errorlevel% + +exit /b 0 diff --git a/test/tests/python-hy/container.sh b/test/tests/python-hy/container.sh index d9a5321415739..513a05dbfb49b 100644 --- a/test/tests/python-hy/container.sh +++ b/test/tests/python-hy/container.sh @@ -1,5 +1,5 @@ #!/bin/sh -set -e +set -eu python= for c in pypy3 pypy python3 python; do @@ -18,9 +18,11 @@ fi # see https://github.com/hylang/hy/issues/1111 for example breakage if ! "$python" -c 'import sys; exit((sys.version_info[0] == 3 and sys.version_info[1] >= 8) or sys.version_info[0] > 3)'; then echo >&2 'skipping Hy test -- not allowed on Python 3.8+ (yet!)' - cat expected-std-out.txt # cheaters gunna cheat + # cheaters gunna cheat + cat expected-std-out.txt exit fi -pip install -q 'hy==0.16.0' +pip install -q 'hy==0.17.0' + hy ./container.hy diff --git a/test/tests/python-imports/container.py b/test/tests/python-imports/container.py index f73704a096295..e69e0676cc208 100644 --- a/test/tests/python-imports/container.py +++ b/test/tests/python-imports/container.py @@ -1,23 +1,25 @@ -import curses -import readline - -import bz2 -assert(bz2.decompress(bz2.compress(b'IT WORKS IT WORKS IT WORKS')) == b'IT WORKS IT WORKS IT WORKS') - import platform +isWindows = platform.system() == 'Windows' isNotPypy = platform.python_implementation() != 'PyPy' isCaveman = platform.python_version_tuple()[0] == '2' -if isCaveman: - import gdbm -else: - import dbm.gnu +if not isWindows: + import curses + import readline - if isNotPypy: - # PyPy and Python 2 don't support lzma - import lzma - assert(lzma.decompress(lzma.compress(b'IT WORKS IT WORKS IT WORKS')) == b'IT WORKS IT WORKS IT WORKS') + if isCaveman: + import gdbm + else: + import dbm.gnu + + if isNotPypy: + # PyPy and Python 2 don't support lzma + import lzma + assert(lzma.decompress(lzma.compress(b'IT WORKS IT WORKS IT WORKS')) == b'IT WORKS IT WORKS IT WORKS') + +import bz2 +assert(bz2.decompress(bz2.compress(b'IT WORKS IT WORKS IT WORKS')) == b'IT WORKS IT WORKS IT WORKS') import zlib assert(zlib.decompress(zlib.compress(b'IT WORKS IT WORKS IT WORKS')) == b'IT WORKS IT WORKS IT WORKS') diff --git a/test/tests/run-in-container.sh b/test/tests/run-in-container.sh index 5bea5f1278244..f2680b8b472c0 100755 --- a/test/tests/run-in-container.sh +++ b/test/tests/run-in-container.sh @@ -1,11 +1,11 @@ -#!/bin/bash -set -eo pipefail +#!/usr/bin/env bash +set -Eeuo pipefail # NOT INTENDED TO BE USED AS A TEST "run.sh" DIRECTLY # SEE OTHER "run-*-in-container.sh" SCRIPTS FOR USAGE # arguments to docker -args=() +args=( --rm ) opts="$(getopt -o '+' --long 'docker-arg:' -- "$@")" eval set -- "$opts" @@ -34,7 +34,8 @@ entrypoint="$1" shift # do some fancy footwork so that if testDir is /a/b/c, we mount /a/b and use c as the working directory (so relative symlinks work one level up) -thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +thisDir="$(readlink -f "$BASH_SOURCE")" +thisDir="$(dirname "$thisDir")" testDir="$(readlink -f "$testDir")" testBase="$(basename "$testDir")" hostMount="$(dirname "$testDir")" @@ -47,15 +48,12 @@ newImage="$("$thisDir/image-name.sh" librarytest/run-in-container "$image--$test FROM $image COPY dir $containerMount WORKDIR $workdir -ENTRYPOINT ["$entrypoint"] EOD -args+=( --rm ) - # there is strong potential for nokogiri+overlayfs failure # see https://github.com/docker-library/ruby/issues/55 -gemHome="$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$newImage" | awk -F '=' '$1 == "GEM_HOME" { print $2; exit }')" -if [ "$gemHome" ]; then +gemHome="$(docker image inspect --format '{{- range .Config.Env -}}{{- println . -}}{{- end -}}' "$newImage" | awk -F '=' '$1 == "GEM_HOME" { print $2; exit }')" +if [ -n "$gemHome" ]; then # must be a Ruby image driver="$(docker info --format '{{ .Driver }}' 2>/dev/null)" if [ "$driver" = 'overlay' ]; then @@ -64,4 +62,10 @@ if [ "$gemHome" ]; then fi fi -exec docker run "${args[@]}" "$newImage" "$@" +args+=( --entrypoint "$entrypoint" ) + +# we can't use "exec" here because Windows needs to override "docker" with a function that sets "MSYS_NO_PATHCONV" (see "test/run.sh" for where that's defined) +if ! docker run "${args[@]}" "$newImage" "$@"; then + exit 1 +fi +exit 0 diff --git a/test/tests/run-java-in-container.sh b/test/tests/run-java-in-container.sh index 783e5b9e58b3f..3c508592dafdc 100755 --- a/test/tests/run-java-in-container.sh +++ b/test/tests/run-java-in-container.sh @@ -1,5 +1,5 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -Eeuo pipefail testDir="$(readlink -f "$(dirname "$BASH_SOURCE")")" runDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" @@ -8,21 +8,15 @@ image="$1" # TODO make this work for ibmjava too (jre or sfj -> sdk) jdk="${image/jre/jdk}" -volume="$(docker volume create)" -trap "docker volume rm '$volume' &> /dev/null" EXIT +newImage="$("$runDir/image-name.sh" librarytest/java-hello-world "$image")" +"$runDir/docker-build.sh" "$testDir" "$newImage" <nul 2>&1 && echo %p && exit 0 )' | tr -d '\r')" + python="${python% }" # "echo %p && ..." will print the trailing space because cmd... + if [ -z "$python" ]; then + echo >&2 'error: unable to determine how to run python' + exit 1 fi - done - echo >&2 "error: unable to determine how to run python" - exit 1 -' -- ./container.py + source "$runDir/run-in-container.sh" "$testDir" "$1" "$python" container.py + ;; + + *) + source "$runDir/run-in-container.sh" "$testDir" "$1" sh -ec ' + for c in pypy3 pypy python3 python; do + if [ -x "/usr/local/bin/$c" ]; then + exec "/usr/local/bin/$c" "$@" + fi + done + echo >&2 "error: unable to determine how to run python" + exit 1 + ' -- ./container.py + ;; +esac diff --git a/test/tests/run-sh-in-container.sh b/test/tests/run-sh-in-container.sh index a4c7c19dff4dd..d233bee689e84 100755 --- a/test/tests/run-sh-in-container.sh +++ b/test/tests/run-sh-in-container.sh @@ -1,7 +1,19 @@ -#!/bin/bash -set -e +#!/usr/bin/env bash +set -Eeuo pipefail -testDir="$(readlink -f "$(dirname "$BASH_SOURCE")")" -runDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +testDir="$(dirname "$BASH_SOURCE")" +testDir="$(readlink -f "$testDir")" +runDir="$(readlink -f "$BASH_SOURCE")" +runDir="$(dirname "$runDir")" -source "$runDir/run-in-container.sh" "$testDir" "$1" sh ./container.sh +case "$1" in + *windowsservercore* | *nanoserver*) + [ -f "$testDir/container.cmd" ] + source "$runDir/run-in-container.sh" "$testDir" "$1" cmd /Q /S /C '.\container.cmd' + ;; + + *) + [ -f "$testDir/container.sh" ] + source "$runDir/run-in-container.sh" "$testDir" "$1" sh ./container.sh + ;; +esac