#!/usr/bin/env bash # Podman and Toolbox commands to run PODMAN=${PODMAN:-podman} TOOLBOX=${TOOLBOX:-toolbox} # Helpful globals LATEST_FEDORA_VERSION=${LATEST_FEDORA_VERSION:-"32"} DEFAULT_FEDORA_VERSION=${DEFAULT_FEDORA_VERSION:-"f31"} REGISTRY_URL=${REGISTRY_URL:-"registry.fedoraproject.org"} TOOLBOX_DEFAULT_IMAGE=${TOOLBOX_DEFAULT_IMAGE:-"registry.fedoraproject.org/f31/fedora-toolbox:31"} TOOLBOX_TIMEOUT=${TOOLBOX_TIMEOUT:-100} PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-100} ################ # run_podman # Invoke $PODMAN, with timeout, using BATS 'run' ################ # # This is the preferred mechanism for invoking podman: first, it # invokes $PODMAN, which may be 'podman-remote' or '/some/path/podman'. # # Second, we log the command run and its output. This doesn't normally # appear in BATS output, but it will if there's an error. # # Next, we check exit status. Since the normal desired code is 0, # that's the default; but the first argument can override: # # run_podman 125 nonexistent-subcommand # run_podman '?' some-other-command # let our caller check status # # Since we use the BATS 'run' mechanism, $output and $status will be # defined for our caller. # function run_podman() { # Number as first argument = expected exit code; default 0 expected_rc=0 case "$1" in [0-9]) expected_rc=$1; shift;; [1-9][0-9]) expected_rc=$1; shift;; [12][0-9][0-9]) expected_rc=$1; shift;; '?') expected_rc= ; shift;; # ignore exit code esac # stdout is only emitted upon error; this echo is to help a debugger echo "\$ $PODMAN $*" run timeout --foreground -v --kill=10 $PODMAN_TIMEOUT $PODMAN "$@" 3>/dev/null # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" fi if [ "$status" -ne 0 ]; then echo -n "[ rc=$status "; if [ -n "$expected_rc" ]; then if [ "$status" -eq "$expected_rc" ]; then echo -n "(expected) "; else echo -n "(** EXPECTED $expected_rc **) "; fi fi echo "]" fi if [ -n "$expected_rc" ]; then if [ "$status" -ne "$expected_rc" ]; then die "exit code is $status; expected $expected_rc" fi fi } function run_toolbox() { # Number as first argument = expected exit code; default 0 expected_rc=0 case "$1" in [0-9]) expected_rc=$1; shift;; [1-9][0-9]) expected_rc=$1; shift;; [12][0-9][0-9]) expected_rc=$1; shift;; '?') expected_rc= ; shift;; # ignore exit code esac # stdout is only emitted upon error; this echo is to help a debugger echo "\$ $TOOLBOX $*" run timeout --foreground -v --kill=10 $TOOLBOX_TIMEOUT $TOOLBOX "$@" 3>/dev/null # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" fi if [ "$status" -ne 0 ]; then echo -n "[ rc=$status "; if [ -n "$expected_rc" ]; then if [ "$status" -eq "$expected_rc" ]; then echo -n "(expected) "; else echo -n "(** EXPECTED $expected_rc **) "; fi fi echo "]" fi if [ -n "$expected_rc" ]; then if [ "$status" -ne "$expected_rc" ]; then die "exit code is $status; expected $expected_rc" fi fi } # Toolbox helper functions function is_toolbox_ready() { toolbox_container="$1" expected_string="Listening to file system and ticker events" num_of_tries=5 timeout=2 run_podman logs $toolbox_container for ((i = 0; i < $num_of_tries; i++)); do if [[ "$output" =~ .*"$expected_string".* ]]; then return fi sleep $timeout run_podman logs $toolbox_container done echo "Output of 'podman logs $toolbox_container':" echo "$output" echo "" die "container $toolbox_container was not ready in time" } # Functions to prepare environment function create_toolbox() { echo "# [create_toolbox]" local numberof="$1" local naming="$2" local image="$3" if [ "$numberof" = "" ]; then numberof=${numberof:-1} fi if [ "$image" = "" ]; then image=$TOOLBOX_DEFAULT_IMAGE fi for i in $(seq "$numberof"); do if [ "$naming" = "" ]; then run_toolbox '?' -y create -i "$image" else run_toolbox '?' -y create -c "$naming-$i" -i "$image" fi done } function get_images() { echo "# [get_images]" local numberof="$1" local image="" if [ "$numberof" = "" ]; then numberof=${numberof:-1} fi for i in $(seq $numberof); do local version=$[$LATEST_FEDORA_VERSION-$i] image="$REGISTRY_URL/f$version/fedora-toolbox:$version" run_podman pull "$image" || echo "Podman couldn't pull the image." done } function remove_all_images() { echo "# [remove_all_images]" run_podman images --all --format '{{.Repository}}:{{.Tag}} {{.ID}}' for line in "${lines[@]}"; do set $line run_podman rmi --force "$1" >/dev/null 2>&1 || true run_podman rmi --force "$2" >/dev/null 2>&1 || true done } function remove_all_images_but_default() { echo "# [remove_all_images_but_default]" found_needed_image=1 run_podman images --all --format '{{.Repository}}:{{.Tag}} {{.ID}}' for line in "${lines[@]}"; do set $line if [ "$1" == "$TOOLBOX_DEFAULT_IMAGE" ]; then found_needed_image=1 else run_podman rmi --force "$1" >/dev/null 2>&1 || true fi done } function get_image_name() { echo "# [get_image_name]" local type="$1" local version="$2" if [ -z "$type" ]; then type=${type:-fedora} fi if [ -z "$version" ]; then version=${version:-$DEFAULT_FEDORA_VERSION} fi case "$type" in fedora) echo "$REGISTRY_URL/f$version/fedora-toolbox:$version" ;; esac } function remove_all_containers() { echo "# [remove_all_containers]" run_toolbox '?' rm --all --force } # BATS specific functions ######### # die # Abort with helpful message ######### function die() { echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" >&2 echo "#| FAIL: $*" >&2 echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2 false } ######## # is # Compare actual vs expected string; fail w/diagnostic if mismatch ######## # # Compares given string against expectations, using 'expr' to allow patterns. # # Examples:failed to inspect # # is "$actual" "$expected" "descriptive test name" # is "apple" "orange" "name of a test that will fail in most universes" # is "apple" "[a-z]\+" "this time it should pass" # function is() { local actual="$1" local expect="$2" local testname="${3:-FIXME}" if [ -z "$expect" ]; then if [ -z "$actual" ]; then return fi expect='[no output]' elif expr "$actual" : "$expect" >/dev/null; then return fi # This is a multi-line message, which may in turn contain multi-line # output, so let's format it ourself, readably local -a actual_split readarray -t actual_split <<<"$actual" printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2 printf "#| FAIL: $testname\n" >&2 printf "#| expected: '%s'\n" "$expect" >&2 printf "#| actual: '%s'\n" "${actual_split[0]}" >&2 local line for line in "${actual_split[@]:1}"; do printf "#| > '%s'\n" "$line" >&2 done printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2 false } ################# # parse_table # Split a table on '|' delimiters; return space-separated ################# # # See sample .bats scripts for examples. The idea is to list a set of # tests in a table, then use simple logic to iterate over each test. # Columns are separated using '|' (pipe character) because sometimes # we need spaces in our fields. # function parse_table() { while read line; do test -z "$line" && continue declare -a row=() while read col; do dprint "col=<<$col>>" row+=("$col") done < <(echo "$line" | tr '|' '\012' | sed -e 's/^ *//' -e 's/\\/\\\\/g') printf "%q " "${row[@]}" printf "\n" done <<<"$1" }