github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/test/apiv2/test-apiv2 (about) 1 #!/bin/bash 2 # 3 # Usage: test-apiv2 [PORT] 4 # 5 # DEVELOPER NOTE: you almost certainly don't need to play in here. See README. 6 # 7 ME=$(basename $0) 8 9 ############################################################################### 10 # BEGIN stuff you can but probably shouldn't customize 11 12 PODMAN_TEST_IMAGE_REGISTRY=${PODMAN_TEST_IMAGE_REGISTRY:-"quay.io"} 13 PODMAN_TEST_IMAGE_USER=${PODMAN_TEST_IMAGE_USER:-"libpod"} 14 PODMAN_TEST_IMAGE_NAME=${PODMAN_TEST_IMAGE_NAME:-"alpine_labels"} 15 PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"latest"} 16 PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG" 17 18 IMAGE=$PODMAN_TEST_IMAGE_FQN 19 20 # END stuff you can but probably shouldn't customize 21 ############################################################################### 22 # BEGIN setup 23 24 TMPDIR=${TMPDIR:-/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 HOST=localhost 33 PORT=${PODMAN_SERVICE_PORT:-8081} 34 35 # Keep track of test count and failures in files, not variables, because 36 # variables don't carry back up from subshells. 37 testcounter_file=$WORKDIR/.testcounter 38 failures_file=$WORKDIR/.failures 39 40 echo 0 >$testcounter_file 41 echo 0 >$failures_file 42 43 # Where the tests live 44 TESTS_DIR=$(realpath $(dirname $0)) 45 46 # Path to podman binary 47 PODMAN_BIN=${PODMAN:-${TESTS_DIR}/../../bin/podman} 48 49 # END setup 50 ############################################################################### 51 # BEGIN infrastructure code - the helper functions used in tests themselves 52 53 ######### 54 # die # Exit error with a message to stderr 55 ######### 56 function die() { 57 echo "$ME: $*" >&2 58 exit 1 59 } 60 61 ######## 62 # is # Simple comparison 63 ######## 64 function is() { 65 local actual=$1 66 local expect=$2 67 local testname=$3 68 69 if [ "$actual" = "$expect" ]; then 70 # On success, include expected value; this helps readers understand 71 _show_ok 1 "$testname=$expect" 72 return 73 fi 74 _show_ok 0 "$testname" "$expect" "$actual" 75 } 76 77 ########## 78 # like # Compare, but allowing patterns 79 ########## 80 function like() { 81 local actual=$1 82 local expect=$2 83 local testname=$3 84 85 if expr "$actual" : "$expect" &>/dev/null; then 86 # On success, include expected value; this helps readers understand 87 _show_ok 1 "$testname~$expect" 88 return 89 fi 90 _show_ok 0 "$testname" "~ $expect" "$actual" 91 } 92 93 ############## 94 # _show_ok # Helper for is() and like(): displays 'ok' or 'not ok' 95 ############## 96 function _show_ok() { 97 local ok=$1 98 local testname=$2 99 100 # If output is a tty, colorize pass/fail 101 local red= 102 local green= 103 local reset= 104 local bold= 105 if [ -t 1 ]; then 106 red='\e[31m' 107 green='\e[32m' 108 reset='\e[0m' 109 bold='\e[1m' 110 fi 111 112 _bump $testcounter_file 113 count=$(<$testcounter_file) 114 if [ $ok -eq 1 ]; then 115 echo -e "${green}ok $count ${TEST_CONTEXT} $testname${reset}" 116 echo "ok $count ${TEST_CONTEXT} $testname" >>$LOG 117 return 118 fi 119 120 # Failed 121 local expect=$3 122 local actual=$4 123 echo -e "${red}not ok $count ${TEST_CONTEXT} $testname${reset}" 124 echo -e "${red}# expected: $expect${reset}" 125 echo -e "${red}# actual: ${bold}$actual${reset}" 126 127 echo "not ok $count ${TEST_CONTEXT} $testname" >>$LOG 128 echo " expected: $expect" 129 130 _bump $failures_file 131 } 132 133 ########### 134 # _bump # Increment a counter in a file 135 ########### 136 function _bump() { 137 local file=$1 138 139 count=$(<$file) 140 echo $(( $count + 1 )) >| $file 141 } 142 143 ############# 144 # jsonify # convert 'foo=bar,x=y' to json {"foo":"bar","x":"y"} 145 ############# 146 function jsonify() { 147 # split by comma 148 local -a settings_in 149 read -ra settings_in <<<"$1" 150 151 # convert each to double-quoted form 152 local -a settings_out 153 for i in ${settings_in[*]}; do 154 settings_out+=$(sed -e 's/\(.*\)=\(.*\)/"\1":"\2"/' <<<$i) 155 done 156 157 # ...and wrap inside braces. 158 # FIXME: handle commas 159 echo "{${settings_out[*]}}" 160 } 161 162 ####### 163 # t # Main test helper 164 ####### 165 function t() { 166 local method=$1; shift 167 local path=$1; shift 168 local curl_args 169 170 local testname="$method $path" 171 # POST requests require an extra params arg 172 if [[ $method = "POST" ]]; then 173 curl_args="-d $(jsonify $1)" 174 testname="$testname [$1]" 175 shift 176 fi 177 178 # entrypoint path can include a descriptive comment; strip it off 179 path=${path%% *} 180 181 # curl -X HEAD but without --head seems to wait for output anyway 182 if [[ $method == "HEAD" ]]; then 183 curl_args="--head" 184 fi 185 local expected_code=$1; shift 186 187 # If given path begins with /, use it as-is; otherwise prepend /version/ 188 local url=http://$HOST:$PORT 189 if expr "$path" : "/" >/dev/null; then 190 url="$url$path" 191 else 192 url="$url/v1.40/$path" 193 fi 194 195 # Log every action we do 196 echo "-------------------------------------------------------------" >>$LOG 197 echo "\$ $testname" >>$LOG 198 rm -f $WORKDIR/curl.* 199 curl -s -X $method ${curl_args} \ 200 -H 'Content-type: application/json' \ 201 --dump-header $WORKDIR/curl.headers.out \ 202 -o $WORKDIR/curl.result.out "$url" 203 204 if [[ $? -eq 7 ]]; then 205 echo "FATAL: curl failure on $url - cannot continue" >&2 206 exit 1 207 fi 208 209 cat $WORKDIR/curl.headers.out >>$LOG 2>/dev/null || true 210 output=$(< $WORKDIR/curl.result.out) 211 212 # Log results. If JSON, filter through jq for readability 213 if egrep -qi '^Content-Type: application/json' $WORKDIR/curl.headers.out; then 214 jq . <<<"$output" >>$LOG 215 else 216 echo "$output" >>$LOG 217 fi 218 219 # Test return code 220 actual_code=$(head -n1 $WORKDIR/curl.headers.out | awk '/^HTTP/ { print $2}') 221 is "$actual_code" "$expected_code" "$testname : status" 222 223 224 # Special case: 204/304, by definition, MUST NOT return content (rfc2616) 225 if [[ $expected_code = 204 || $expected_code = 304 ]]; then 226 if [ -n "$*" ]; then 227 die "Internal error: ${expected_code} status returns no output; fix your test." 228 fi 229 if [ -n "$output" ]; then 230 _show_ok 0 "$testname: ${expected_code} status returns no output" "''" "$output" 231 fi 232 return 233 fi 234 235 for i; do 236 case "$i" in 237 # Exact match on json field 238 *=*) 239 json_field=$(expr "$i" : "\([^=]*\)=") 240 expect=$(expr "$i" : '[^=]*=\(.*\)') 241 actual=$(jq -r "$json_field" <<<"$output") 242 is "$actual" "$expect" "$testname : $json_field" 243 ;; 244 # regex match on json field 245 *~*) 246 json_field=$(expr "$i" : "\([^~]*\)~") 247 expect=$(expr "$i" : '[^~]*~\(.*\)') 248 actual=$(jq -r "$json_field" <<<"$output") 249 like "$actual" "$expect" "$testname : $json_field" 250 ;; 251 # Direct string comparison 252 *) 253 is "$output" "$i" "$testname : output" 254 ;; 255 esac 256 done 257 } 258 259 ################### 260 # start_service # Run the socket listener 261 ################### 262 service_pid= 263 function start_service() { 264 # If there's a listener on the port, nothing for us to do 265 { exec 3<> /dev/tcp/$HOST/$PORT; } &>/dev/null && return 266 267 test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN" 268 269 if [ "$HOST" != "localhost" ]; then 270 die "Cannot start service on non-localhost ($HOST)" 271 fi 272 273 $PODMAN_BIN --root $WORKDIR system service --timeout 15 tcp:127.0.0.1:$PORT \ 274 &> $WORKDIR/server.log & 275 service_pid=$! 276 277 # Wait 278 local _timeout=5 279 while [ $_timeout -gt 0 ]; do 280 { exec 3<> /dev/tcp/$HOST/$PORT; } &>/dev/null && return 281 sleep 1 282 _timeout=$(( $_timeout - 1 )) 283 done 284 die "Timed out waiting for service" 285 } 286 287 ############ 288 # podman # Needed by some test scripts to invoke the actual podman binary 289 ############ 290 function podman() { 291 echo "\$ $PODMAN_BIN $*" >>$WORKDIR/output.log 292 $PODMAN_BIN --root $WORKDIR "$@" >>$WORKDIR/output.log 2>&1 293 } 294 295 #################### 296 # root, rootless # Is server rootless? 297 #################### 298 ROOTLESS= 299 function root() { 300 ! rootless 301 } 302 303 function rootless() { 304 if [[ -z $ROOTLESS ]]; then 305 ROOTLESS=$(curl -s http://$HOST:$PORT/v1.40/info | jq .Rootless) 306 fi 307 test "$ROOTLESS" = "true" 308 } 309 310 # True if cgroups v2 are enabled 311 function have_cgroupsv2() { 312 cgroup_type=$(stat -f -c %T /sys/fs/cgroup) 313 test "$cgroup_type" = "cgroup2fs" 314 } 315 316 # END infrastructure code 317 ############################################################################### 318 # BEGIN sanity checks 319 320 for tool in curl jq podman; do 321 type $tool &>/dev/null || die "$ME: Required tool '$tool' not found" 322 done 323 324 # END sanity checks 325 ############################################################################### 326 # BEGIN entry handler (subtest invoker) 327 328 # Identify the tests to run. If called with args, use those as globs. 329 tests_to_run=() 330 if [ -n "$*" ]; then 331 shopt -s nullglob 332 for i; do 333 match=(${TESTS_DIR}/*${i}*.at) 334 if [ ${#match} -eq 0 ]; then 335 die "No match for $TESTS_DIR/*$i*.at" 336 fi 337 tests_to_run+=("${match[@]}") 338 done 339 shopt -u nullglob 340 else 341 tests_to_run=($TESTS_DIR/*.at) 342 fi 343 344 start_service 345 346 for i in ${tests_to_run[@]}; do 347 TEST_CONTEXT="[$(basename $i .at)]" 348 source $i 349 done 350 351 # END entry handler 352 ############################################################################### 353 354 # Clean up 355 356 if [ -n "$service_pid" ]; then 357 kill $service_pid 358 wait $service_pid 359 fi 360 361 test_count=$(<$testcounter_file) 362 failure_count=$(<$failures_file) 363 364 if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then 365 rm -rf $WORKDIR 366 fi 367 368 echo "1..${test_count}" 369 370 exit $failure_count