From bce97a3b5dd1477c74232b49d246491aa289a995 Mon Sep 17 00:00:00 2001
From: Ed Santiago <santiago@redhat.com>
Date: Wed, 8 Jun 2022 13:41:43 -0600
Subject: [PATCH] apiv2 tests: clean up

Mostly fix a bad design decision I made early on, re: registry.
 old: registry starts once, runs to the end
 new: registry is brought up on demand, then stopped
Reason: there are times when we need a password-controlled
registry, and times when we need it open.

As long as I'm in here, I've also cleaned up some confusing code
and fixed things so tests can run rootless again.

Signed-off-by: Ed Santiago <santiago@redhat.com>
---
 test/apiv2/10-images.at     |  2 +-
 test/apiv2/12-imagesMore.at | 28 ++++++++-------
 test/apiv2/15-manifest.at   |  7 ++--
 test/apiv2/20-containers.at | 19 +++++++---
 test/apiv2/60-auth.at       |  4 ++-
 test/apiv2/test-apiv2       | 70 +++++++++++++++++++++----------------
 6 files changed, 75 insertions(+), 55 deletions(-)

diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index 13aaea3173..f03b957861 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -53,7 +53,7 @@ t POST "images/create?fromImage=alpine" 200 .error~null .status~".*Download comp
 t POST "images/create?fromImage=alpine&tag=latest" 200
 
 # 10977 - handle platform parameter correctly
-t POST "images/create?fromImage=testimage:20210610&platform=linux/arm64" 200
+t POST "images/create?fromImage=quay.io/libpod/testimage:20210610&platform=linux/arm64" 200
 t GET  "images/testimage:20210610/json" 200 \
   .Architecture=arm64
 
diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at
index 98f396a175..57d5e114d2 100644
--- a/test/apiv2/12-imagesMore.at
+++ b/test/apiv2/12-imagesMore.at
@@ -27,20 +27,22 @@ t POST "libpod/images/$IMAGE/tag?repo=localhost:$REGISTRY_PORT/myrepo&tag=mytag"
 t GET libpod/images/$IMAGE/json 200 \
   .RepoTags[1]=localhost:$REGISTRY_PORT/myrepo:mytag
 
-# Push to local registry and check output
-while read -r LINE
-do
-  if echo "${LINE}" | jq --exit-status 'select( .status != null) | select ( .status | contains("digest: sha256:"))' &>/dev/null; then
-    GOT_DIGEST="1"
-  fi
-done < <(curl -sL "http://$HOST:$PORT/images/localhost:$REGISTRY_PORT/myrepo/push?tlsVerify=false&tag=mytag" -XPOST -H "X-Registry-Config: $REGISTRY_CONFIG_HEADER")
-if [ -z "${GOT_DIGEST}" ] ; then
-  echo -e "${red}not ok: did not found digest in output${nc}"  1>&2;
-fi
-
-# Push to local registry
+# Push to local registry...
 t POST "images/localhost:$REGISTRY_PORT/myrepo/push?tlsVerify=false&tag=mytag" 200
 
+# ...and check output. We can't use our built-in checks because this output
+# is a sequence of JSON objects, i.e., individual ones, not in a JSON array.
+# The lines themselves are valid JSON, but taken together they are not.
+readarray lines <<<"$output"
+s0=$(jq -r .status <<<"${lines[0]}")
+is "$s0" "The push refers to repository [localhost:$REGISTRY_PORT/myrepo:mytag]" \
+   "Push to local registry: first status line"
+
+# FIXME: is there a way to test the actual digest?
+s1=$(jq -r .status <<<"${lines[1]}")
+like "$s1" "mytag: digest: sha256:[0-9a-f]\{64\} size: [0-9]\+" \
+     "Push to local registry: second status line"
+
 # Untag the image
 t POST "libpod/images/$iid/untag?repo=localhost:$REGISTRY_PORT/myrepo&tag=mytag" 201
 
@@ -53,3 +55,5 @@ t GET libpod/images/$IMAGE/json 200 \
 # Remove image
 t DELETE libpod/images/$IMAGE 200 \
   .ExitCode=0
+
+stop_registry
diff --git a/test/apiv2/15-manifest.at b/test/apiv2/15-manifest.at
index 65ce41e7d3..970bed5a82 100644
--- a/test/apiv2/15-manifest.at
+++ b/test/apiv2/15-manifest.at
@@ -27,10 +27,6 @@ RUN >file2
 EOF
 )
 
-function cleanUpManifestTest() {
-    podman rmi -a
-}
-
 t POST /v3.4.0/libpod/manifests/$id_abc/add images="[\"containers-storage:$id_abc_image\"]" 200
 t PUT /v4.0.0/libpod/manifests/$id_xyz operation='update' images="[\"containers-storage:$id_xyz_image\"]" 200
 
@@ -41,4 +37,5 @@ t POST "/v4.0.0/libpod/manifests/xyz:latest/registry/localhost:$REGISTRY_PORT%2F
 t DELETE /v4.0.0/libpod/manifests/$id_abc 200
 t DELETE /v4.0.0/libpod/manifests/$id_xyz 200
 
-cleanUpManifestTest
+podman rmi -a
+stop_registry
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index 4d32a1031a..8f34b7e059 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -45,16 +45,16 @@ t GET libpod/containers/json?all=true 200 \
   .[0].IsInfra=false
 
 # Test compat API for Network Settings (.Network is N/A when rootless)
-network_expect=
+network_expect="Networks=null"
 if root; then
-    network_expect='.[0].NetworkSettings.Networks.podman.NetworkID=podman'
+    network_expect="Networks.podman.NetworkID=podman"
 fi
 t GET /containers/json?all=true 200 \
   length=1 \
   .[0].Id~[0-9a-f]\\{64\\} \
   .[0].Image=$IMAGE \
   .[0].Mounts~.*/tmp \
-  $network_expect
+  .[0].NetworkSettings.$network_expect
 
 # compat API imageid with sha256: prefix
 t GET containers/json?limit=1 200 \
@@ -239,11 +239,15 @@ t GET containers/$cid/json 200 \
 t POST containers/create Image=$IMAGE Entrypoint='["top"]' 201 \
   .Id~[0-9a-f]\\{64\\}
 cid_top=$(jq -r '.Id' <<<"$output")
+
+# Unexpected difference between /containers/json and /containers/CID/json:
+# in rootless, the former returns .Networks=none, the latter .Networks={}
+network_expect=${network_expect/=null/='{}'}
 t GET containers/${cid_top}/json 200 \
   .Config.Entrypoint[0]="top" \
   .Config.Cmd='[]' \
   .Path="top" \
-  .NetworkSettings.Networks.podman.NetworkID=podman
+  .NetworkSettings.$network_expect
 t POST  containers/${cid_top}/start 204
 # make sure the container is running
 t GET containers/${cid_top}/json 200 \
@@ -374,8 +378,13 @@ t DELETE containers/$cid?v=true 204
 t POST containers/create Image=$IMAGE HostConfig='{"NetworkMode":"default"}' 201 \
   .Id~[0-9a-f]\\{64\\}
 cid=$(jq -r '.Id' <<<"$output")
+if root; then
+    network_type=bridge
+else
+    network_type=slirp4netns
+fi
 t GET containers/$cid/json 200 \
-  .HostConfig.NetworkMode="bridge"
+  .HostConfig.NetworkMode="$network_type"
 
 t DELETE containers/$cid?v=true 204
 
diff --git a/test/apiv2/60-auth.at b/test/apiv2/60-auth.at
index 1e087d12b2..465b0a96d8 100644
--- a/test/apiv2/60-auth.at
+++ b/test/apiv2/60-auth.at
@@ -3,7 +3,7 @@
 # registry-related tests
 #
 
-start_registry
+start_registry htpasswd
 
 # Test unreachable
 t POST /v1.40/auth username=$REGISTRY_USERNAME password=WrOnGPassWord serveraddress=does.not.exist.io:1234/ \
@@ -26,3 +26,5 @@ t POST /v1.40/auth username=$REGISTRY_USERNAME password=$REGISTRY_PASSWORD serve
   200 \
   .Status="Login Succeeded" \
   .IdentityToken=""
+
+stop_registry
diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2
index bd28ae1453..6151b7672c 100755
--- a/test/apiv2/test-apiv2
+++ b/test/apiv2/test-apiv2
@@ -62,7 +62,7 @@ clean_up_server() {
         podman rm -a
         podman rmi -af
 
-        stop_registry
+        stop_registry --cleanup
         stop_service
     fi
 }
@@ -242,7 +242,7 @@ function t() {
             esac
         done
         if [[ -z "$curl_args" ]]; then
-            curl_args+=(-d $(jsonify ${post_args[@]}))
+            curl_args=(-d $(jsonify ${post_args[@]}))
             testname="$testname [${curl_args[@]}]"
         fi
     fi
@@ -273,10 +273,6 @@ function t() {
         curl_args+=("--head")
     fi
 
-    if [ -n "$REGISTRY_CONFIG_HEADER" ]; then
-        curl_args+=(-H "X-Registry-Config: $REGISTRY_CONFIG_HEADER")
-    fi
-
     local expected_code=$1; shift
 
     # Log every action we do
@@ -384,11 +380,6 @@ function start_service() {
         die "Cannot start service on non-localhost ($HOST)"
     fi
 
-    echo "rootdir: "$WORKDIR
-    # Some tests use shortnames; force registry override to work around
-    # docker.io throttling.
-#    FIXME esm revisit pulling expected images re: shortnames caused tests to fail
-#    env CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf
     $PODMAN_BIN \
         --root $WORKDIR/server_root --syslog=true \
         system service \
@@ -414,18 +405,18 @@ function stop_service() {
 REGISTRY_PORT=
 REGISTRY_USERNAME=
 REGISTRY_PASSWORD=
-REGISTRY_CONFIG_HEADER=
 function start_registry() {
-    # We can be invoked multiple times, e.g. from different subtests, but
-    # let's assume that once started we only kill it at the end of tests.
+    # We can be called multiple times, but each time should start a new
+    # registry container with (possibly) different configuration. That
+    # means that all callers must be responsible for invoking stop_registry.
     if [[ -n "$REGISTRY_PORT" ]]; then
-        return
+        die "start_registry invoked twice in succession, without stop_registry"
     fi
 
+    # First arg is auth type (default: "none", but can also be "htpasswd")
+    local auth="${1:-none}"
+
     REGISTRY_PORT=$(random_port)
-    REGISTRY_USERNAME=u$(random_string 7)
-    REGISTRY_PASSWORD=p$(random_string 7)
-    REGISTRY_CONFIG_HEADER=$(echo "{\"localhost:${REGISTRY_PORT}\":{\"username\":\"${REGISTRY_USERNAME}\",\"password\":\"${REGISTRY_PASSWORD}\"}}" | base64 --wrap=0)
 
     local REGDIR=$WORKDIR/registry
     local AUTHDIR=$REGDIR/auth
@@ -439,22 +430,33 @@ function start_registry() {
         podman ${PODMAN_REGISTRY_ARGS} pull $REGISTRY_IMAGE ||
         podman ${PODMAN_REGISTRY_ARGS} pull $REGISTRY_IMAGE
 
-    # Create a local cert and credentials
-    # FIXME: is there a hidden "--quiet" flag? This is too noisy.
-    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=registry host certificate" \
-            -addext subjectAltName=DNS:localhost
-    htpasswd -Bbn ${REGISTRY_USERNAME} ${REGISTRY_PASSWORD} \
-             > $AUTHDIR/htpasswd
+    # Create a local cert (no need to do this more than once)
+    if [[ ! -e $AUTHDIR/domain.key ]]; then
+        # FIXME: is there a hidden "--quiet" flag? This is too noisy.
+        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=registry host certificate" \
+                -addext subjectAltName=DNS:localhost
+    fi
+
+    # If invoked with auth=htpasswd, create credentials
+    REGISTRY_USERNAME=
+    REGISTRY_PASSWORD=
+    if [[ "$auth" = "htpasswd" ]]; then
+        REGISTRY_USERNAME=u$(random_string 7)
+        REGISTRY_PASSWORD=p$(random_string 7)
+
+        htpasswd -Bbn ${REGISTRY_USERNAME} ${REGISTRY_PASSWORD} \
+                 > $AUTHDIR/htpasswd
+    fi
 
     # Run the registry, and wait for it to come up
     podman ${PODMAN_REGISTRY_ARGS} run -d \
            -p ${REGISTRY_PORT}:5000 \
            --name registry \
            -v $AUTHDIR:/auth:Z \
-           -e "REGISTRY_AUTH=htpasswd" \
+           -e "REGISTRY_AUTH=$auth" \
            -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
            -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
            -e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/domain.crt \
@@ -468,13 +470,19 @@ function stop_registry() {
     local REGDIR=${WORKDIR}/registry
     if [[ -d $REGDIR ]]; then
         local OPTS="--root ${REGDIR}/root --runroot ${REGDIR}/runroot"
-        podman $OPTS stop -f -t 0 -a
+        podman $OPTS stop -i -t 0 registry
 
         # rm/rmi are important when running rootless: without them we
         # get EPERMS in tmpdir cleanup because files are owned by subuids.
-        podman $OPTS rm -f -a
-        podman $OPTS rmi -f -a
+        podman $OPTS rm -f -i registry
+        if [[ "$1" = "--cleanup" ]]; then
+            podman $OPTS rmi -f -a
+        fi
     fi
+
+    REGISTRY_PORT=
+    REGISTRY_USERNAME=
+    REGISTRY_PASSWORD=
 }
 
 #################