mirror of
				https://github.com/containers/skopeo.git
				synced 2025-11-04 03:20:21 +00:00 
			
		
		
		
	To make sure that the basic functionality is exercised in Skopeo and c/image CI. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
		
			
				
	
	
		
			388 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
#!/bin/bash
 | 
						|
 | 
						|
# Directory containing system test sources
 | 
						|
TEST_SOURCE_DIR=${TEST_SOURCE_DIR:-$(dirname ${BASH_SOURCE})}
 | 
						|
 | 
						|
# Skopeo executable
 | 
						|
SKOPEO_BINARY=${SKOPEO_BINARY:-${TEST_SOURCE_DIR}/../bin/skopeo}
 | 
						|
 | 
						|
# Default timeout for a skopeo command.
 | 
						|
SKOPEO_TIMEOUT=${SKOPEO_TIMEOUT:-300}
 | 
						|
 | 
						|
# Default image to run as a local registry
 | 
						|
REGISTRY_FQIN=${SKOPEO_TEST_REGISTRY_FQIN:-quay.io/libpod/registry:2}
 | 
						|
 | 
						|
###############################################################################
 | 
						|
# BEGIN setup/teardown
 | 
						|
 | 
						|
# Provide common setup and teardown functions, but do not name them such!
 | 
						|
# That way individual tests can override with their own setup/teardown,
 | 
						|
# while retaining the ability to include these if they so desire.
 | 
						|
 | 
						|
function standard_setup() {
 | 
						|
    # Argh. Although BATS provides $BATS_TMPDIR, it's just /tmp!
 | 
						|
    # That's bloody worthless. Let's make our own, in which subtests
 | 
						|
    # can write whatever they like and trust that it'll be deleted
 | 
						|
    # on cleanup.
 | 
						|
    TESTDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-/tmp} skopeo_bats.XXXXXX)
 | 
						|
}
 | 
						|
 | 
						|
function standard_teardown() {
 | 
						|
    if [[ -n $TESTDIR ]]; then
 | 
						|
        rm -rf $TESTDIR
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
# Individual .bats files may override or extend these
 | 
						|
function setup() {
 | 
						|
    standard_setup
 | 
						|
}
 | 
						|
 | 
						|
function teardown() {
 | 
						|
    standard_teardown
 | 
						|
}
 | 
						|
 | 
						|
# END   setup/teardown
 | 
						|
###############################################################################
 | 
						|
# BEGIN standard helpers for running skopeo and testing results
 | 
						|
 | 
						|
#################
 | 
						|
#  run_skopeo  #  Invoke skopeo, with timeout, using BATS 'run'
 | 
						|
#################
 | 
						|
#
 | 
						|
# This is the preferred mechanism for invoking skopeo:
 | 
						|
#
 | 
						|
#  * we use 'timeout' to abort (with a diagnostic) if something
 | 
						|
#    takes too long; this is preferable to a CI hang.
 | 
						|
#  * we log the command run and its output. This doesn't normally
 | 
						|
#    appear in BATS output, but it will if there's an error.
 | 
						|
#  * we check exit status. Since the normal desired code is 0,
 | 
						|
#    that's the default; but the first argument can override:
 | 
						|
#
 | 
						|
#     run_skopeo 125  nonexistent-subcommand
 | 
						|
#     run_skopeo '?'  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_skopeo() {
 | 
						|
    # 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
 | 
						|
 | 
						|
    # Remember command args, for possible use in later diagnostic messages
 | 
						|
    MOST_RECENT_SKOPEO_COMMAND="skopeo $*"
 | 
						|
 | 
						|
    # stdout is only emitted upon error; this echo is to help a debugger
 | 
						|
    echo "\$ $SKOPEO_BINARY $*"
 | 
						|
    run timeout --foreground --kill=10 $SKOPEO_TIMEOUT ${SKOPEO_BINARY} "$@"
 | 
						|
    # 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 [ "$status" -eq 124 -o "$status" -eq 137 ]; then
 | 
						|
        # FIXME: 'timeout -v' requires coreutils-8.29; travis seems to have
 | 
						|
        #        an older version. If/when travis updates, please add -v
 | 
						|
        #        to the 'timeout' command above, and un-comment this out:
 | 
						|
        # if expr "$output" : ".*timeout: sending" >/dev/null; then
 | 
						|
        echo "*** TIMED OUT ***"
 | 
						|
        false
 | 
						|
    fi
 | 
						|
 | 
						|
    if [ -n "$expected_rc" ]; then
 | 
						|
        if [ "$status" -ne "$expected_rc" ]; then
 | 
						|
            die "exit code is $status; expected $expected_rc"
 | 
						|
        fi
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
#################
 | 
						|
#  log_and_run  #  log a command for later debugging, then run it
 | 
						|
#################
 | 
						|
#
 | 
						|
# When diagnosing a test failure,  it can be really nice to see the
 | 
						|
# more important commands that have been run in test setup: openssl,
 | 
						|
# podman registry, other complex commands that can give one a boost
 | 
						|
# when trying to reproduce problems. This simple wrapper takes a
 | 
						|
# command as its arg, echoes it to stdout (with a '$' prefix),
 | 
						|
# then runs the command. BATS does not show stdout unless there's
 | 
						|
# an error. Use this judiciously.
 | 
						|
#
 | 
						|
function log_and_run() {
 | 
						|
    echo "\$ $*"
 | 
						|
    "$@"
 | 
						|
}
 | 
						|
 | 
						|
#########
 | 
						|
#  die  #  Abort with helpful message
 | 
						|
#########
 | 
						|
function die() {
 | 
						|
    echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"  >&2
 | 
						|
    echo "#| FAIL: $*"                                           >&2
 | 
						|
    echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2
 | 
						|
    false
 | 
						|
}
 | 
						|
 | 
						|
###################
 | 
						|
#  expect_output  #  Compare actual vs expected string; fail if mismatch
 | 
						|
###################
 | 
						|
#
 | 
						|
# Compares $output against the given string argument. Optional second
 | 
						|
# argument is descriptive text to show as the error message (default:
 | 
						|
# the command most recently run by 'run_skopeo'). This text can be
 | 
						|
# useful to isolate a failure when there are multiple identical
 | 
						|
# run_skopeo invocations, and the difference is solely in the
 | 
						|
# config or setup; see, e.g., run.bats:run-cmd().
 | 
						|
#
 | 
						|
# By default we run an exact string comparison; use --substring to
 | 
						|
# look for the given string anywhere in $output.
 | 
						|
#
 | 
						|
# By default we look in "$output", which is set in run_skopeo().
 | 
						|
# To override, use --from="some-other-string" (e.g. "${lines[0]}")
 | 
						|
#
 | 
						|
# Examples:
 | 
						|
#
 | 
						|
#   expect_output "this is exactly what we expect"
 | 
						|
#   expect_output "foo=bar"  "description of this particular test"
 | 
						|
#   expect_output --from="${lines[0]}"  "expected first line"
 | 
						|
#
 | 
						|
function expect_output() {
 | 
						|
    # By default we examine $output, the result of run_skopeo
 | 
						|
    local actual="$output"
 | 
						|
    local check_substring=
 | 
						|
 | 
						|
    # option processing: recognize --from="...", --substring
 | 
						|
    local opt
 | 
						|
    for opt; do
 | 
						|
        local value=$(expr "$opt" : '[^=]*=\(.*\)')
 | 
						|
        case "$opt" in
 | 
						|
            --from=*)       actual="$value";   shift;;
 | 
						|
            --substring)    check_substring=1; shift;;
 | 
						|
            --)             shift; break;;
 | 
						|
            -*)             die "Invalid option '$opt'" ;;
 | 
						|
            *)              break;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
 | 
						|
    local expect="$1"
 | 
						|
    local testname="${2:-${MOST_RECENT_SKOPEO_COMMAND:-[no test name given]}}"
 | 
						|
 | 
						|
    if [ -z "$expect" ]; then
 | 
						|
        if [ -z "$actual" ]; then
 | 
						|
            return
 | 
						|
        fi
 | 
						|
        expect='[no output]'
 | 
						|
    elif [ "$actual" = "$expect" ]; then
 | 
						|
	return
 | 
						|
    elif [ -n "$check_substring" ]; then
 | 
						|
        if [[ "$actual" =~ $expect ]]; then
 | 
						|
            return
 | 
						|
        fi
 | 
						|
    fi
 | 
						|
 | 
						|
    # This is a multi-line message, which may in turn contain multi-line
 | 
						|
    # output, so let's format it ourselves, 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
 | 
						|
}
 | 
						|
 | 
						|
#######################
 | 
						|
#  expect_line_count  #  Check the expected number of output lines
 | 
						|
#######################
 | 
						|
#
 | 
						|
# ...from the most recent run_skopeo command
 | 
						|
#
 | 
						|
function expect_line_count() {
 | 
						|
    local expect="$1"
 | 
						|
    local testname="${2:-${MOST_RECENT_SKOPEO_COMMAND:-[no test name given]}}"
 | 
						|
 | 
						|
    local actual="${#lines[@]}"
 | 
						|
    if [ "$actual" -eq "$expect" ]; then
 | 
						|
        return
 | 
						|
    fi
 | 
						|
 | 
						|
    printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n"          >&2
 | 
						|
    printf "#| FAIL: $testname\n"                                       >&2
 | 
						|
    printf "#| Expected %d lines of output, got %d\n" $expect $actual   >&2
 | 
						|
    printf "#| Output was:\n"                                           >&2
 | 
						|
    local line
 | 
						|
    for line in "${lines[@]}"; do
 | 
						|
        printf "#| >%s\n" "$line"                                       >&2
 | 
						|
    done
 | 
						|
    printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"         >&2
 | 
						|
    false
 | 
						|
}
 | 
						|
 | 
						|
# END   standard helpers for running skopeo and testing results
 | 
						|
###############################################################################
 | 
						|
# BEGIN helpers for starting/stopping registries
 | 
						|
 | 
						|
####################
 | 
						|
#  start_registry  #  Run a local registry container
 | 
						|
####################
 | 
						|
#
 | 
						|
# Usage:  start_registry [OPTIONS] NAME
 | 
						|
#
 | 
						|
#   OPTIONS
 | 
						|
#       --port=NNNN         Port to listen on (default: 5000)
 | 
						|
#       --testuser=XXX      Require authentication; this is the username
 | 
						|
#       --testpassword=XXX  ...and the password (these two go together)
 | 
						|
#       --with-cert         Create a cert for running with TLS (not working)
 | 
						|
#       --enable-delete     Set allowing registry deletions (default: false)
 | 
						|
#
 | 
						|
#   NAME is the container name to assign.
 | 
						|
#
 | 
						|
start_registry() {
 | 
						|
    local port=5000
 | 
						|
    local testuser=
 | 
						|
    local testpassword=
 | 
						|
    local create_cert=
 | 
						|
    local enable_delete=false
 | 
						|
 | 
						|
    # option processing: recognize options for running the registry
 | 
						|
    # in different modes.
 | 
						|
    local opt
 | 
						|
    for opt; do
 | 
						|
        local value=$(expr "$opt" : '[^=]*=\(.*\)')
 | 
						|
        case "$opt" in
 | 
						|
            --port=*)           port="$value";          shift;;
 | 
						|
            --testuser=*)       testuser="$value";      shift;;
 | 
						|
            --testpassword=*)   testpassword="$value";  shift;;
 | 
						|
            --with-cert)        create_cert=1;          shift;;
 | 
						|
            --enable-delete=*)  enable_delete="$value"; shift;;
 | 
						|
            -*)                 die "Invalid option '$opt'" ;;
 | 
						|
            *)                  break;;
 | 
						|
        esac
 | 
						|
    done
 | 
						|
 | 
						|
    local name=${1?start_registry() invoked without a NAME}
 | 
						|
 | 
						|
    # Temp directory must be defined and must exist
 | 
						|
    [[ -n $TESTDIR && -d $TESTDIR ]]
 | 
						|
 | 
						|
    AUTHDIR=$TESTDIR/auth
 | 
						|
    mkdir -p $AUTHDIR
 | 
						|
 | 
						|
    local -a reg_args=(-v $AUTHDIR:/auth:Z -p $port:5000)
 | 
						|
    if [[ "$enable_delete" == "true" ]]; then
 | 
						|
        reg_args+=( -e REGISTRY_STORAGE_DELETE_ENABLED=true)
 | 
						|
    fi
 | 
						|
 | 
						|
    # TODO: This is TEMPORARY (as of 2020-03-30); remove once crun is fixed.
 | 
						|
    # Skopeo PR #836 claims there's a "regression" in crun with cgroupsv1,
 | 
						|
    # but offers no details about what it is (crun issue nor PR) nor when/if
 | 
						|
    # it's fixed. It's simply a workaround, forcing podman to use runc,
 | 
						|
    # which might work great for skopeo CI but breaks Fedora gating tests.
 | 
						|
    # Instead of always forcing runc, do so only when under cgroups v1:
 | 
						|
    local runtime=
 | 
						|
    cgroup_type=$(stat -f -c %T /sys/fs/cgroup)
 | 
						|
    if [[ $cgroup_type == "tmpfs" ]]; then
 | 
						|
        runtime="--runtime runc"
 | 
						|
    fi
 | 
						|
 | 
						|
    # cgroup option necessary under podman-in-podman (CI tests),
 | 
						|
    # and doesn't seem to do any harm otherwise.
 | 
						|
    PODMAN="podman $runtime --cgroup-manager=cgroupfs"
 | 
						|
 | 
						|
    # Called with --testuser? Create an htpasswd file
 | 
						|
    if [[ -n $testuser ]]; then
 | 
						|
        if [[ -z $testpassword ]]; then
 | 
						|
            die "start_registry() invoked with testuser but no testpassword"
 | 
						|
        fi
 | 
						|
 | 
						|
        if ! egrep -q "^$testuser:" $AUTHDIR/htpasswd; then
 | 
						|
            htpasswd -Bbn $testuser $testpassword >> $AUTHDIR/htpasswd
 | 
						|
        fi
 | 
						|
 | 
						|
        reg_args+=(
 | 
						|
            -e REGISTRY_AUTH=htpasswd
 | 
						|
            -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
 | 
						|
            -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"
 | 
						|
        )
 | 
						|
    fi
 | 
						|
 | 
						|
    # Called with --with-cert? Create certificates.
 | 
						|
    if [[ -n $create_cert ]]; then
 | 
						|
        CERT=$AUTHDIR/domain.crt
 | 
						|
        if [ ! -e $CERT ]; then
 | 
						|
            log_and_run openssl req -newkey rsa:4096 -nodes -sha256 \
 | 
						|
                    -keyout $AUTHDIR/domain.key -x509 -days 2 \
 | 
						|
                    -out $CERT \
 | 
						|
                    -subj "/C=US/ST=Foo/L=Bar/O=Red Hat, Inc./CN=registry host certificate" \
 | 
						|
                    -addext subjectAltName=DNS:localhost
 | 
						|
        fi
 | 
						|
 | 
						|
        reg_args+=(
 | 
						|
            -e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/domain.crt
 | 
						|
            -e REGISTRY_HTTP_TLS_KEY=/auth/domain.key
 | 
						|
        )
 | 
						|
 | 
						|
        # Copy .crt file to a directory *without* the .key one, so we can
 | 
						|
        # test the client. (If client sees a matching .key file, it fails)
 | 
						|
        # Thanks to Miloslav Trmac for this hint.
 | 
						|
        mkdir -p $TESTDIR/client-auth
 | 
						|
        log_and_run cp $CERT $TESTDIR/client-auth/
 | 
						|
    fi
 | 
						|
 | 
						|
    log_and_run $PODMAN run -d --name $name "${reg_args[@]}" $REGISTRY_FQIN
 | 
						|
 | 
						|
    # Wait for registry to actually come up
 | 
						|
    timeout=10
 | 
						|
    while [[ $timeout -ge 1 ]]; do
 | 
						|
        if echo -n >/dev/tcp/127.0.0.1/$port; then
 | 
						|
            return
 | 
						|
        fi
 | 
						|
 | 
						|
        timeout=$(( timeout - 1 ))
 | 
						|
        sleep 1
 | 
						|
    done
 | 
						|
    log_and_run $PODMAN logs $name
 | 
						|
    die "Timed out waiting for registry container to respond on :$port"
 | 
						|
}
 | 
						|
 | 
						|
# END   helpers for starting/stopping registries
 | 
						|
###############################################################################
 | 
						|
# BEGIN miscellaneous tools
 | 
						|
 | 
						|
###################
 | 
						|
#  random_string  #  Returns a pseudorandom human-readable string
 | 
						|
###################
 | 
						|
#
 | 
						|
# Numeric argument, if present, is desired length of string
 | 
						|
#
 | 
						|
function random_string() {
 | 
						|
    local length=${1:-10}
 | 
						|
 | 
						|
    head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
 | 
						|
}
 | 
						|
 | 
						|
# END   miscellaneous tools
 | 
						|
###############################################################################
 |