podman/test/system/150-login.bats
Ed Santiago a9dbd2b3de Migrate away from docker.io
CI and system tests currently pull some images from docker.io.
Eliminate that, by:

  - building a custom image containing much of what we need
    for testing; and
  - copying other needed images to quay.io

(Reason: effective 2020-11-01 docker.io will limit the
number of image pulls).

The principal change is to create a new quay.io/libpod/testimage,
using the new test/system/build-testimage script, instead of
relying on quay.io/libpod/alpine_labels. We also switch to
using a hardcoded :YYYYMMDD tag, instead of :latest, in an
attempt to futureproof our CI. This image includes 'httpd'
from busybox-extras, which we use in our networking test
(previously we had to pull and run busybox from docker.io).

The testimage can and should be extended as needed for future
tests, e.g. adding test file content or other useful tools.

For the '--pull' tests which require actually pulling from
the registry, I've created an image with the same name but
tagged :00000000 so it will never be pulled by default.
Since this image is only used minimally, it's just busybox.

Unfortunately there remain two cases we cannot solve in
this tiny alpine-based image:

  1) docker registry
  2) systemd

For those, I've (manually) run:

    podman pull [ docker.io/library/registry:2.7 | registry.fedoraproject.org/fedora:31 ]
    podman tag !$ quay.io/...
    podman push !$

...and amended the calling tests accordingly.

I've tried to make the the smallest reasonable diff, not the
smallest possible one. I hope it's a reasonable tradeoff.

Signed-off-by: Ed Santiago <santiago@redhat.com>
2020-09-08 06:06:06 -06:00

330 lines
11 KiB
Bash

#!/usr/bin/env bats -*- bats -*-
#
# tests for podman login
#
load helpers
###############################################################################
# BEGIN one-time envariable setup
# Create a scratch directory; our podman registry will run from here. We
# also use it for other temporary files like authfiles.
if [ -z "${PODMAN_LOGIN_WORKDIR}" ]; then
export PODMAN_LOGIN_WORKDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-${TMPDIR:-/tmp}} podman_bats_login.XXXXXX)
fi
# Randomly-generated username and password
if [ -z "${PODMAN_LOGIN_USER}" ]; then
export PODMAN_LOGIN_USER="user$(random_string 4)"
export PODMAN_LOGIN_PASS=$(random_string 15)
fi
# Randomly-assigned port in the 5xxx range
if [ -z "${PODMAN_LOGIN_REGISTRY_PORT}" ]; then
for port in $(shuf -i 5000-5999);do
if ! { exec 3<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then
export PODMAN_LOGIN_REGISTRY_PORT=$port
break
fi
done
fi
# Override any user-set path to an auth file
unset REGISTRY_AUTH_FILE
# END one-time envariable setup
###############################################################################
# BEGIN filtering - none of these tests will work with podman-remote
function setup() {
skip_if_remote "none of these tests work with podman-remote"
basic_setup
}
# END filtering - none of these tests will work with podman-remote
###############################################################################
# BEGIN first "test" - start a registry for use by other tests
#
# This isn't really a test: it's a helper that starts a local registry.
# Note that we're careful to use a root/runroot separate from our tests,
# so setup/teardown don't clobber our registry image.
#
@test "podman login [start registry]" {
AUTHDIR=${PODMAN_LOGIN_WORKDIR}/auth
mkdir -p $AUTHDIR
# Registry image; copy of docker.io, but on our own registry
local REGISTRY_IMAGE="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/registry:2.7"
# Pull registry image, but into a separate container storage
mkdir -p ${PODMAN_LOGIN_WORKDIR}/root
mkdir -p ${PODMAN_LOGIN_WORKDIR}/runroot
PODMAN_LOGIN_ARGS="--root ${PODMAN_LOGIN_WORKDIR}/root --runroot ${PODMAN_LOGIN_WORKDIR}/runroot"
# Give it three tries, to compensate for flakes
run_podman ${PODMAN_LOGIN_ARGS} pull $REGISTRY_IMAGE ||
run_podman ${PODMAN_LOGIN_ARGS} pull $REGISTRY_IMAGE ||
run_podman ${PODMAN_LOGIN_ARGS} pull $REGISTRY_IMAGE
# Registry image needs a cert. Self-signed is good enough.
CERT=$AUTHDIR/domain.crt
if [ ! -e $CERT ]; then
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout $AUTHDIR/domain.key -x509 -days 2 \
-out $AUTHDIR/domain.crt \
-subj "/C=US/ST=Foo/L=Bar/O=Red Hat, Inc./CN=localhost"
fi
# Store credentials where container will see them
if [ ! -e $AUTHDIR/htpasswd ]; then
htpasswd -Bbn ${PODMAN_LOGIN_USER} ${PODMAN_LOGIN_PASS} \
> $AUTHDIR/htpasswd
# In case $PODMAN_TEST_KEEP_LOGIN_REGISTRY is set, for testing later
echo "${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}" \
> $AUTHDIR/htpasswd-plaintext
fi
# Run the registry container.
run_podman '?' ${PODMAN_LOGIN_ARGS} rm -f registry
run_podman ${PODMAN_LOGIN_ARGS} run -d \
-p ${PODMAN_LOGIN_REGISTRY_PORT}:5000 \
--name registry \
-v $AUTHDIR:/auth:Z \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/auth/domain.key \
$REGISTRY_IMAGE
}
# END first "test" - start a registry for use by other tests
###############################################################################
# BEGIN actual tests
# BEGIN primary podman login/push/pull tests
@test "podman login - basic test" {
run_podman login --tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password-stdin \
localhost:${PODMAN_LOGIN_REGISTRY_PORT} <<<"${PODMAN_LOGIN_PASS}"
is "$output" "Login Succeeded!" "output from podman login"
# Now log out
run_podman logout localhost:${PODMAN_LOGIN_REGISTRY_PORT}
is "$output" "Removed login credentials for localhost:${PODMAN_LOGIN_REGISTRY_PORT}" \
"output from podman logout"
}
@test "podman login - with wrong credentials" {
registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
run_podman 125 login --tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password-stdin \
$registry <<< "x${PODMAN_LOGIN_PASS}"
is "$output" \
"Error: error logging into \"$registry\": invalid username/password" \
'output from podman login'
}
@test "podman login - check generated authfile" {
authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
rm -f $authfile
registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
run_podman login --authfile=$authfile \
--tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password ${PODMAN_LOGIN_PASS} \
$registry
# Confirm that authfile now exists
test -e $authfile || \
die "podman login did not create authfile $authfile"
# Special bracket form needed because of colon in host:port
run jq -r ".[\"auths\"][\"$registry\"][\"auth\"]" <$authfile
is "$status" "0" "jq from $authfile"
expect_userpass="${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS}"
actual_userpass=$(base64 -d <<<"$output")
is "$actual_userpass" "$expect_userpass" "credentials stored in $authfile"
# Now log out and make sure credentials are removed
run_podman logout --authfile=$authfile $registry
run jq -r '.auths' <$authfile
is "$status" "0" "jq from $authfile"
is "$output" "{}" "credentials removed from $authfile"
}
# Some push tests
@test "podman push fail" {
# Create an invalid authfile
authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
rm -f $authfile
wrong_auth=$(base64 <<<"baduser:wrongpassword")
cat >$authfile <<EOF
{
"auths": {
"localhost:${PODMAN_LOGIN_REGISTRY_PORT}": {
"auth": "$wrong_auth"
}
}
}
EOF
run_podman 125 push --authfile=$authfile \
--tls-verify=false $IMAGE \
localhost:${PODMAN_LOGIN_REGISTRY_PORT}/badpush:1
is "$output" ".*: unauthorized: authentication required" \
"auth error on push"
}
@test "podman push ok" {
# Preserve image ID for later comparison against push/pulled image
run_podman inspect --format '{{.Id}}' $IMAGE
iid=$output
destname=ok-$(random_string 10 | tr A-Z a-z)-ok
# Use command-line credentials
run_podman push --tls-verify=false \
--creds ${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS} \
$IMAGE localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname
# Yay! Pull it back
run_podman pull --tls-verify=false \
--creds ${PODMAN_LOGIN_USER}:${PODMAN_LOGIN_PASS} \
localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$destname
# Compare to original image
run_podman inspect --format '{{.Id}}' $destname
is "$output" "$iid" "Image ID of pulled image == original IID"
run_podman rmi $destname
}
# END primary podman login/push/pull tests
###############################################################################
# BEGIN cooperation with skopeo
# Skopeo helper - keep this separate, so we can test with different
# envariable settings
function _test_skopeo_credential_sharing() {
if ! type -p skopeo; then
skip "skopeo not available"
fi
registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
run_podman login "$@" --tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password ${PODMAN_LOGIN_PASS} \
$registry
destname=skopeo-ok-$(random_string 10 | tr A-Z a-z)-ok
echo "# skopeo copy ..."
run skopeo copy "$@" \
--format=v2s2 \
--dest-tls-verify=false \
containers-storage:$IMAGE \
docker://$registry/$destname
echo "$output"
is "$status" "0" "skopeo copy - exit status"
is "$output" ".*Copying blob .*" "output of skopeo copy"
is "$output" ".*Copying config .*" "output of skopeo copy"
is "$output" ".*Writing manifest .*" "output of skopeo copy"
echo "# skopeo inspect ..."
run skopeo inspect "$@" --tls-verify=false docker://$registry/$destname
echo "$output"
is "$status" "0" "skopeo inspect - exit status"
got_name=$(jq -r .Name <<<"$output")
is "$got_name" "$registry/$dest_name" "skopeo inspect -> Name"
# Now try without a valid login; it should fail
run_podman logout "$@" $registry
echo "# skopeo inspect [with no credentials] ..."
run skopeo inspect "$@" --tls-verify=false docker://$registry/$destname
echo "$output"
is "$status" "1" "skopeo inspect - exit status"
is "$output" ".*: unauthorized: authentication required" \
"auth error on skopeo inspect"
}
@test "podman login - shares credentials with skopeo - default auth file" {
if is_rootless; then
if [ -z "${XDG_RUNTIME_DIR}" ]; then
skip "skopeo does not match podman when XDG_RUNTIME_DIR unset; #823"
fi
fi
_test_skopeo_credential_sharing
}
@test "podman login - shares credentials with skopeo - via envariable" {
authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
rm -f $authfile
REGISTRY_AUTH_FILE=$authfile _test_skopeo_credential_sharing
rm -f $authfile
}
@test "podman login - shares credentials with skopeo - via --authfile" {
# Also test that command-line --authfile overrides envariable
authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
rm -f $authfile
fake_authfile=${PODMAN_LOGIN_WORKDIR}/auth-$(random_string 10).json
rm -f $fake_authfile
REGISTRY_AUTH_FILE=$authfile _test_skopeo_credential_sharing --authfile=$authfile
if [ -e $fake_authfile ]; then
die "REGISTRY_AUTH_FILE overrode command-line --authfile!"
fi
rm -f $authfile
}
# END cooperation with skopeo
# END actual tests
###############################################################################
# BEGIN teardown (remove the registry container)
@test "podman login [stop registry, clean up]" {
# For manual debugging; user may request keeping the registry running
if [ -n "${PODMAN_TEST_KEEP_LOGIN_REGISTRY}" ]; then
skip "[leaving registry running by request]"
fi
run_podman --root ${PODMAN_LOGIN_WORKDIR}/root \
--runroot ${PODMAN_LOGIN_WORKDIR}/runroot \
rm -f registry
run_podman --root ${PODMAN_LOGIN_WORKDIR}/root \
--runroot ${PODMAN_LOGIN_WORKDIR}/runroot \
rmi -a
# By default, clean up
if [ -z "${PODMAN_TEST_KEEP_LOGIN_WORKDIR}" ]; then
rm -rf ${PODMAN_LOGIN_WORKDIR}
fi
# Make sure socket is closed
if { exec 3<> /dev/tcp/127.0.0.1/${PODMAN_LOGIN_REGISTRY_PORT}; } &>/dev/null; then
die "Socket still seems open"
fi
}
# END teardown (remove the registry container)
###############################################################################
# vim: filetype=sh