run.sh 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. dir="$(dirname "$(readlink -f "$BASH_SOURCE")")"
  4. self="$(basename "$0")"
  5. usage() {
  6. cat <<EOUSAGE
  7. usage: $self [-t test ...] image:tag [...]
  8. ie: $self debian:trixie
  9. $self -t utc python:3
  10. $self -t utc python:3 -t python-imports
  11. This script processes the specified Docker images to test their running
  12. environments.
  13. EOUSAGE
  14. }
  15. # arg handling
  16. opts="$(getopt -o 'ht:c:?' --long 'dry-run,help,test:,config:,keep-namespace' -- "$@" || { usage >&2 && false; })"
  17. eval set -- "$opts"
  18. declare -A argTests=()
  19. declare -a configs=()
  20. dryRun=
  21. keepNamespace=
  22. while true; do
  23. flag=$1
  24. shift
  25. case "$flag" in
  26. --dry-run) dryRun=1 ;;
  27. --help|-h|'-?') usage && exit 0 ;;
  28. --test|-t) argTests["$1"]=1 && shift ;;
  29. --config|-c) configs+=("$(readlink -f "$1")") && shift ;;
  30. --keep-namespace) keepNamespace=1 ;;
  31. --) break ;;
  32. *)
  33. {
  34. echo "error: unknown flag: $flag"
  35. usage
  36. } >&2
  37. exit 1
  38. ;;
  39. esac
  40. done
  41. if [ "$#" -eq 0 ]; then
  42. usage >&2
  43. exit 1
  44. fi
  45. # declare configuration variables
  46. declare -a globalTests=()
  47. declare -A testAlias=()
  48. declare -A imageTests=()
  49. declare -A globalExcludeTests=()
  50. declare -A explicitTests=()
  51. # if there are no user-specified configs, use the default config
  52. if [ "${#configs[@]}" -eq 0 ]; then
  53. configs+=( "$dir/config.sh" )
  54. fi
  55. case "$(uname -o)" in
  56. Msys)
  57. # https://stackoverflow.com/a/34386471/433558
  58. # https://github.com/docker/toolbox/blob/6960f28d5b01857d69b2095a02949264f09d3e57/windows/start.sh#L104-L107
  59. docker() {
  60. MSYS_NO_PATHCONV=1 command docker "$@"
  61. }
  62. export -f docker
  63. ;;
  64. esac
  65. # load the configs
  66. declare -A testPaths=()
  67. for conf in "${configs[@]}"; do
  68. . "$conf"
  69. # Determine the full path to any newly-declared tests
  70. confDir="$(dirname "$conf")"
  71. for testName in ${globalTests[@]} ${imageTests[@]}; do
  72. [ -n "${testPaths[$testName]:-}" ] && continue
  73. if [ -d "$confDir/tests/$testName" ]; then
  74. # Test directory found relative to the conf file
  75. testPaths[$testName]="$confDir/tests/$testName"
  76. elif [ -d "$dir/tests/$testName" ]; then
  77. # Test directory found in the main tests/ directory
  78. testPaths[$testName]="$dir/tests/$testName"
  79. fi
  80. done
  81. done
  82. didFail=
  83. for dockerImage in "$@"; do
  84. echo "testing $dockerImage"
  85. repo="${dockerImage%:*}"
  86. tagVar="${dockerImage#*:}"
  87. #version="${tagVar%-*}"
  88. variant="${tagVar##*-}"
  89. case "$tagVar" in
  90. *onbuild*)
  91. # "maven:onbuild-alpine" is still onbuild
  92. # ONCE ONBUILD, ALWAYS ONBUILD
  93. variant='onbuild'
  94. ;;
  95. *apache-*)
  96. # lolPHP
  97. variant='apache'
  98. ;;
  99. *fpm-*)
  100. # lolPHP
  101. variant='fpm'
  102. ;;
  103. *alpine*)
  104. # "alpine3.4", "alpine3.6", "nginx:alpine-perl", etc are still "alpine" variants
  105. variant='alpine'
  106. ;;
  107. slim-*|*-slim-*)
  108. # "slim-trixie" is still "slim"
  109. variant='slim'
  110. ;;
  111. psmdb-*)
  112. # Percona Server for MongoDB is still "mongo"
  113. variant='psmdb'
  114. ;;
  115. *nanoserver*)
  116. # all nanoserver variants are windows
  117. variant='nanoserver'
  118. ;;
  119. *windowsservercore*)
  120. # all servercore variants are windows
  121. variant='windowsservercore'
  122. ;;
  123. esac
  124. testRepo="$repo"
  125. if [ -z "$keepNamespace" ]; then
  126. testRepo="${testRepo##*/}"
  127. fi
  128. for possibleAlias in \
  129. "${testAlias[$repo:$variant]:-}" \
  130. "${testAlias[$repo]:-}" \
  131. "${testAlias[$testRepo:$variant]:-}" \
  132. "${testAlias[$testRepo]:-}" \
  133. ; do
  134. if [ -n "$possibleAlias" ]; then
  135. testRepo="$possibleAlias"
  136. break
  137. fi
  138. done
  139. explicitVariant=
  140. for possibleExplicit in \
  141. "${explicitTests[:$variant]:-}" \
  142. "${explicitTests[$repo:$variant]:-}" \
  143. "${explicitTests[$repo]:-}" \
  144. "${explicitTests[$testRepo:$variant]:-}" \
  145. "${explicitTests[$testRepo]:-}" \
  146. ; do
  147. if [ -n "$possibleExplicit" ]; then
  148. explicitVariant=1
  149. break
  150. fi
  151. done
  152. testCandidates=()
  153. if [ -z "$explicitVariant" ]; then
  154. testCandidates+=( "${globalTests[@]}" )
  155. fi
  156. testCandidates+=(
  157. ${imageTests[:$variant]:-}
  158. )
  159. if [ -z "$explicitVariant" ]; then
  160. testCandidates+=(
  161. ${imageTests[$testRepo]:-}
  162. )
  163. fi
  164. testCandidates+=(
  165. ${imageTests[$testRepo:$variant]:-}
  166. )
  167. if [ "$testRepo" != "$repo" ]; then
  168. if [ -z "$explicitVariant" ]; then
  169. testCandidates+=(
  170. ${imageTests[$repo]:-}
  171. )
  172. fi
  173. testCandidates+=(
  174. ${imageTests[$repo:$variant]:-}
  175. )
  176. fi
  177. tests=()
  178. for t in "${testCandidates[@]}"; do
  179. if [ "${#argTests[@]}" -gt 0 ] && [ -z "${argTests[$t]:-}" ]; then
  180. # skipping due to -t
  181. continue
  182. fi
  183. if [ \
  184. ! -z "${globalExcludeTests[${testRepo}_$t]:-}" \
  185. -o ! -z "${globalExcludeTests[${testRepo}:${variant}_$t]:-}" \
  186. -o ! -z "${globalExcludeTests[:${variant}_$t]:-}" \
  187. -o ! -z "${globalExcludeTests[${repo}_$t]:-}" \
  188. -o ! -z "${globalExcludeTests[${repo}:${variant}_$t]:-}" \
  189. -o ! -z "${globalExcludeTests[:${variant}_$t]:-}" \
  190. ]; then
  191. # skipping due to exclude
  192. continue
  193. fi
  194. tests+=( "$t" )
  195. done
  196. # check for zero tests before checking for existing image
  197. # this will make windows variants no longer fail
  198. if [ "${#tests[@]}" -eq '0' ]; then
  199. echo $'\timage has no tests...skipping'
  200. continue
  201. fi
  202. if ! docker inspect "$dockerImage" &> /dev/null; then
  203. echo $'\timage does not exist!'
  204. didFail=1
  205. continue
  206. fi
  207. currentTest=0
  208. totalTest="${#tests[@]}"
  209. for t in "${tests[@]}"; do
  210. (( currentTest+=1 ))
  211. echo -ne "\t'$t' [$currentTest/$totalTest]..."
  212. # run test against dockerImage here
  213. # find the script for the test
  214. scriptDir="${testPaths[$t]}"
  215. if [ -d "$scriptDir" ]; then
  216. script="$scriptDir/run.sh"
  217. if [ -x "$script" ] && [ ! -d "$script" ]; then
  218. # TODO dryRun logic
  219. if output="$("$script" "$dockerImage")"; then
  220. output="$(tr -d '\r' <<<"$output")" # Windows gives us \r\n ... :D
  221. if [ -f "$scriptDir/expected-std-out.txt" ] && ! d="$(diff -u "$scriptDir/expected-std-out.txt" - <<<"$output" 2>/dev/null)"; then
  222. echo 'failed; unexpected output:'
  223. echo "$d"
  224. didFail=1
  225. else
  226. echo 'passed'
  227. fi
  228. else
  229. echo 'failed'
  230. didFail=1
  231. fi
  232. else
  233. echo "skipping"
  234. echo >&2 "error: $script missing, not executable or is a directory"
  235. didFail=1
  236. continue
  237. fi
  238. else
  239. echo "skipping"
  240. echo >&2 "error: unable to locate test '$t'"
  241. didFail=1
  242. continue
  243. fi
  244. done
  245. done
  246. if [ -n "$didFail" ]; then
  247. exit 1
  248. fi