github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/test/compose/test-compose (about) 1 #!/usr/bin/env bash 2 # 3 # Usage: test-compose [testname] 4 # 5 ME=$(basename $0) 6 7 ############################################################################### 8 # BEGIN stuff you can but probably shouldn't customize 9 10 # Directory where this script and all subtests live 11 TEST_ROOTDIR=$(realpath $(dirname $0)) 12 13 # Podman executable 14 PODMAN_BIN=$(realpath $TEST_ROOTDIR/../../bin)/podman 15 16 # Local path to docker socket with unix prefix 17 # The path will be changed for rootless users 18 DOCKER_SOCK=/var/run/docker.sock 19 20 # END stuff you can but probably shouldn't customize 21 ############################################################################### 22 # BEGIN setup 23 24 export TMPDIR=${TMPDIR:-/var/tmp} 25 WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX) 26 27 # Log of all HTTP requests and responses; always make '.log' point to latest 28 LOGBASE=${TMPDIR}/$ME.log 29 LOG=${LOGBASE}.$(date +'%Y%m%dT%H%M%S') 30 ln -sf $LOG $LOGBASE 31 32 # Keep track of test count and failures in files, not variables, because 33 # variables don't carry back up from subshells. 34 testcounter_file=$WORKDIR/.testcounter 35 failures_file=$WORKDIR/.failures 36 37 echo 0 >$testcounter_file 38 echo 0 >$failures_file 39 40 # END setup 41 ############################################################################### 42 # BEGIN infrastructure code - the helper functions used in tests themselves 43 44 ################# 45 # is_rootless # Check if we run as normal user 46 ################# 47 function is_rootless() { 48 [ "$(id -u)" -ne 0 ] 49 } 50 51 ######### 52 # die # Exit error with a message to stderr 53 ######### 54 function die() { 55 echo "$ME: $*" >&2 56 exit 1 57 } 58 59 ######## 60 # is # Simple comparison 61 ######## 62 function is() { 63 local actual=$1 64 local expect=$2 65 local testname=$3 66 67 if [[ $actual = $expect ]]; then 68 # On success, include expected value; this helps readers understand 69 _show_ok 1 "$testname=$expect" 70 return 71 fi 72 _show_ok 0 "$testname" "$expect" "$actual" 73 } 74 75 ########## 76 # like # Compare, but allowing patterns 77 ########## 78 function like() { 79 local actual=$1 80 local expect=$2 81 local testname=$3 82 83 # "is" (equality) is a subset of "like", but one that expr fails on if 84 # the expected result has shell-special characters like '['. Treat it 85 # as a special case. 86 87 if [[ "$actual" = "$expect" ]]; then 88 _show_ok 1 "$testname=$expect" 89 return 90 fi 91 92 if expr "$actual" : ".*$expect" &>/dev/null; then 93 # On success, include expected value; this helps readers understand 94 _show_ok 1 "$testname ('$actual') ~ $expect" 95 return 96 fi 97 _show_ok 0 "$testname" "~ $expect" "$actual" 98 } 99 100 ############## 101 # _show_ok # Helper for is() and like(): displays 'ok' or 'not ok' 102 ############## 103 function _show_ok() { 104 local ok=$1 105 local testname=$2 106 107 # If output is a tty, colorize pass/fail 108 local red= 109 local green= 110 local reset= 111 local bold= 112 if [ -t 1 ]; then 113 red='\e[31m' 114 green='\e[32m' 115 reset='\e[0m' 116 bold='\e[1m' 117 fi 118 119 _bump $testcounter_file 120 count=$(<$testcounter_file) 121 122 # "skip" is a special case of "ok". Assume that our caller has included 123 # the magical '# skip - reason" comment string. 124 if [[ $ok == "skip" ]]; then 125 # colon-plus: replace green with yellow, but only if green is non-null 126 green="${green:+\e[33m}" 127 ok=1 128 fi 129 if [ $ok -eq 1 ]; then 130 echo -e "${green}ok $count $testname${reset}" 131 echo "ok $count $testname" >>$LOG 132 return 133 fi 134 135 # Failed 136 local expect=$3 137 local actual=$4 138 printf "${red}not ok $count $testname${reset}\n" 139 # Not all errors include actual/expect 140 if [[ -n "$expect" || -n "$actual" ]]; then 141 printf "${red}# expected: %s${reset}\n" "$expect" 142 printf "${red}# actual: ${bold}%s${reset}\n" "$actual" 143 fi 144 145 echo "not ok $count $testname" >>$LOG 146 echo " expected: $expect" >>$LOG 147 148 _bump $failures_file 149 } 150 151 ########### 152 # _bump # Increment a counter in a file 153 ########### 154 function _bump() { 155 local file=$1 156 157 count=$(<$file) 158 echo $(( $count + 1 )) >| $file 159 } 160 161 ############### 162 # test_port # Run curl against a port, check results against expectation 163 ############### 164 function test_port() { 165 local port="$1" # e.g. 5000 166 local op="$2" # '=' or '~' 167 local expect="$3" # what to expect from curl output 168 169 # -s -S means "silent, but show errors" 170 local actual 171 actual=$(curl --retry 3 --retry-all-errors -s -S http://127.0.0.1:$port/) 172 local curl_rc=$? 173 174 if [ $curl_rc -ne 0 ]; then 175 _show_ok 0 "$testname - curl (port $port) failed with status $curl_rc" 176 echo "# podman ps -a:" 177 $PODMAN_BIN --storage-driver=vfs --root $WORKDIR/root --runroot $WORKDIR/runroot ps -a 178 if type -p ss; then 179 echo "# ss -tulpn:" 180 ss -tulpn 181 echo "# podman unshare --rootless-cni ss -tulpn:" 182 $PODMAN_BIN --storage-driver=vfs --root $WORKDIR/root --runroot $WORKDIR/runroot unshare --rootless-cni ss -tulpn 183 fi 184 echo "# cat $WORKDIR/server.log:" 185 cat $WORKDIR/server.log 186 echo "# cat $logfile:" 187 cat $logfile 188 return 189 fi 190 191 case "$op" in 192 '=') is "$actual" "$expect" "$testname : port $port" ;; 193 '~') like "$actual" "$expect" "$testname : port $port" ;; 194 *) die "Invalid operator '$op'" ;; 195 esac 196 } 197 198 199 ################### 200 # start_service # Run the socket listener 201 ################### 202 service_pid= 203 function start_service() { 204 test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN" 205 206 # FIXME: use ${testname} subdir but we can't: 50-char limit in runroot 207 if ! is_rootless; then 208 rm -rf $WORKDIR/{root,runroot,cni} 209 else 210 $PODMAN_BIN unshare rm -rf $WORKDIR/{root,runroot,cni} 211 fi 212 rm -f $DOCKER_SOCK 213 mkdir --mode 0755 $WORKDIR/{root,runroot,cni} 214 chcon --reference=/var/lib/containers $WORKDIR/root 215 cp /etc/cni/net.d/*podman*conflist $WORKDIR/cni/ 216 217 $PODMAN_BIN \ 218 --log-level debug \ 219 --storage-driver=vfs \ 220 --root $WORKDIR/root \ 221 --runroot $WORKDIR/runroot \ 222 --cgroup-manager=systemd \ 223 --network-config-dir $WORKDIR/cni \ 224 system service \ 225 --time 0 unix://$DOCKER_SOCK \ 226 &> $WORKDIR/server.log & 227 service_pid=$! 228 229 # Wait (FIXME: how do we test the socket?) 230 local _timeout=5 231 while [ $_timeout -gt 0 ]; do 232 # FIXME: should we actually try a read or write? 233 test -S $DOCKER_SOCK && return 234 sleep 1 235 _timeout=$(( $_timeout - 1 )) 236 done 237 cat $WORKDIR/server.log 238 die "Timed out waiting for service" 239 } 240 241 ############ 242 # podman # Needed by some test scripts to invoke the actual podman binary 243 ############ 244 function podman() { 245 echo "\$ podman $*" >>$WORKDIR/output.log 246 output=$($PODMAN_BIN \ 247 --storage-driver=vfs \ 248 --root $WORKDIR/root \ 249 --runroot $WORKDIR/runroot \ 250 --network-config-dir $WORKDIR/cni \ 251 "$@") 252 echo -n "$output" >>$WORKDIR/output.log 253 } 254 255 ################### 256 # random_string # Returns a pseudorandom human-readable string 257 ################### 258 function random_string() { 259 # Numeric argument, if present, is desired length of string 260 local length=${1:-10} 261 262 head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length 263 } 264 265 # END infrastructure code 266 ############################################################################### 267 # BEGIN sanity checks 268 269 for tool in curl docker-compose; do 270 type $tool &>/dev/null || die "$ME: Required tool '$tool' not found" 271 done 272 273 # END sanity checks 274 ############################################################################### 275 # BEGIN entry handler (subtest invoker) 276 277 # When rootless use a socket path accessible by the rootless user 278 if is_rootless; then 279 DOCKER_SOCK="$WORKDIR/docker.sock" 280 DOCKER_HOST="unix://$DOCKER_SOCK" 281 # export DOCKER_HOST docker-compose will use it 282 export DOCKER_HOST 283 fi 284 285 # Identify the tests to run. If called with args, use those as globs. 286 tests_to_run=() 287 if [ -n "$*" ]; then 288 shopt -s nullglob 289 for i; do 290 match=(${TEST_ROOTDIR}/*${i}*/docker-compose.yml) 291 if [ ${#match} -eq 0 ]; then 292 die "No match for $TEST_ROOTDIR/*$i*.curl" 293 fi 294 tests_to_run+=("${match[@]}") 295 done 296 shopt -u nullglob 297 else 298 tests_to_run=(${TEST_ROOTDIR}/*/docker-compose.yml) 299 fi 300 301 # Too hard to precompute the number of tests; just spit it out at the end. 302 n_tests=0 303 304 # We aren't really TAP 13; this helps logformatter recognize our output as BATS 305 echo "TAP version 13" 306 307 for t in ${tests_to_run[@]}; do 308 testdir="$(dirname $t)" 309 testname="$(basename $testdir)" 310 311 if [ -e $test_dir/SKIP ]; then 312 local reason="$(<$test_dir/SKIP)" 313 if [ -n "$reason" ]; then 314 reason=" - $reason" 315 fi 316 _show_ok skip "$testname # skip$reason" 317 continue 318 fi 319 320 start_service 321 322 logfile=$WORKDIR/$testname.log 323 ( 324 cd $testdir || die "Cannot cd $testdir" 325 326 # setup file may be used for creating temporary directories/files. 327 # We source it so that envariables defined in it will get back to us. 328 if [ -e setup.sh ]; then 329 . setup.sh 330 fi 331 if [ -e teardown.sh ]; then 332 trap '. teardown.sh' 0 333 fi 334 335 docker-compose up -d &> $logfile 336 docker_compose_rc=$? 337 if [[ $docker_compose_rc -ne 0 ]]; then 338 _show_ok 0 "$testname - up" "[ok]" "status=$docker_compose_rc" 339 sed -e 's/^/# /' <$logfile 340 docker-compose down >>$logfile 2>&1 # No status check here 341 exit 1 342 fi 343 _show_ok 1 "$testname - up" 344 345 # Run tests. This is likely to be a series of 'test_port' checks 346 # but may also include podman commands to inspect labels, state 347 if [ -e tests.sh ]; then 348 . tests.sh 349 fi 350 # FIXME: if any tests fail, try 'podman logs' on container? 351 352 if [ -n "$COMPOSE_WAIT" ]; then 353 echo -n "Pausing due to \$COMPOSE_WAIT. Press ENTER to continue: " 354 read keepgoing 355 fi 356 357 # Done. Clean up. 358 docker-compose down &>> $logfile 359 rc=$? 360 if [[ $rc -eq 0 ]]; then 361 _show_ok 1 "$testname - down" 362 else 363 _show_ok 0 "$testname - down" "[ok]" "rc=$rc" 364 # FIXME: show error 365 fi 366 ) 367 368 kill $service_pid 369 wait $service_pid 370 371 # FIXME: otherwise we get EBUSY 372 if ! is_rootless; then 373 umount $WORKDIR/root/overlay &>/dev/null 374 else 375 $PODMAN_BIN unshare umount $WORKDIR/root/overlay &>/dev/null 376 fi 377 378 # FIXME: run 'podman ps'? 379 # rm -rf $WORKDIR/${testname} 380 done 381 382 # END entry handler 383 ############################################################################### 384 385 # Clean up 386 387 test_count=$(<$testcounter_file) 388 failure_count=$(<$failures_file) 389 390 if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then 391 if ! is_rootless; then 392 rm -rf $WORKDIR 393 else 394 $PODMAN_BIN unshare rm -rf $WORKDIR 395 fi 396 fi 397 398 echo "1..${test_count}" 399 400 exit $failure_count