github.com/containers/podman/v5@v5.1.0-rc1/test/system/helpers.bash (about)

     1  # -*- bash -*-
     2  
     3  # Podman command to run; may be podman-remote
     4  PODMAN=${PODMAN:-podman}
     5  QUADLET=${QUADLET:-/usr/libexec/podman/quadlet}
     6  
     7  # crun or runc, unlikely to change. Cache, because it's expensive to determine.
     8  PODMAN_RUNTIME=
     9  
    10  # Standard image to use for most tests
    11  PODMAN_TEST_IMAGE_REGISTRY=${PODMAN_TEST_IMAGE_REGISTRY:-"quay.io"}
    12  PODMAN_TEST_IMAGE_USER=${PODMAN_TEST_IMAGE_USER:-"libpod"}
    13  PODMAN_TEST_IMAGE_NAME=${PODMAN_TEST_IMAGE_NAME:-"testimage"}
    14  PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"20240123"}
    15  PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG"
    16  
    17  # Larger image containing systemd tools.
    18  PODMAN_SYSTEMD_IMAGE_NAME=${PODMAN_SYSTEMD_IMAGE_NAME:-"systemd-image"}
    19  PODMAN_SYSTEMD_IMAGE_TAG=${PODMAN_SYSTEMD_IMAGE_TAG:-"20240124"}
    20  PODMAN_SYSTEMD_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_SYSTEMD_IMAGE_NAME:$PODMAN_SYSTEMD_IMAGE_TAG"
    21  
    22  # Remote image that we *DO NOT* fetch or keep by default; used for testing pull
    23  # This has changed in 2021, from 0 through 3, various iterations of getting
    24  # multiarch to work. It should change only very rarely.
    25  PODMAN_NONLOCAL_IMAGE_TAG=${PODMAN_NONLOCAL_IMAGE_TAG:-"00000004"}
    26  PODMAN_NONLOCAL_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_NONLOCAL_IMAGE_TAG"
    27  
    28  # Because who wants to spell that out each time?
    29  IMAGE=$PODMAN_TEST_IMAGE_FQN
    30  SYSTEMD_IMAGE=$PODMAN_SYSTEMD_IMAGE_FQN
    31  
    32  # Default timeout for a podman command.
    33  PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-120}
    34  
    35  # Prompt to display when logging podman commands; distinguish root/rootless
    36  _LOG_PROMPT='$'
    37  if [ $(id -u) -eq 0 ]; then
    38      _LOG_PROMPT='#'
    39  fi
    40  
    41  ###############################################################################
    42  # BEGIN tools for fetching & caching test images
    43  #
    44  # Registries are flaky: any time we have to pull an image, that's a risk.
    45  #
    46  
    47  # Store in a semipermanent location. Not important for CI, but nice for
    48  # developers so test restarts don't hang fetching images.
    49  export PODMAN_IMAGECACHE=${BATS_TMPDIR:-/tmp}/podman-systest-imagecache-$(id -u)
    50  mkdir -p ${PODMAN_IMAGECACHE}
    51  
    52  function _prefetch() {
    53       local want=$1
    54  
    55       # Do we already have it in image store?
    56       run_podman '?' image exists "$want"
    57       if [[ $status -eq 0 ]]; then
    58           return
    59       fi
    60  
    61      # No image. Do we have it already cached? (Replace / and : with --)
    62      local cachename=$(sed -e 's;[/:];--;g' <<<"$want")
    63      local cachepath="${PODMAN_IMAGECACHE}/${cachename}.tar"
    64      if [[ ! -e "$cachepath" ]]; then
    65          # Not cached. Fetch it and cache it. Retry twice, because of flakes.
    66          cmd="skopeo copy --preserve-digests docker://$want oci-archive:$cachepath"
    67          echo "$_LOG_PROMPT $cmd"
    68          run $cmd
    69          echo "$output"
    70          if [[ $status -ne 0 ]]; then
    71              echo "# 'pull $want' failed, will retry..." >&3
    72              sleep 5
    73  
    74              run $cmd
    75              echo "$output"
    76              if [[ $status -ne 0 ]]; then
    77                  echo "# 'pull $want' failed again, will retry one last time..." >&3
    78                  sleep 30
    79                  $cmd
    80              fi
    81          fi
    82      fi
    83  
    84      # Kludge alert.
    85      # Skopeo has no --storage-driver, --root, or --runroot flags; those
    86      # need to be expressed in the destination string inside [brackets].
    87      # See containers-transports(5). So if we see those options in
    88      # _PODMAN_TEST_OPTS, transmogrify $want into skopeo form.
    89      skopeo_opts=''
    90      driver="$(expr "$_PODMAN_TEST_OPTS" : ".*--storage-driver \([^ ]\+\)" || true)"
    91      if [[ -n "$driver" ]]; then
    92          skopeo_opts+="$driver@"
    93      fi
    94  
    95      altroot="$(expr "$_PODMAN_TEST_OPTS" : ".*--root \([^ ]\+\)" || true)"
    96      if [[ -n "$altroot" ]] && [[ -d "$altroot" ]]; then
    97          skopeo_opts+="$altroot"
    98  
    99          altrunroot="$(expr "$_PODMAN_TEST_OPTS" : ".*--runroot \([^ ]\+\)" || true)"
   100          if [[ -n "$altrunroot" ]] && [[ -d "$altrunroot" ]]; then
   101              skopeo_opts+="+$altrunroot"
   102          fi
   103      fi
   104  
   105      if [[ -n "$skopeo_opts" ]]; then
   106          want="[$skopeo_opts]$want"
   107      fi
   108  
   109      # Cached image is now guaranteed to exist. Be sure to load it
   110      # with skopeo, not podman, in order to preserve metadata
   111      cmd="skopeo copy --all oci-archive:$cachepath containers-storage:$want"
   112      echo "$_LOG_PROMPT $cmd"
   113      $cmd
   114  }
   115  
   116  
   117  # Wrapper for skopeo, because skopeo doesn't work rootless if $XDG is unset
   118  # (as it is in RHEL gating): it defaults to /run/containers/<uid>, which
   119  # of course is a root-only dir, hence fails with permission denied.
   120  # -- https://github.com/containers/skopeo/issues/823
   121  function skopeo() {
   122      local xdg=${XDG_RUNTIME_DIR}
   123      if [ -z "$xdg" ]; then
   124          if is_rootless; then
   125              xdg=/run/user/$(id -u)
   126          fi
   127      fi
   128      XDG_RUNTIME_DIR=${xdg} command skopeo "$@"
   129  }
   130  
   131  # END   tools for fetching & caching test images
   132  ###############################################################################
   133  # BEGIN setup/teardown tools
   134  
   135  # Provide common setup and teardown functions, but do not name them such!
   136  # That way individual tests can override with their own setup/teardown,
   137  # while retaining the ability to include these if they so desire.
   138  
   139  # Setup helper: establish a test environment with exactly the images needed
   140  function basic_setup() {
   141      # Clean up all containers
   142      run_podman rm -t 0 --all --force --ignore
   143  
   144      # ...including external (buildah) ones
   145      run_podman ps --all --external --format '{{.ID}} {{.Names}}'
   146      for line in "${lines[@]}"; do
   147          set $line
   148          echo "# setup(): removing stray external container $1 ($2)" >&3
   149          run_podman '?' rm -f $1
   150          if [[ $status -ne 0 ]]; then
   151              echo "# [setup] $_LOG_PROMPT podman rm -f $1" >&3
   152              for errline in "${lines[@]}"; do
   153                  echo "# $errline" >&3
   154              done
   155          fi
   156      done
   157  
   158      # Clean up all images except those desired.
   159      # 2023-06-26 REMINDER: it is tempting to think that this is clunky,
   160      # wouldn't it be safer/cleaner to just 'rmi -a' then '_prefetch $IMAGE'?
   161      # Yes, but it's also tremendously slower: 29m for a CI run, to 39m.
   162      # Image loads are slow.
   163      found_needed_image=
   164      run_podman '?' images --all --format '{{.Repository}}:{{.Tag}} {{.ID}}'
   165  
   166      for line in "${lines[@]}"; do
   167          set $line
   168          if [[ "$1" == "$PODMAN_TEST_IMAGE_FQN" ]]; then
   169              if [[ -z "$PODMAN_TEST_IMAGE_ID" ]]; then
   170                  # This will probably only trigger the 2nd time through setup
   171                  PODMAN_TEST_IMAGE_ID=$2
   172              fi
   173              found_needed_image=1
   174          elif [[ "$1" == "$PODMAN_SYSTEMD_IMAGE_FQN" ]]; then
   175              # This is a big image, don't force unnecessary pulls
   176              :
   177          else
   178              # Always remove image that doesn't match by name
   179              echo "# setup(): removing stray image $1" >&3
   180              run_podman rmi --force "$1" >/dev/null 2>&1 || true
   181  
   182              # Tagged image will have same IID as our test image; don't rmi it.
   183              if [[ $2 != "$PODMAN_TEST_IMAGE_ID" ]]; then
   184                  echo "# setup(): removing stray image $2" >&3
   185                  run_podman rmi --force "$2" >/dev/null 2>&1 || true
   186              fi
   187          fi
   188      done
   189  
   190      # Make sure desired image is present
   191      if [[ -z "$found_needed_image" ]]; then
   192          _prefetch $PODMAN_TEST_IMAGE_FQN
   193      fi
   194  
   195      # Temporary subdirectory, in which tests can write whatever they like
   196      # and trust that it'll be deleted on cleanup.
   197      # (BATS v1.3 and above provide $BATS_TEST_TMPDIR, but we still use
   198      # ancient BATS (v1.1) in RHEL gating tests.)
   199      PODMAN_TMPDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-/tmp} podman_bats.XXXXXX)
   200  
   201      # runtime is not likely to change
   202      if [[ -z "$PODMAN_RUNTIME" ]]; then
   203          PODMAN_RUNTIME=$(podman_runtime)
   204      fi
   205  
   206      # In the unlikely event that a test runs is() before a run_podman()
   207      MOST_RECENT_PODMAN_COMMAND=
   208  
   209      # Test filenames must match ###-name.bats; use "[###] " as prefix
   210      run expr "$BATS_TEST_FILENAME" : "^.*/\([0-9]\{3\}\)-[^/]\+\.bats\$"
   211      BATS_TEST_NAME_PREFIX="[${output}] "
   212  
   213      # By default, assert() and die() cause an immediate test failure.
   214      # Under special circumstances (usually long test loops), tests
   215      # can call defer-assertion-failures() to continue going, the
   216      # idea being that a large number of failures can show patterns.
   217      ASSERTION_FAILURES=
   218      immediate-assertion-failures
   219  }
   220  
   221  # bail-now is how we terminate a test upon assertion failure.
   222  # By default, and the vast majority of the time, it just triggers
   223  # immediate test termination; but see defer-assertion-failures, below.
   224  function bail-now() {
   225      # "false" does not apply to "bail now"! It means "nonzero exit",
   226      # which BATS interprets as "yes, bail immediately".
   227      false
   228  }
   229  
   230  # Invoked on teardown: will terminate immediately if there have been
   231  # any deferred test failures; otherwise will reset back to immediate
   232  # test termination on future assertions.
   233  function immediate-assertion-failures() {
   234      function bail-now() {
   235          false
   236      }
   237  
   238      # Any backlog?
   239      if [[ -n "$ASSERTION_FAILURES" ]]; then
   240          local n=${#ASSERTION_FAILURES}
   241          ASSERTION_FAILURES=
   242          die "$n test assertions failed. Search for 'FAIL:' above this line." >&2
   243      fi
   244  }
   245  
   246  # Used in special test circumstances--typically multi-condition loops--to
   247  # continue going even on assertion failures. The test will fail afterward,
   248  # usually in teardown. This can be useful to show failure patterns.
   249  function defer-assertion-failures() {
   250      function bail-now() {
   251          ASSERTION_FAILURES+="!"
   252      }
   253  }
   254  
   255  # Basic teardown: remove all pods and containers
   256  function basic_teardown() {
   257      echo "# [teardown]" >&2
   258      local actions=(
   259          "pod rm -t 0 --all --force --ignore"
   260              "rm -t 0 --all --force --ignore"
   261          "network prune --force"
   262          "volume rm -a -f"
   263      )
   264      for action in "${actions[@]}"; do
   265          run_podman '?' $action
   266  
   267          # The -f commands should never exit nonzero, but if they do we want
   268          # to know about it.
   269          #   FIXME: someday: also test for [[ -n "$output" ]] - can't do this
   270          #   yet because too many tests don't clean up their containers
   271          if [[ $status -ne 0 ]]; then
   272              echo "# [teardown] $_LOG_PROMPT podman $action" >&3
   273              for line in "${lines[*]}"; do
   274                  echo "# $line" >&3
   275              done
   276  
   277              # Special case for timeout: check for locks (#18514)
   278              if [[ $status -eq 124 ]]; then
   279                  echo "# [teardown] $_LOG_PROMPT podman system locks" >&3
   280                  run $PODMAN system locks
   281                  for line in "${lines[*]}"; do
   282                      echo "# $line" >&3
   283                  done
   284              fi
   285          fi
   286      done
   287  
   288      command rm -rf $PODMAN_TMPDIR
   289      immediate-assertion-failures
   290  }
   291  
   292  
   293  # Provide the above as default methods.
   294  function setup() {
   295      basic_setup
   296  }
   297  
   298  function teardown() {
   299      basic_teardown
   300  }
   301  
   302  
   303  # Helpers useful for tests running rmi
   304  function archive_image() {
   305      local image=$1
   306  
   307      # FIXME: refactor?
   308      archive_basename=$(echo $1 | tr -c a-zA-Z0-9._- _)
   309      archive=$BATS_TMPDIR/$archive_basename.tar
   310  
   311      run_podman save -o $archive $image
   312  }
   313  
   314  function restore_image() {
   315      local image=$1
   316  
   317      archive_basename=$(echo $1 | tr -c a-zA-Z0-9._- _)
   318      archive=$BATS_TMPDIR/$archive_basename.tar
   319  
   320      run_podman restore $archive
   321  }
   322  
   323  # END   setup/teardown tools
   324  ###############################################################################
   325  # BEGIN podman helpers
   326  
   327  # Displays '[HH:MM:SS.NNNNN]' in command output. logformatter relies on this.
   328  function timestamp() {
   329      date +'[%T.%N]'
   330  }
   331  
   332  ################
   333  #  run_podman  #  Invoke $PODMAN, with timeout, using BATS 'run'
   334  ################
   335  #
   336  # This is the preferred mechanism for invoking podman: first, it
   337  # invokes $PODMAN, which may be 'podman-remote' or '/some/path/podman'.
   338  #
   339  # Second, we use 'timeout' to abort (with a diagnostic) if something
   340  # takes too long; this is preferable to a CI hang.
   341  #
   342  # Third, we log the command run and its output. This doesn't normally
   343  # appear in BATS output, but it will if there's an error.
   344  #
   345  # Next, we check exit status. Since the normal desired code is 0,
   346  # that's the default; but the first argument can override:
   347  #
   348  #     run_podman 125  nonexistent-subcommand
   349  #     run_podman '?'  some-other-command       # let our caller check status
   350  #
   351  # Since we use the BATS 'run' mechanism, $output and $status will be
   352  # defined for our caller.
   353  #
   354  function run_podman() {
   355      # Number as first argument = expected exit code; default 0
   356      # "0+[we]" = require success, but allow warnings/errors
   357      local expected_rc=0
   358      local allowed_levels="dit"
   359      case "$1" in
   360          0\+[we]*)        allowed_levels+=$(expr "$1" : "^0+\([we]\+\)"); shift;;
   361          [0-9])           expected_rc=$1; shift;;
   362          [1-9][0-9])      expected_rc=$1; shift;;
   363          [12][0-9][0-9])  expected_rc=$1; shift;;
   364          '?')             expected_rc=  ; shift;;  # ignore exit code
   365      esac
   366  
   367      # Remember command args, for possible use in later diagnostic messages
   368      MOST_RECENT_PODMAN_COMMAND="podman $*"
   369  
   370      # BATS >= 1.5.0 treats 127 as a special case, adding a big nasty warning
   371      # at the end of the test run if any command exits thus. Silence it.
   372      #   https://bats-core.readthedocs.io/en/stable/warnings/BW01.html
   373      local silence127=
   374      if [[ "$expected_rc" = "127" ]]; then
   375          # We could use "-127", but that would cause BATS to fail if the
   376          # command exits any other status -- and default BATS failure messages
   377          # are much less helpful than the run_podman ones. "!" is more flexible.
   378          silence127="!"
   379      fi
   380  
   381      # stdout is only emitted upon error; this printf is to help in debugging
   382      printf "\n%s %s %s %s\n" "$(timestamp)" "$_LOG_PROMPT" "$PODMAN" "$*"
   383      # BATS hangs if a subprocess remains and keeps FD 3 open; this happens
   384      # if podman crashes unexpectedly without cleaning up subprocesses.
   385      run $silence127 timeout --foreground -v --kill=10 $PODMAN_TIMEOUT $PODMAN $_PODMAN_TEST_OPTS "$@" 3>/dev/null
   386      # without "quotes", multiple lines are glommed together into one
   387      if [ -n "$output" ]; then
   388          echo "$(timestamp) $output"
   389  
   390          # FIXME FIXME FIXME: instrumenting to track down #15488. Please
   391          # remove once that's fixed. We include the args because, remember,
   392          # bats only shows output on error; it's possible that the first
   393          # instance of the metacopy warning happens in a test that doesn't
   394          # check output, hence doesn't fail.
   395          if [[ "$output" =~ Ignoring.global.metacopy.option ]]; then
   396              echo "# YO! metacopy warning triggered by: podman $*" >&3
   397          fi
   398      fi
   399      if [ "$status" -ne 0 ]; then
   400          echo -n "$(timestamp) [ rc=$status ";
   401          if [ -n "$expected_rc" ]; then
   402              if [ "$status" -eq "$expected_rc" ]; then
   403                  echo -n "(expected) ";
   404              else
   405                  echo -n "(** EXPECTED $expected_rc **) ";
   406              fi
   407          fi
   408          echo "]"
   409      fi
   410  
   411      if [ "$status" -eq 124 ]; then
   412          if expr "$output" : ".*timeout: sending" >/dev/null; then
   413              # It's possible for a subtest to _want_ a timeout
   414              if [[ "$expected_rc" != "124" ]]; then
   415                  echo "*** TIMED OUT ***"
   416                  false
   417              fi
   418          fi
   419      fi
   420  
   421      if [ -n "$expected_rc" ]; then
   422          if [ "$status" -ne "$expected_rc" ]; then
   423              die "exit code is $status; expected $expected_rc"
   424          fi
   425      fi
   426  
   427      # Check for "level=<unexpected>" in output, because a successful command
   428      # should never issue unwanted warnings or errors. The "0+w" convention
   429      # (see top of function) allows our caller to indicate that warnings are
   430      # expected, e.g., "podman stop" without -t0.
   431      if [[ $status -eq 0 ]]; then
   432          # FIXME: don't do this on Debian or RHEL. runc is way too buggy:
   433          #   - #11784 - lstat /sys/fs/.../*.scope: ENOENT
   434          #   - #11785 - cannot toggle freezer: cgroups not configured
   435          # As of January 2024 the freezer one seems to be fixed in Debian-runc
   436          # but not in RHEL8-runc. The lstat one is closed-wontfix.
   437          if [[ $PODMAN_RUNTIME != "runc" ]]; then
   438              # FIXME: All kube commands emit unpredictable errors:
   439              #    "Storage for container <X> has been removed"
   440              #    "no container with ID <X> found in database"
   441              # These are level=error but we still get exit-status 0.
   442              # Just skip all kube commands completely
   443              if [[ ! "$*" =~ kube ]]; then
   444                  if [[ "$output" =~ level=[^${allowed_levels}] ]]; then
   445                      die "Command succeeded, but issued unexpected warnings"
   446                  fi
   447              fi
   448          fi
   449      fi
   450  }
   451  
   452  
   453  # Wait for certain output from a container, indicating that it's ready.
   454  function wait_for_output {
   455      local sleep_delay=1
   456      local how_long=$PODMAN_TIMEOUT
   457      local expect=
   458      local cid=
   459  
   460      # Arg processing. A single-digit number is how long to sleep between
   461      # iterations; a 2- or 3-digit number is the total time to wait; all
   462      # else are, in order, the string to expect and the container name/ID.
   463      local i
   464      for i in "$@"; do
   465          if expr "$i" : '[0-9]\+$' >/dev/null; then
   466              if [ $i -le 9 ]; then
   467                  sleep_delay=$i
   468              else
   469                  how_long=$i
   470              fi
   471          elif [ -z "$expect" ]; then
   472              expect=$i
   473          else
   474              cid=$i
   475          fi
   476      done
   477  
   478      [ -n "$cid" ] || die "FATAL: wait_for_output: no container name/ID in '$*'"
   479  
   480      t1=$(expr $SECONDS + $how_long)
   481      while [ $SECONDS -lt $t1 ]; do
   482          run_podman 0+w logs $cid
   483          logs=$output
   484          if expr "$logs" : ".*$expect" >/dev/null; then
   485              return
   486          fi
   487  
   488          # Barf if container is not running
   489          run_podman inspect --format '{{.State.Running}}' $cid
   490          if [ $output != "true" ]; then
   491              run_podman inspect --format '{{.State.ExitCode}}' $cid
   492              exitcode=$output
   493  
   494              # One last chance: maybe the container exited just after logs cmd
   495              run_podman 0+w logs $cid
   496              if expr "$logs" : ".*$expect" >/dev/null; then
   497                  return
   498              fi
   499  
   500              die "Container exited (status: $exitcode) before we saw '$expect': $logs"
   501          fi
   502  
   503          sleep $sleep_delay
   504      done
   505  
   506      die "timed out waiting for '$expect' from $cid"
   507  }
   508  
   509  # Shortcut for the lazy
   510  function wait_for_ready {
   511      wait_for_output 'READY' "$@"
   512  }
   513  
   514  ###################
   515  #  wait_for_file  #  Returns once file is available on host
   516  ###################
   517  function wait_for_file() {
   518      local file=$1                       # The path to the file
   519      local _timeout=${2:-5}              # Optional; default 5 seconds
   520  
   521      # Wait
   522      while [ $_timeout -gt 0 ]; do
   523          test -e $file && return
   524          sleep 1
   525          _timeout=$(( $_timeout - 1 ))
   526      done
   527  
   528      die "Timed out waiting for $file"
   529  }
   530  
   531  ###########################
   532  #  wait_for_file_content  #  Like wait_for_output, but with files (not ctrs)
   533  ###########################
   534  function wait_for_file_content() {
   535      local file=$1                       # The path to the file
   536      local content=$2                    # What to expect in the file
   537      local _timeout=${3:-5}              # Optional; default 5 seconds
   538  
   539      while :; do
   540          grep -q "$content" "$file" && return
   541  
   542          test $_timeout -gt 0 || die "Timed out waiting for '$content' in $file"
   543  
   544          _timeout=$(( $_timeout - 1 ))
   545          sleep 1
   546  
   547          # For debugging. Note that file does not necessarily exist yet.
   548          if [[ -e "$file" ]]; then
   549              echo "[ wait_for_file_content: retrying wait for '$content' in: ]"
   550              sed -e 's/^/[ /' -e 's/$/ ]/' <"$file"
   551          else
   552              echo "[ wait_for_file_content: $file does not exist (yet) ]"
   553          fi
   554      done
   555  }
   556  
   557  # END   podman helpers
   558  ###############################################################################
   559  # BEGIN miscellaneous tools
   560  
   561  # Shortcuts for common needs:
   562  function is_rootless() {
   563      [ "$(id -u)" -ne 0 ]
   564  }
   565  
   566  function is_remote() {
   567      [[ "$PODMAN" =~ -remote ]]
   568  }
   569  
   570  function is_cgroupsv1() {
   571      # WARNING: This will break if there's ever a cgroups v3
   572      ! is_cgroupsv2
   573  }
   574  
   575  # True if cgroups v2 are enabled
   576  function is_cgroupsv2() {
   577      cgroup_type=$(stat -f -c %T /sys/fs/cgroup)
   578      test "$cgroup_type" = "cgroup2fs"
   579  }
   580  
   581  # True if podman is using netavark
   582  function is_netavark() {
   583      run_podman info --format '{{.Host.NetworkBackend}}'
   584      if [[ "$output" =~ netavark ]]; then
   585          return 0
   586      fi
   587      return 1
   588  }
   589  
   590  function is_aarch64() {
   591      [ "$(uname -m)" == "aarch64" ]
   592  }
   593  
   594  function selinux_enabled() {
   595      /usr/sbin/selinuxenabled 2> /dev/null
   596  }
   597  
   598  # Returns the OCI runtime *basename* (typically crun or runc). Much as we'd
   599  # love to cache this result, we probably shouldn't.
   600  function podman_runtime() {
   601      # This function is intended to be used as '$(podman_runtime)', i.e.
   602      # our caller wants our output. It's unsafe to use run_podman().
   603      runtime=$($PODMAN $_PODMAN_TEST_OPTS info --format '{{ .Host.OCIRuntime.Name }}' 2>/dev/null)
   604      basename "${runtime:-[null]}"
   605  }
   606  
   607  # Returns the storage driver: 'overlay' or 'vfs'
   608  function podman_storage_driver() {
   609      run_podman info --format '{{.Store.GraphDriverName}}' >/dev/null
   610      # Should there ever be a new driver
   611      case "$output" in
   612          overlay) ;;
   613          vfs)     ;;
   614          *)       die "Unknown storage driver '$output'; if this is a new driver, please review uses of this function in tests." ;;
   615      esac
   616      echo "$output"
   617  }
   618  
   619  # Given a (scratch) directory path, returns a set of command-line options
   620  # for running an isolated podman that will not step on system podman. Set:
   621  #  - rootdir, so we don't clobber real images or storage;
   622  #  - tmpdir, so we use an isolated DB; and
   623  #  - runroot, out of an abundance of paranoia
   624  function podman_isolation_opts() {
   625      local path=${1?podman_isolation_opts: missing PATH arg}
   626  
   627      for opt in root runroot tmpdir;do
   628          mkdir -p $path/$opt
   629          echo " --$opt $path/$opt"
   630      done
   631  }
   632  
   633  # rhbz#1895105: rootless journald is unavailable except to users in
   634  # certain magic groups; which our testuser account does not belong to
   635  # (intentional: that is the RHEL default, so that's the setup we test).
   636  function journald_unavailable() {
   637      if ! is_rootless; then
   638          # root must always have access to journal
   639          return 1
   640      fi
   641  
   642      run journalctl -n 1
   643      if [[ $status -eq 0 ]]; then
   644          return 1
   645      fi
   646  
   647      if [[ $output =~ permission ]]; then
   648          return 0
   649      fi
   650  
   651      # This should never happen; if it does, it's likely that a subsequent
   652      # test will fail. This output may help track that down.
   653      echo "WEIRD: 'journalctl -n 1' failed with a non-permission error:"
   654      echo "$output"
   655      return 1
   656  }
   657  
   658  # Returns the name of the local pause image.
   659  function pause_image() {
   660      # This function is intended to be used as '$(pause_image)', i.e.
   661      # our caller wants our output. run_podman() messes with output because
   662      # it emits the command invocation to stdout, hence the redirection.
   663      run_podman version --format "{{.Server.Version}}-{{.Server.Built}}" >/dev/null
   664      echo "localhost/podman-pause:$output"
   665  }
   666  
   667  # Wait for the pod (1st arg) to transition into the state (2nd arg)
   668  function _ensure_pod_state() {
   669      for i in {0..5}; do
   670          run_podman pod inspect $1 --format "{{.State}}"
   671          if [[ $output == "$2" ]]; then
   672              return
   673          fi
   674          sleep 0.5
   675      done
   676  
   677      die "Timed out waiting for pod $1 to enter state $2"
   678  }
   679  
   680  # Wait for the container's (1st arg) running state (2nd arg)
   681  function _ensure_container_running() {
   682      for i in {0..20}; do
   683          run_podman container inspect $1 --format "{{.State.Running}}"
   684          if [[ $output == "$2" ]]; then
   685              return
   686          fi
   687          sleep 0.5
   688      done
   689  
   690      die "Timed out waiting for container $1 to enter state running=$2"
   691  }
   692  
   693  ###########################
   694  #  _add_label_if_missing  #  make sure skip messages include rootless/remote
   695  ###########################
   696  function _add_label_if_missing() {
   697      local msg="$1"
   698      local want="$2"
   699  
   700      if [ -z "$msg" ]; then
   701          echo
   702      elif expr "$msg" : ".*$want" &>/dev/null; then
   703          echo "$msg"
   704      else
   705          echo "[$want] $msg"
   706      fi
   707  }
   708  
   709  ######################
   710  #  skip_if_no_ssh #  ...with an optional message
   711  ######################
   712  function skip_if_no_ssh() {
   713      if no_ssh; then
   714          local msg=$(_add_label_if_missing "$1" "ssh")
   715          skip "${msg:-not applicable with no ssh binary}"
   716      fi
   717  }
   718  
   719  ######################
   720  #  skip_if_rootless  #  ...with an optional message
   721  ######################
   722  function skip_if_rootless() {
   723      if is_rootless; then
   724          local msg=$(_add_label_if_missing "$1" "rootless")
   725          skip "${msg:-not applicable under rootless podman}"
   726      fi
   727  }
   728  
   729  ######################
   730  #  skip_if_not_rootless  #  ...with an optional message
   731  ######################
   732  function skip_if_not_rootless() {
   733      if ! is_rootless; then
   734          local msg=$(_add_label_if_missing "$1" "rootful")
   735          skip "${msg:-not applicable under rootlfull podman}"
   736      fi
   737  }
   738  
   739  ####################
   740  #  skip_if_remote  #  ...with an optional message
   741  ####################
   742  function skip_if_remote() {
   743      if is_remote; then
   744          local msg=$(_add_label_if_missing "$1" "remote")
   745          skip "${msg:-test does not work with podman-remote}"
   746      fi
   747  }
   748  
   749  ########################
   750  #  skip_if_no_selinux  #
   751  ########################
   752  function skip_if_no_selinux() {
   753      if [ ! -e /usr/sbin/selinuxenabled ]; then
   754          skip "selinux not available"
   755      elif ! /usr/sbin/selinuxenabled; then
   756          skip "selinux disabled"
   757      fi
   758  }
   759  
   760  #######################
   761  #  skip_if_cgroupsv1  #  ...with an optional message
   762  #######################
   763  function skip_if_cgroupsv1() {
   764      if ! is_cgroupsv2; then
   765          skip "${1:-test requires cgroupsv2}"
   766      fi
   767  }
   768  
   769  #######################
   770  #  skip_if_cgroupsv2  #  ...with an optional message
   771  #######################
   772  function skip_if_cgroupsv2() {
   773      if is_cgroupsv2; then
   774          skip "${1:-test requires cgroupsv1}"
   775      fi
   776  }
   777  
   778  ######################
   779  #  skip_if_rootless_cgroupsv1  #  ...with an optional message
   780  ######################
   781  function skip_if_rootless_cgroupsv1() {
   782      if is_rootless; then
   783          if ! is_cgroupsv2; then
   784              local msg=$(_add_label_if_missing "$1" "rootless cgroupvs1")
   785              skip "${msg:-not supported as rootless under cgroupsv1}"
   786          fi
   787      fi
   788  }
   789  
   790  ##################################
   791  #  skip_if_journald_unavailable  #  rhbz#1895105: rootless journald permissions
   792  ##################################
   793  function skip_if_journald_unavailable {
   794      if journald_unavailable; then
   795          skip "Cannot use rootless journald on this system"
   796      fi
   797  }
   798  
   799  function skip_if_aarch64 {
   800      if is_aarch64; then
   801          skip "${msg:-Cannot run this test on aarch64 systems}"
   802      fi
   803  }
   804  
   805  #########
   806  #  die  #  Abort with helpful message
   807  #########
   808  function die() {
   809      # FIXME: handle multi-line output
   810      echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"  >&2
   811      echo "#| FAIL: $*"                                           >&2
   812      echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
   813      bail-now
   814  }
   815  
   816  ############
   817  #  assert  #  Compare actual vs expected string; fail if mismatch
   818  ############
   819  #
   820  # Compares string (default: $output) against the given string argument.
   821  # By default we do an exact-match comparison against $output, but there
   822  # are two different ways to invoke us, each with an optional description:
   823  #
   824  #      assert               "EXPECT" [DESCRIPTION]
   825  #      assert "RESULT" "OP" "EXPECT" [DESCRIPTION]
   826  #
   827  # The first form (one or two arguments) does an exact-match comparison
   828  # of "$output" against "EXPECT". The second (three or four args) compares
   829  # the first parameter against EXPECT, using the given OPerator. If present,
   830  # DESCRIPTION will be displayed on test failure.
   831  #
   832  # Examples:
   833  #
   834  #   assert "this is exactly what we expect"
   835  #   assert "${lines[0]}" =~ "^abc"  "first line begins with abc"
   836  #
   837  function assert() {
   838      local actual_string="$output"
   839      local operator='=='
   840      local expect_string="$1"
   841      local testname="$2"
   842  
   843      case "${#*}" in
   844          0)   die "Internal error: 'assert' requires one or more arguments" ;;
   845          1|2) ;;
   846          3|4) actual_string="$1"
   847               operator="$2"
   848               expect_string="$3"
   849               testname="$4"
   850               ;;
   851          *)   die "Internal error: too many arguments to 'assert'" ;;
   852      esac
   853  
   854      # Comparisons.
   855      # Special case: there is no !~ operator, so fake it via '! x =~ y'
   856      local not=
   857      local actual_op="$operator"
   858      if [[ $operator == '!~' ]]; then
   859          not='!'
   860          actual_op='=~'
   861      fi
   862      if [[ $operator == '=' || $operator == '==' ]]; then
   863          # Special case: we can't use '=' or '==' inside [[ ... ]] because
   864          # the right-hand side is treated as a pattern... and '[xy]' will
   865          # not compare literally. There seems to be no way to turn that off.
   866          if [ "$actual_string" = "$expect_string" ]; then
   867              return
   868          fi
   869      elif [[ $operator == '!=' ]]; then
   870          # Same special case as above
   871          if [ "$actual_string" != "$expect_string" ]; then
   872              return
   873          fi
   874      else
   875          if eval "[[ $not \$actual_string $actual_op \$expect_string ]]"; then
   876              return
   877          elif [ $? -gt 1 ]; then
   878              die "Internal error: could not process 'actual' $operator 'expect'"
   879          fi
   880      fi
   881  
   882      # Test has failed. Get a descriptive test name.
   883      if [ -z "$testname" ]; then
   884          testname="${MOST_RECENT_PODMAN_COMMAND:-[no test name given]}"
   885      fi
   886  
   887      # Display optimization: the typical case for 'expect' is an
   888      # exact match ('='), but there are also '=~' or '!~' or '-ge'
   889      # and the like. Omit the '=' but show the others; and always
   890      # align subsequent output lines for ease of comparison.
   891      local op=''
   892      local ws=''
   893      if [ "$operator" != '==' ]; then
   894          op="$operator "
   895          ws=$(printf "%*s" ${#op} "")
   896      fi
   897  
   898      # This is a multi-line message, which may in turn contain multi-line
   899      # output, so let's format it ourself to make it more readable.
   900      local expect_split
   901      mapfile -t expect_split <<<"$expect_string"
   902      local actual_split
   903      mapfile -t actual_split <<<"$actual_string"
   904  
   905      # bash %q is really nice, except for the way it backslashes spaces
   906      local -a expect_split_q
   907      for line in "${expect_split[@]}"; do
   908          local q=$(printf "%q" "$line" | sed -e 's/\\ / /g')
   909          expect_split_q+=("$q")
   910      done
   911      local -a actual_split_q
   912      for line in "${actual_split[@]}"; do
   913          local q=$(printf "%q" "$line" | sed -e 's/\\ / /g')
   914          actual_split_q+=("$q")
   915      done
   916  
   917      printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n"    >&2
   918      printf "#|     FAIL: %s\n" "$testname"                        >&2
   919      printf "#| expected: %s%s\n" "$op" "${expect_split_q[0]}"     >&2
   920      local line
   921      for line in "${expect_split_q[@]:1}"; do
   922          printf "#|         > %s%s\n" "$ws" "$line"                >&2
   923      done
   924      printf "#|   actual: %s%s\n" "$ws" "${actual_split_q[0]}"     >&2
   925      for line in "${actual_split_q[@]:1}"; do
   926          printf "#|         > %s%s\n" "$ws" "$line"                >&2
   927      done
   928      printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"   >&2
   929      bail-now
   930  }
   931  
   932  ########
   933  #  is  #  **DEPRECATED**; see assert() above
   934  ########
   935  function is() {
   936      local actual="$1"
   937      local expect="$2"
   938      local testname="${3:-${MOST_RECENT_PODMAN_COMMAND:-[no test name given]}}"
   939  
   940      local is_expr=
   941      if [ -z "$expect" ]; then
   942          if [ -z "$actual" ]; then
   943              # Both strings are empty.
   944              return
   945          fi
   946          expect='[no output]'
   947      elif [[ "$actual" = "$expect" ]]; then
   948          # Strings are identical.
   949          return
   950      else
   951          # Strings are not identical. Are there wild cards in our expect string?
   952          if expr "$expect" : ".*[^\\][\*\[]" >/dev/null; then
   953              # There is a '[' or '*' without a preceding backslash.
   954              is_expr=' (using expr)'
   955          elif [[ "${expect:0:1}" = '[' ]]; then
   956              # String starts with '[', e.g. checking seconds like '[345]'
   957              is_expr=' (using expr)'
   958          fi
   959          if [[ -n "$is_expr" ]]; then
   960              if expr "$actual" : "$expect" >/dev/null; then
   961                  return
   962              fi
   963          fi
   964      fi
   965  
   966      # This is a multi-line message, which may in turn contain multi-line
   967      # output, so let's format it ourself to make it more readable.
   968      local -a actual_split
   969      readarray -t actual_split <<<"$actual"
   970      printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
   971      printf "#|     FAIL: $testname\n"                          >&2
   972      printf "#| expected: '%s'%s\n" "$expect" "$is_expr"        >&2
   973      printf "#|   actual: '%s'\n" "${actual_split[0]}"          >&2
   974      local line
   975      for line in "${actual_split[@]:1}"; do
   976          printf "#|         > '%s'\n" "$line"                   >&2
   977      done
   978      printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2
   979      bail-now
   980  }
   981  
   982  ####################
   983  #  allow_warnings  #  check cmd output for warning messages other than these
   984  ####################
   985  #
   986  # HEADS UP: Operates on '$lines' array, so, must be invoked after run_podman
   987  #
   988  function allow_warnings() {
   989      for line in "${lines[@]}"; do
   990          if [[ "$line" =~ level=[we] ]]; then
   991              local ok=
   992              for pattern in "$@"; do
   993                  if [[ "$line" =~ $pattern ]]; then
   994                     ok=ok
   995                  fi
   996              done
   997              if [[ -z "$ok" ]]; then
   998                  die "Unexpected warning/error in command results: $line"
   999              fi
  1000          fi
  1001      done
  1002  }
  1003  
  1004  #####################
  1005  #  require_warning  #  Require the given message, but disallow any others
  1006  #####################
  1007  # Optional 2nd argument is a message to display if warning is missing
  1008  function require_warning() {
  1009      local expect="$1"
  1010      local msg="${2:-Did not find expected warning/error message}"
  1011      assert "$output" =~ "$expect" "$msg"
  1012      allow_warnings "$expect"
  1013  }
  1014  
  1015  ############
  1016  #  dprint  #  conditional debug message
  1017  ############
  1018  #
  1019  # Set PODMAN_TEST_DEBUG to the name of one or more functions you want to debug
  1020  #
  1021  # Examples:
  1022  #
  1023  #    $ PODMAN_TEST_DEBUG=parse_table bats .
  1024  #    $ PODMAN_TEST_DEBUG="test_podman_images test_podman_run" bats .
  1025  #
  1026  function dprint() {
  1027      test -z "$PODMAN_TEST_DEBUG" && return
  1028  
  1029      caller="${FUNCNAME[1]}"
  1030  
  1031      # PODMAN_TEST_DEBUG is a space-separated list of desired functions
  1032      # e.g. "parse_table test_podman_images" (or even just "table")
  1033      for want in $PODMAN_TEST_DEBUG; do
  1034          # Check if our calling function matches any of the desired strings
  1035          if expr "$caller" : ".*$want" >/dev/null; then
  1036              echo "# ${FUNCNAME[1]}() : $*" >&3
  1037              return
  1038          fi
  1039      done
  1040  }
  1041  
  1042  
  1043  #################
  1044  #  parse_table  #  Split a table on '|' delimiters; return space-separated
  1045  #################
  1046  #
  1047  # See sample .bats scripts for examples. The idea is to list a set of
  1048  # tests in a table, then use simple logic to iterate over each test.
  1049  # Columns are separated using '|' (pipe character) because sometimes
  1050  # we need spaces in our fields.
  1051  #
  1052  function parse_table() {
  1053      while read line; do
  1054          test -z "$line" && continue
  1055  
  1056          declare -a row=()
  1057          while read col; do
  1058              dprint "col=<<$col>>"
  1059              row+=("$col")
  1060          done <  <(echo "$line" | sed -E -e 's/(^|\s)\|(\s|$)/\n /g' | sed -e 's/^ *//' -e 's/\\/\\\\/g')
  1061          # the above seds:
  1062          #   1) Convert '|' to newline, but only if bracketed by spaces or
  1063          #      at beginning/end of line (this allows 'foo|bar' in tests);
  1064          #   2) then remove leading whitespace;
  1065          #   3) then double-escape all backslashes
  1066  
  1067          printf "%q " "${row[@]}"
  1068          printf "\n"
  1069      done <<<"$1"
  1070  }
  1071  
  1072  
  1073  ###################
  1074  #  random_string  #  Returns a pseudorandom human-readable string
  1075  ###################
  1076  #
  1077  # Numeric argument, if present, is desired length of string
  1078  #
  1079  function random_string() {
  1080      local length=${1:-10}
  1081  
  1082      head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
  1083  }
  1084  
  1085  #########################
  1086  #  find_exec_pid_files  #  Returns nothing or exec_pid hash files
  1087  #########################
  1088  #
  1089  # Return exec_pid hash files if exists, otherwise, return nothing
  1090  #
  1091  function find_exec_pid_files() {
  1092      run_podman info --format '{{.Store.RunRoot}}'
  1093      local storage_path="$output"
  1094      if [ -d $storage_path ]; then
  1095          find $storage_path -type f -iname 'exec_pid_*'
  1096      fi
  1097  }
  1098  
  1099  
  1100  #############################
  1101  #  remove_same_dev_warning  #  Filter out useless warning from output
  1102  #############################
  1103  #
  1104  # On some CI systems, 'podman run --privileged' emits a useless warning:
  1105  #
  1106  #    WARNING: The same type, major and minor should not be used for multiple devices.
  1107  #
  1108  # This obviously screws us up when we look at output results.
  1109  #
  1110  # This function removes the warning from $output and $lines. We don't
  1111  # do a full string match because there's another variant of that message:
  1112  #
  1113  #    WARNING: Creating device "/dev/null" with same type, major and minor as existing "/dev/foodevdir/null".
  1114  #
  1115  # (We should never again see that precise error ever again, but we could
  1116  # see variants of it).
  1117  #
  1118  function remove_same_dev_warning() {
  1119      # No input arguments. We operate in-place on $output and $lines
  1120  
  1121      local i=0
  1122      local -a new_lines=()
  1123      while [[ $i -lt ${#lines[@]} ]]; do
  1124          if expr "${lines[$i]}" : 'WARNING: .* same type, major' >/dev/null; then
  1125              :
  1126          else
  1127              new_lines+=("${lines[$i]}")
  1128          fi
  1129          i=$(( i + 1 ))
  1130      done
  1131  
  1132      lines=("${new_lines[@]}")
  1133      output=$(printf '%s\n' "${lines[@]}")
  1134  }
  1135  
  1136  # run 'podman help', parse the output looking for 'Available Commands';
  1137  # return that list.
  1138  function _podman_commands() {
  1139      dprint "$@"
  1140      # &>/dev/null prevents duplicate output
  1141      run_podman help "$@" &>/dev/null
  1142      awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' <<<"$output" | grep .
  1143  }
  1144  
  1145  ##########################
  1146  #  sleep_to_next_second  #  Sleep until second rolls over
  1147  ##########################
  1148  
  1149  function sleep_to_next_second() {
  1150      sleep 0.$(printf '%04d' $((10000 - 10#$(date +%4N))))
  1151  }
  1152  
  1153  function wait_for_command_output() {
  1154      local cmd="$1"
  1155      local want="$2"
  1156      local tries=20
  1157      local sleep_delay=0.5
  1158  
  1159      case "${#*}" in
  1160          2) ;;
  1161          4) tries="$3"
  1162             sleep_delay="$4"
  1163             ;;
  1164          *) die "Internal error: 'wait_for_command_output' requires two or four arguments" ;;
  1165      esac
  1166  
  1167      while [[ $tries -gt 0 ]]; do
  1168          echo "$_LOG_PROMPT $cmd"
  1169          run $cmd
  1170          echo "$output"
  1171          if [[ "$output" = "$want" ]]; then
  1172              return
  1173          fi
  1174  
  1175          sleep $sleep_delay
  1176          tries=$((tries - 1))
  1177      done
  1178      die "Timed out waiting for '$cmd' to return '$want'"
  1179  }
  1180  
  1181  # END   miscellaneous tools
  1182  ###############################################################################