From f3e69d7300e3b4d6c5bb676c1dae27b10c3a4d56 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Thu, 10 Dec 2020 14:34:21 -0700 Subject: test-compose: rewrite to new subdir form ...in which we use all-local tests Signed-off-by: Ed Santiago --- test/compose/test-compose | 284 ++++++++++++++++------------------------------ 1 file changed, 95 insertions(+), 189 deletions(-) (limited to 'test/compose/test-compose') diff --git a/test/compose/test-compose b/test/compose/test-compose index f7643b078..9558fbf58 100755 --- a/test/compose/test-compose +++ b/test/compose/test-compose @@ -1,32 +1,26 @@ #!/usr/bin/env bash # -# Usage: test-docker-compose [testname] -# -# DEVELOPER NOTE: you almost certainly don't need to play in here. See README. +# Usage: test-compose [testname] # ME=$(basename $0) ############################################################################### # BEGIN stuff you can but probably shouldn't customize -# Directory where this script (and extra test configs) live +# Directory where this script and all subtests live TEST_ROOTDIR=$(realpath $(dirname $0)) # Podman executable -PODMAN_BIN=$(realpath bin)/podman - -# Github repo containing sample docker-compose setups -# FIXME: we should probably version this -AWESOME_COMPOSE=https://github.com/docker/awesome-compose +PODMAN_BIN=$(realpath $TEST_ROOTDIR/../../bin)/podman -# Local path to docker socket +# Local path to docker socket (we will add the unix:/ prefix when we need it) DOCKER_SOCK=/var/run/docker.sock # END stuff you can but probably shouldn't customize ############################################################################### # BEGIN setup -TMPDIR=${TMPDIR:-/var/tmp} +export TMPDIR=${TMPDIR:-/var/tmp} WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX) # Log of all HTTP requests and responses; always make '.log' point to latest @@ -153,145 +147,30 @@ function _bump() { echo $(( $count + 1 )) >| $file } -############# -# jsonify # convert 'foo=bar,x=y' to json {"foo":"bar","x":"y"} -############# -function jsonify() { - # split by comma - local -a settings_in - read -ra settings_in <<<"$1" - - # convert each to double-quoted form - local -a settings_out - for i in ${settings_in[*]}; do - settings_out+=$(sed -e 's/\(.*\)=\(.*\)/"\1":"\2"/' <<<$i) - done - - # ...and wrap inside braces. - # FIXME: handle commas - echo "{${settings_out[*]}}" -} - -####### -# t # Main test helper -####### -function t() { - local method=$1; shift - local path=$1; shift - local curl_args - - local testname="$method $path" - # POST requests require an extra params arg - if [[ $method = "POST" ]]; then - curl_args="-d $(jsonify $1)" - testname="$testname [$curl_args]" - shift - fi - - # entrypoint path can include a descriptive comment; strip it off - path=${path%% *} - - # curl -X HEAD but without --head seems to wait for output anyway - if [[ $method == "HEAD" ]]; then - curl_args="--head" - fi - local expected_code=$1; shift - - # If given path begins with /, use it as-is; otherwise prepend /version/ - local url=http://$HOST:$PORT - if expr "$path" : "/" >/dev/null; then - url="$url$path" - else - url="$url/v1.40/$path" - fi - - # Log every action we do - echo "-------------------------------------------------------------" >>$LOG - echo "\$ $testname" >>$LOG - rm -f $WORKDIR/curl.* - # -s = silent, but --write-out 'format' gives us important response data - response=$(curl -s -X $method ${curl_args} \ - -H 'Content-type: application/json' \ - --dump-header $WORKDIR/curl.headers.out \ - --write-out '%{http_code}^%{content_type}^%{time_total}' \ - -o $WORKDIR/curl.result.out "$url") - - # Any error from curl is instant bad news, from which we can't recover - rc=$? - if [[ $rc -ne 0 ]]; then - echo "FATAL: curl failure ($rc) on $url - cannot continue" >&2 - exit 1 - fi - - # Show returned headers (without trailing ^M or empty lines) in log file. - # Sometimes -- I can't remember why! -- we don't get headers. - if [[ -e $WORKDIR/curl.headers.out ]]; then - tr -d '\015' < $WORKDIR/curl.headers.out | egrep '.' >>$LOG - fi - - IFS='^' read actual_code content_type time_total <<<"$response" - printf "X-Response-Time: ${time_total}s\n\n" >>$LOG - - # Log results, if text. If JSON, filter through jq for readability. - if [[ $content_type =~ /octet ]]; then - output="[$(file --brief $WORKDIR/curl.result.out)]" - echo "$output" >>$LOG - else - output=$(< $WORKDIR/curl.result.out) - - if [[ $content_type =~ application/json ]]; then - jq . <<<"$output" >>$LOG - else - echo "$output" >>$LOG - fi - fi - - # Test return code - is "$actual_code" "$expected_code" "$testname : status" - - # Special case: 204/304, by definition, MUST NOT return content (rfc2616) - if [[ $expected_code = 204 || $expected_code = 304 ]]; then - if [ -n "$*" ]; then - die "Internal error: ${expected_code} status returns no output; fix your test." - fi - if [ -n "$output" ]; then - _show_ok 0 "$testname: ${expected_code} status returns no output" "''" "$output" - fi - return - fi - - local i - - # Special case: if response code does not match, dump the response body - # and skip all further subtests. - if [[ $actual_code != $expected_code ]]; then - echo -e "# response: $output" - for i; do - _show_ok skip "$testname: $i # skip - wrong return code" - done - return +############### +# test_port # Run curl against a port, check results against expectation +############### +function test_port() { + local port="$1" # e.g. 5000 + local op="$2" # '=' or '~' + local expect="$3" # what to expect from curl output + + local actual=$(curl --retry 5 --retry-connrefused -s http://127.0.0.1:$port/) + local curl_rc=$? + if [ $curl_rc -ne 0 ]; then + _show_ok 0 "$testname - curl failed with status $curl_rc" +### docker-compose down >>$logfile 2>&1 +### exit 1 fi - for i; do - if expr "$i" : "[^=~]\+=.*" >/dev/null; then - # Exact match on json field - json_field=$(expr "$i" : "\([^=]*\)=") - expect=$(expr "$i" : '[^=]*=\(.*\)') - actual=$(jq -r "$json_field" <<<"$output") - is "$actual" "$expect" "$testname : $json_field" - elif expr "$i" : "[^=~]\+~.*" >/dev/null; then - # regex match on json field - json_field=$(expr "$i" : "\([^~]*\)~") - expect=$(expr "$i" : '[^~]*~\(.*\)') - actual=$(jq -r "$json_field" <<<"$output") - like "$actual" "$expect" "$testname : $json_field" - else - # Direct string comparison - is "$output" "$i" "$testname : output" - fi - done + case "$op" in + '=') is "$actual" "$expect" "$testname : port $port" ;; + '~') like "$actual" "$expect" "$testname : port $port" ;; + *) die "Invalid operator '$op'" ;; + esac } + ################### # start_service # Run the socket listener ################### @@ -299,8 +178,10 @@ service_pid= function start_service() { test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN" - rm -rf $WORKDIR/{root,runroot,cni} - mkdir $WORKDIR/cni + # FIXME: use ${testname} subdir but we can't: 50-char limit in runroot + rm -rf $WORKDIR/{root,runroot,cni} + mkdir --mode 0755 $WORKDIR/{root,runroot,cni} + chcon --reference=/var/lib/containers $WORKDIR/root cp /etc/cni/net.d/*podman*conflist $WORKDIR/cni/ $PODMAN_BIN \ @@ -329,8 +210,21 @@ function start_service() { # podman # Needed by some test scripts to invoke the actual podman binary ############ function podman() { - echo "\$ $PODMAN_BIN $*" >>$WORKDIR/output.log - $PODMAN_BIN --root $WORKDIR "$@" >>$WORKDIR/output.log 2>&1 + echo "\$ podman $*" >>$WORKDIR/output.log + $PODMAN_BIN \ + --root $WORKDIR/root \ + --runroot $WORKDIR/runroot \ + "$@" >>$WORKDIR/output.log 2>&1 +} + +################### +# random_string # Returns a pseudorandom human-readable string +################### +function random_string() { + # Numeric argument, if present, is desired length of string + local length=${1:-10} + + head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length } # END infrastructure code @@ -345,17 +239,12 @@ done ############################################################################### # BEGIN entry handler (subtest invoker) -TESTS_DIR=$WORKDIR/awesome-compose - -git clone $AWESOME_COMPOSE $TESTS_DIR -git -C $TESTS_DIR checkout -q a3c38822277bcca04abbadf34120dcff808db3ec - # Identify the tests to run. If called with args, use those as globs. tests_to_run=() if [ -n "$*" ]; then shopt -s nullglob for i; do - match=(${TEST_ROOTDIR}/*${i}*.curl) + match=(${TEST_ROOTDIR}/*${i}*/docker-compose.yml) if [ ${#match} -eq 0 ]; then die "No match for $TEST_ROOTDIR/*$i*.curl" fi @@ -363,56 +252,68 @@ if [ -n "$*" ]; then done shopt -u nullglob else - tests_to_run=(${TEST_ROOTDIR}/*.curl) + tests_to_run=(${TEST_ROOTDIR}/*/docker-compose.yml) fi -# Test count: each of those tests might have a local set of subtests -n_tests=$((2 * ${#tests_to_run[*]})) +# Too hard to precompute the number of tests; just spit it out at the end. +n_tests=0 for t in ${tests_to_run[@]}; do - n_curls=$(wc -l $t | awk '{print $1}') - n_tests=$(( n_tests + n_curls )) -done + testdir="$(dirname $t)" + testname="$(basename $testdir)" - -echo "1..$n_tests" - -for t in ${tests_to_run[@]}; do - testname="$(basename $t .curl)" + if [ -e $test_dir/SKIP ]; then + local reason="$(<$test_dir/SKIP)" + if [ -n "$reason" ]; then + reason=" - $reason" + fi + _show_ok skip "$testname # skip$reason" + continue + fi start_service logfile=$WORKDIR/$testname.log ( - cd $TESTS_DIR/$testname || die "Cannot cd $TESTS_DIR/$testname" + cd $testdir || die "Cannot cd $testdir" + + # setup file may be used for creating temporary directories/files. + # We source it so that envariables defined in it will get back to us. + if [ -e setup.sh ]; then + . setup.sh + fi + if [ -e teardown.sh ]; then + trap '. teardown.sh' 0 + fi + docker-compose up -d &> $logfile - if [[ $? -ne 0 ]]; then - _show_ok 0 "$testname - up" "[ok]" "$(< $logfile)" - # FIXME: cat log + docker_compose_rc=$? + if [[ $docker_compose_rc -ne 0 ]]; then + _show_ok 0 "$testname - up" "[ok]" "status=$docker_compose_rc" + sed -e 's/^/# /' <$logfile docker-compose down >>$logfile 2>&1 # No status check here exit 1 fi _show_ok 1 "$testname - up" - # FIXME: run tests, e.g. curl - curls=$TEST_ROOTDIR/$testname.curl - if [[ -e $curls ]]; then - while read port expect; do - actual=$(curl --retry 5 --retry-connrefused -s http://127.0.0.1:$port/) - curl_rc=$? - if [ $curl_rc -ne 0 ]; then - _show_ok 0 "$testname - curl failed with status $curl_rc" - docker-compose down >>$logfile 2>&1 - exit 1 - fi - like "$actual" "$expect" "$testname : port $port" - done < $curls + # Run tests. This is likely to be a series of 'test_port' checks + # but may also include podman commands to inspect labels, state + if [ -e tests.sh ]; then + . tests.sh + fi + # FIXME: if any tests fail, try 'podman logs' on container? + + if [ -n "$COMPOSE_WAIT" ]; then + echo -n "Pausing due to \$COMPOSE_WAIT. Press ENTER to continue: " + read keepgoing fi + # Done. Clean up. docker-compose down &> $logfile - if [[ $? -eq 0 ]]; then + rc=$? + if [[ $rc -eq 0 ]]; then _show_ok 1 "$testname - down" else - _show_ok 0 "$testname - down" "[ok]" "$(< $logfile)" + _show_ok 0 "$testname - down" "[ok]" "rc=$rc" # FIXME: show error fi ) @@ -422,6 +323,9 @@ for t in ${tests_to_run[@]}; do # FIXME: otherwise we get EBUSY umount $WORKDIR/root/overlay &>/dev/null + + # FIXME: run 'podman ps'? +# rm -rf $WORKDIR/${testname} done # END entry handler @@ -432,8 +336,10 @@ done test_count=$(<$testcounter_file) failure_count=$(<$failures_file) -if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then - rm -rf $WORKDIR -fi +#if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then +# rm -rf $WORKDIR +#fi + +echo "1..${test_count}" exit $failure_count -- cgit v1.2.3-54-g00ecf