github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/test/apiv2/test-apiv2 (about)

     1  #!/usr/bin/env 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 ('$actual') ~ $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  
   115      # "skip" is a special case of "ok". Assume that our caller has included
   116      # the magical '# skip - reason" comment string.
   117      if [[ $ok == "skip" ]]; then
   118          # colon-plus: replace green with yellow, but only if green is non-null
   119          green="${green:+\e[33m}"
   120          ok=1
   121      fi
   122      if [ $ok -eq 1 ]; then
   123          echo -e "${green}ok $count ${TEST_CONTEXT} $testname${reset}"
   124          echo    "ok $count ${TEST_CONTEXT} $testname" >>$LOG
   125          return
   126      fi
   127  
   128      # Failed
   129      local expect=$3
   130      local actual=$4
   131      echo -e "${red}not ok $count ${TEST_CONTEXT} $testname${reset}"
   132      echo -e "${red}#  expected: $expect${reset}"
   133      echo -e "${red}#    actual: ${bold}$actual${reset}"
   134  
   135      echo    "not ok $count ${TEST_CONTEXT} $testname" >>$LOG
   136      echo    "  expected: $expect"                     >>$LOG
   137  
   138      _bump $failures_file
   139  }
   140  
   141  ###########
   142  #  _bump  #  Increment a counter in a file
   143  ###########
   144  function _bump() {
   145      local file=$1
   146  
   147      count=$(<$file)
   148      echo $(( $count + 1 )) >| $file
   149  }
   150  
   151  #############
   152  #  jsonify  #  convert 'foo=bar,x=y' to json {"foo":"bar","x":"y"}
   153  #############
   154  function jsonify() {
   155      # split by comma
   156      local -a settings_in
   157      read -ra settings_in <<<"$1"
   158  
   159      # convert each to double-quoted form
   160      local -a settings_out
   161      for i in ${settings_in[*]}; do
   162          settings_out+=$(sed -e 's/\(.*\)=\(.*\)/"\1":"\2"/' <<<$i)
   163      done
   164  
   165      # ...and wrap inside braces.
   166      # FIXME: handle commas
   167      echo "{${settings_out[*]}}"
   168  }
   169  
   170  #######
   171  #  t  #  Main test helper
   172  #######
   173  function t() {
   174      local method=$1; shift
   175      local path=$1; shift
   176      local curl_args
   177  
   178      local testname="$method $path"
   179      # POST requests require an extra params arg
   180      if [[ $method = "POST" ]]; then
   181          curl_args="-d $(jsonify $1)"
   182          testname="$testname [$curl_args]"
   183          shift
   184      fi
   185  
   186      # entrypoint path can include a descriptive comment; strip it off
   187      path=${path%% *}
   188  
   189      # curl -X HEAD but without --head seems to wait for output anyway
   190      if [[ $method == "HEAD" ]]; then
   191          curl_args="--head"
   192      fi
   193      local expected_code=$1; shift
   194  
   195      # If given path begins with /, use it as-is; otherwise prepend /version/
   196      local url=http://$HOST:$PORT
   197      if expr "$path" : "/" >/dev/null; then
   198          url="$url$path"
   199      else
   200          url="$url/v1.40/$path"
   201      fi
   202  
   203      # Log every action we do
   204      echo "-------------------------------------------------------------" >>$LOG
   205      echo "\$ $testname"                                                  >>$LOG
   206      rm -f $WORKDIR/curl.*
   207      # -s = silent, but --write-out 'format' gives us important response data
   208      response=$(curl -s -X $method ${curl_args}                   \
   209                      -H 'Content-type: application/json'          \
   210                      --dump-header $WORKDIR/curl.headers.out       \
   211                      --write-out '%{http_code}^%{content_type}^%{time_total}' \
   212                      -o $WORKDIR/curl.result.out "$url")
   213  
   214      # Any error from curl is instant bad news, from which we can't recover
   215      rc=$?
   216      if [[ $rc -ne 0 ]]; then
   217          echo "FATAL: curl failure ($rc) on $url - cannot continue" >&2
   218          exit 1
   219      fi
   220  
   221      # Show returned headers (without trailing ^M or empty lines) in log file.
   222      # Sometimes -- I can't remember why! -- we don't get headers.
   223      if [[ -e $WORKDIR/curl.headers.out ]]; then
   224          tr -d '\015' < $WORKDIR/curl.headers.out | egrep '.' >>$LOG
   225      fi
   226  
   227      IFS='^' read actual_code content_type time_total <<<"$response"
   228      printf "X-Response-Time: ${time_total}s\n\n" >>$LOG
   229  
   230      # Log results, if text. If JSON, filter through jq for readability.
   231      if [[ $content_type =~ /octet ]]; then
   232          output="[$(file --brief $WORKDIR/curl.result.out)]"
   233          echo "$output" >>$LOG
   234      else
   235          output=$(< $WORKDIR/curl.result.out)
   236  
   237          if [[ $content_type =~ application/json ]]; then
   238              jq . <<<"$output" >>$LOG
   239          else
   240              echo "$output" >>$LOG
   241          fi
   242      fi
   243  
   244      # Test return code
   245      is "$actual_code" "$expected_code" "$testname : status"
   246  
   247      # Special case: 204/304, by definition, MUST NOT return content (rfc2616)
   248      if [[ $expected_code = 204 || $expected_code = 304 ]]; then
   249          if [ -n "$*" ]; then
   250              die "Internal error: ${expected_code} status returns no output; fix your test."
   251          fi
   252          if [ -n "$output" ]; then
   253              _show_ok 0 "$testname: ${expected_code} status returns no output" "''" "$output"
   254          fi
   255          return
   256      fi
   257  
   258      local i
   259  
   260      # Special case: if response code does not match, dump the response body
   261      # and skip all further subtests.
   262      if [[ $actual_code != $expected_code ]]; then
   263          echo -e "#  response: $output"
   264          for i; do
   265              _show_ok skip "$testname: $i # skip - wrong return code"
   266          done
   267          return
   268      fi
   269  
   270      for i; do
   271          if expr "$i" : "[^=~]\+=.*" >/dev/null; then
   272              # Exact match on json field
   273              json_field=$(expr "$i" : "\([^=]*\)=")
   274              expect=$(expr "$i" : '[^=]*=\(.*\)')
   275              actual=$(jq -r "$json_field" <<<"$output")
   276              is "$actual" "$expect" "$testname : $json_field"
   277          elif expr "$i" : "[^=~]\+~.*" >/dev/null; then
   278              # regex match on json field
   279              json_field=$(expr "$i" : "\([^~]*\)~")
   280              expect=$(expr "$i" : '[^~]*~\(.*\)')
   281              actual=$(jq -r "$json_field" <<<"$output")
   282              like "$actual" "$expect" "$testname : $json_field"
   283          else
   284              # Direct string comparison
   285              is "$output" "$i" "$testname : output"
   286          fi
   287      done
   288  }
   289  
   290  ###################
   291  #  start_service  #  Run the socket listener
   292  ###################
   293  service_pid=
   294  function start_service() {
   295      # If there's a listener on the port, nothing for us to do
   296      { exec 3<> /dev/tcp/$HOST/$PORT; } &>/dev/null && return
   297  
   298      test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN"
   299  
   300      if [ "$HOST" != "localhost" ]; then
   301          die "Cannot start service on non-localhost ($HOST)"
   302      fi
   303  
   304      $PODMAN_BIN --root $WORKDIR system service --time 15 tcp:127.0.0.1:$PORT \
   305          &> $WORKDIR/server.log &
   306      service_pid=$!
   307  
   308      # Wait
   309      local _timeout=5
   310      while [ $_timeout -gt 0 ]; do
   311          { exec 3<> /dev/tcp/$HOST/$PORT; } &>/dev/null && return
   312          sleep 1
   313          _timeout=$(( $_timeout - 1 ))
   314      done
   315      die "Timed out waiting for service"
   316  }
   317  
   318  ############
   319  #  podman  #  Needed by some test scripts to invoke the actual podman binary
   320  ############
   321  function podman() {
   322      echo "\$ $PODMAN_BIN $*"           >>$WORKDIR/output.log
   323      $PODMAN_BIN --root $WORKDIR "$@"   >>$WORKDIR/output.log 2>&1
   324  }
   325  
   326  ####################
   327  #  root, rootless  #  Is server rootless?
   328  ####################
   329  ROOTLESS=
   330  function root() {
   331      ! rootless
   332  }
   333  
   334  function rootless() {
   335      if [[ -z $ROOTLESS ]]; then
   336          ROOTLESS=$(curl -s http://$HOST:$PORT/v1.40/info | jq .Rootless)
   337      fi
   338      test "$ROOTLESS" = "true"
   339  }
   340  
   341  # True if cgroups v2 are enabled
   342  function have_cgroupsv2() {
   343      cgroup_type=$(stat -f -c %T /sys/fs/cgroup)
   344      test "$cgroup_type" = "cgroup2fs"
   345  }
   346  
   347  # END   infrastructure code
   348  ###############################################################################
   349  # BEGIN sanity checks
   350  
   351  for tool in curl jq podman; do
   352      type $tool &>/dev/null || die "$ME: Required tool '$tool' not found"
   353  done
   354  
   355  # END   sanity checks
   356  ###############################################################################
   357  # BEGIN entry handler (subtest invoker)
   358  
   359  # Identify the tests to run. If called with args, use those as globs.
   360  tests_to_run=()
   361  if [ -n "$*" ]; then
   362      shopt -s nullglob
   363      for i; do
   364          match=(${TESTS_DIR}/*${i}*.at)
   365          if [ ${#match} -eq 0 ]; then
   366              die "No match for $TESTS_DIR/*$i*.at"
   367          fi
   368          tests_to_run+=("${match[@]}")
   369      done
   370      shopt -u nullglob
   371  else
   372      tests_to_run=($TESTS_DIR/*.at)
   373  fi
   374  
   375  start_service
   376  
   377  for i in ${tests_to_run[@]}; do
   378      TEST_CONTEXT="[$(basename $i .at)]"
   379      source $i
   380  done
   381  
   382  # END   entry handler
   383  ###############################################################################
   384  
   385  # Clean up
   386  
   387  if [ -n "$service_pid" ]; then
   388      kill $service_pid
   389      wait $service_pid
   390  fi
   391  
   392  test_count=$(<$testcounter_file)
   393  failure_count=$(<$failures_file)
   394  
   395  if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then
   396      rm -rf $WORKDIR
   397  fi
   398  
   399  echo "1..${test_count}"
   400  
   401  exit $failure_count