diff options
Diffstat (limited to 'test')
121 files changed, 3301 insertions, 555 deletions
diff --git a/test/README.md b/test/README.md index 769bdbfd7..b44deadaf 100644 --- a/test/README.md +++ b/test/README.md @@ -1,4 +1,4 @@ - + # Test utils Test utils provide common functions and structs for testing. It includes two structs: * `PodmanTest`: Handle the *podman* command and other global resources like temporary diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index f03b95786..3ffc6f738 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -203,7 +203,7 @@ t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 # Libpod: allow building from url: https://github.com/alpinelinux/docker-alpine.git and must ignore any provided tar t POST "libpod/build?remote=https%3A%2F%2Fgithub.com%2Falpinelinux%2Fdocker-alpine.git" $CONTAINERFILE_TAR 200 \ - .stream~"STEP 1/5: FROM alpine:3.14" + .stream~"STEP 1/5: FROM alpine:" # Build api response header must contain Content-type: application/json t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 @@ -227,16 +227,36 @@ t GET libpod/images/quay.io/libpod/busybox:latest/exists 204 CONTAINERFILE_WITH_ERR_TAR="${TMPD}/containerfile.tar" cat > $TMPD/containerfile << EOF -FROM quay.io/fedora/fedora +FROM $IMAGE RUN echo 'some error' >&2 EOF tar --format=posix -C $TMPD -cvf ${CONTAINERFILE_WITH_ERR_TAR} containerfile &> /dev/null -t POST "build?q=1&dockerfile=containerfile" $CONTAINERFILE_WITH_ERR_TAR 200 -response_output=$(cat "$WORKDIR/curl.result.out") -if [[ ${response_output} == *"some error"* ]];then - _show_ok 0 "compat quiet build" "~ $response_output" "found output from stderr in API" +t POST "/build?q=1&dockerfile=containerfile" $CONTAINERFILE_WITH_ERR_TAR 200 +if [[ $output == *"some error"* ]];then + _show_ok 0 "compat quiet build" "[should not contain 'some error']" "$output" +else + _show_ok 1 "compat quiet build" fi cleanBuildTest +# compat API vs libpod API event differences: +# on image removal, libpod produces 'remove' events. +# compat produces 'delete' events. +podman image build -t test:test -<<EOF +from $IMAGE +EOF + +START=$(date +%s) + +t DELETE libpod/images/test:test 200 +# HACK HACK HACK There is a race around events being added to the journal +# This sleep seems to avoid the race. +# If it fails and begins to flake, investigate a retry loop. +sleep 1 +t GET "libpod/events?stream=false&since=$START" 200 \ + 'select(.status | contains("remove")).Action=remove' +t GET "events?stream=false&since=$START" 200 \ + 'select(.status | contains("delete")).Action=delete' + # vim: filetype=sh diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index 498d67569..eb58b8377 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -3,9 +3,6 @@ # Tests for more image-related endpoints # -red='\e[31m' -nc='\e[0m' - start_registry podman pull -q $IMAGE @@ -63,7 +60,9 @@ podman pull -q $IMAGE # test podman image SCP # ssh needs to work so we can validate that the failure is past argument parsing -podman system connection add --default test ssh://$USER@localhost/run/user/$UID/podman/podman.sock +conn=apiv2test-temp-connection +podman system connection add --default $conn \ + ssh://$USER@localhost/run/user/$UID/podman/podman.sock # should fail but need to check the output... # status 125 here means that the save/load fails due to # cirrus weirdness with exec.Command. All of the args have been parsed successfully. @@ -72,4 +71,7 @@ t POST "libpod/images/scp/$IMAGE?destination=QA::" 500 \ t DELETE libpod/images/$IMAGE 200 \ .ExitCode=0 +# Clean up +podman system connection rm $conn + stop_registry diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index a8d9baef3..cc238e27e 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -98,6 +98,12 @@ else fi fi +# max_usage is not set for cgroupv2 +if have_cgroupsv2; then + t GET libpod/containers/stats?containers='[$cid]' 200 \ + .memory_stats.max_usage=null +fi + t DELETE libpod/containers/$cid 200 .[0].Id=$cid # Issue #14676: make sure the stats show the memory limit specified for the container @@ -111,6 +117,17 @@ if root; then podman rm -f $CTRNAME fi +# Issue #15765: make sure the memory limit is capped +if root; then + CTRNAME=ctr-with-limit + podman run --name $CTRNAME -d -m 512m -v /tmp:/tmp $IMAGE top + + t GET libpod/containers/$CTRNAME/stats?stream=false 200 \ + .memory_stats.limit!=18446744073709552000 + + podman rm -f $CTRNAME +fi + # Issue #6799: it should be possible to start a container, even w/o args. t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \ .Id~[0-9a-f]\\{64\\} @@ -309,7 +326,9 @@ t POST containers/create Image=${MultiTagName} 201 \ .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ - .Image=${MultiTagName} + .Config.Image=${MultiTagName} \ + .Image~sha256:[0-9a-f]\\{64\\} + t DELETE containers/$cid 204 t DELETE images/${MultiTagName} 200 # vim: filetype=sh @@ -527,3 +546,39 @@ t GET containers/status-test/json 200 .State.Status="stopping" sleep 3 t GET containers/status-test/json 200 .State.Status="exited" + +# test podman generate spec as input for the api +cname=specgen$(random_string 10) +podman create --name=$cname $IMAGE + +TMPD=$(mktemp -d podman-apiv2-test.build.XXXXXXXX) + +podman generate spec -f ${TMPD}/myspec.json -c $cname + +# Create a container based on that spec +t POST libpod/containers/create ${TMPD}/myspec.json 201 \ + .Id~[0-9a-f]\\{64\\} + +# Verify +t GET libpod/containers/$cname/json 200 \ + .ImageName=$IMAGE \ + .Name=$cname + +if root; then + podman run -dt --name=updateCtr alpine + echo '{"Memory":{"Limit":500000}, "CPU":{"Shares":123}}' >${TMPD}/update.json + t POST libpod/containers/updateCtr/update ${TMPD}/update.json 201 + + # Verify + echo '{ "AttachStdout":true,"Cmd":["cat","/sys/fs/cgroup/cpu.weight"]}' >${TMPD}/exec.json + t POST containers/updateCtr/exec ${TMPD}/exec.json 201 .Id~[0-9a-f]\\{64\\} + eid=$(jq -r '.Id' <<<"$output") + # 002 is the byte length + t POST exec/$eid/start 200 $'\001\0025' + + podman rm -f updateCtr +fi + +rm -rf $TMPD + +podman container rm -fa diff --git a/test/apiv2/23-containersArchive.at b/test/apiv2/23-containersArchive.at index c55164780..c1b936e3a 100644 --- a/test/apiv2/23-containersArchive.at +++ b/test/apiv2/23-containersArchive.at @@ -3,22 +3,31 @@ # test more container-related endpoints # -red='\e[31m' -nc='\e[0m' - podman pull $IMAGE &>/dev/null # Ensure clean slate podman rm -a -f &>/dev/null -CTR="ArchiveTestingCtr" +CTR="ArchiveTestingCtr$(random_string 5)" TMPD=$(mktemp -d podman-apiv2-test.archive.XXXXXXXX) HELLO_TAR="${TMPD}/hello.tar" -echo "Hello" > $TMPD/hello.txt +HELLO_S="Hello_$(random_string 8)" +echo "$HELLO_S" > $TMPD/hello.txt tar --owner=1042 --group=1043 --format=posix -C $TMPD -cvf ${HELLO_TAR} hello.txt &> /dev/null +# Start a container, and wait for it. (I know we don't actually do anything +# if we time out. If we do, subsequent tests will fail. I just want to avoid +# a race between container-start and tests-start) podman run -d --name "${CTR}" "${IMAGE}" top +timeout=10 +while [[ $timeout -gt 0 ]]; do + if podman container exists "${CTR}"; then + break + fi + timeout=$((timeout - 1)) + sleep 1 +done function cleanUpArchiveTest() { podman container stop "${CTR}" &> /dev/null @@ -30,63 +39,47 @@ t HEAD "containers/nonExistentCtr/archive?path=%2F" 404 t HEAD "containers/${CTR}/archive?path=%2Fnon%2Fexistent%2Fpath" 404 t HEAD "containers/${CTR}/archive?path=%2Fetc%2Fpasswd" 200 -curl "http://$HOST:$PORT/containers/${CTR}/archive?path=%2Ftmp%2F" \ - -X PUT \ - -H "Content-Type: application/x-tar" \ - --upload-file "${HELLO_TAR}" &> /dev/null +# Send tarfile to container... +t PUT "/containers/${CTR}/archive?path=%2Ftmp%2F" ${HELLO_TAR} 200 '' -if ! podman exec -it "${CTR}" "grep" "Hello" "/tmp/hello.txt" &> /dev/null ; then - echo -e "${red}NOK: The hello.txt file has not been uploaded.${nc}" 1>&2; - cleanUpArchiveTest - exit 1 -fi +# ...and 'exec cat file' to confirm that it got extracted into place. +cat >$TMPD/exec.json <<EOF +{ "AttachStdout":true,"Cmd":["cat","/tmp/hello.txt"]} +EOF +t POST containers/${CTR}/exec $TMPD/exec.json 201 .Id~[0-9a-f]\\{64\\} +eid=$(jq -r '.Id' <<<"$output") +# The 017 is the byte length +t POST exec/$eid/start 200 $'\001\017'$HELLO_S +# Now fetch hello.txt back as a tarfile t HEAD "containers/${CTR}/archive?path=%2Ftmp%2Fhello.txt" 200 +t GET "containers/${CTR}/archive?path=%2Ftmp%2Fhello.txt" 200 -curl "http://$HOST:$PORT/containers/${CTR}/archive?path=%2Ftmp%2Fhello.txt" \ - --dump-header "${TMPD}/headers.txt" \ - -o "${TMPD}/body.tar" \ - -X GET &> /dev/null - -PATH_STAT="$(grep X-Docker-Container-Path-Stat "${TMPD}/headers.txt" | cut -d " " -f 2 | base64 -d --ignore-garbage)" - -ARCHIVE_TEST_ERROR="" +# Check important-looking header +PATH_STAT="$(grep X-Docker-Container-Path-Stat "$WORKDIR/curl.headers.out" | cut -d " " -f 2 | base64 -d --ignore-garbage)" -if [ "$(echo "${PATH_STAT}" | jq ".name")" != '"hello.txt"' ]; then - echo -e "${red}NOK: Wrong name in X-Docker-Container-Path-Stat header.${nc}" 1>&2; - ARCHIVE_TEST_ERROR="1" -fi +is "$(jq -r .name <<<$PATH_STAT)" "hello.txt" "Docker-Path-Stat .name" +is "$(jq -r .size <<<$PATH_STAT)" "15" "Docker-Path-Stat .size" -if [ "$(echo "${PATH_STAT}" | jq ".size")" != "6" ]; then - echo -e "${red}NOK: Wrong size in X-Docker-Container-Path-Stat header.${nc}" 1>&2; - ARCHIVE_TEST_ERROR="1" -fi +# Check filename and its contents +tar_tf=$(tar tf $WORKDIR/curl.result.out) +is "$tar_tf" "hello.txt" "fetched tarball: file name" -if ! tar -tf "${TMPD}/body.tar" | grep "hello.txt" &> /dev/null; then - echo -e "${red}NOK: Body doesn't contain expected file.${nc}" 1>&2; - ARCHIVE_TEST_ERROR="1" -fi +tar_contents=$(tar xf $WORKDIR/curl.result.out --to-stdout) +is "$tar_contents" "$HELLO_S" "fetched tarball: file contents" -if [ "$(tar -xf "${TMPD}/body.tar" hello.txt --to-stdout)" != "Hello" ]; then - echo -e "${red}NOK: Content of file doesn't match.${nc}" 1>&2; - ARCHIVE_TEST_ERROR="1" -fi +# TODO: uid/gid should be also preserved on way back (GET request) +# right now it ends up as 0/0 instead of 1042/1043 +tar_uidgid=$(tar tvf $WORKDIR/curl.result.out | awk '{print $2}') +is "$tar_uidgid" "0/0" "fetched tarball: file uid/gid" -# test if uid/gid was set correctly in the server -uidngid=$($PODMAN_BIN --root $WORKDIR/server_root exec "${CTR}" stat -c "%u:%g" "/tmp/hello.txt") -if [[ "${uidngid}" != "1042:1043" ]]; then - echo -e "${red}NOK: UID/GID of the file doesn't match.${nc}" 1>&2; - ARCHIVE_TEST_ERROR="1" -fi +# test if uid/gid was set correctly in the server. Again, via exec. +cat >$TMPD/exec.json <<EOF +{ "AttachStdout":true,"Cmd":["stat","-c","%u:%g","/tmp/hello.txt"]} +EOF -# TODO: uid/gid should be also preserved on way back (GET request) -# right now it ends up as root:root instead of 1042:1043 -#if [[ "$(tar -tvf "${TMPD}/body.tar")" != *"1042/1043"* ]]; then -# echo -e "${red}NOK: UID/GID of the file doesn't match.${nc}" 1>&2; -# ARCHIVE_TEST_ERROR="1" -#fi +t POST containers/${CTR}/exec $TMPD/exec.json 201 .Id~[0-9a-f]\\{64\\} +eid=$(jq -r '.Id' <<<"$output") +t POST exec/$eid/start 200 $'\001\012'1042:1043 cleanUpArchiveTest -if [[ "${ARCHIVE_TEST_ERROR}" ]] ; then - exit 1; -fi diff --git a/test/apiv2/26-containersWait.at b/test/apiv2/26-containersWait.at index 55bcd4592..41938d567 100644 --- a/test/apiv2/26-containersWait.at +++ b/test/apiv2/26-containersWait.at @@ -3,9 +3,6 @@ # test more container-related endpoints # -red='\e[31m' -nc='\e[0m' - podman pull "${IMAGE}" &>/dev/null # Ensure clean slate @@ -21,29 +18,29 @@ t POST "containers/${CTR}/wait?condition=non-existent-cond" 400 t POST "containers/${CTR}/wait?condition=not-running" 200 +# Test waiting for EXIT (need to start a background trigger first) +(sleep 2;podman start "${CTR}") & +child_pid=$! + +# This will block until the background job completes t POST "containers/${CTR}/wait?condition=next-exit" 200 \ .StatusCode=0 \ - .Error=null & -child_pid=$! -podman start "${CTR}" + .Error=null wait "${child_pid}" - -# check if headers are sent in advance before body -WAIT_TEST_ERROR="" -curl -I -X POST "http://$HOST:$PORT/containers/${CTR}/wait?condition=next-exit" &> "/dev/null" & -child_pid=$! -sleep 0.5 -if kill -2 "${child_pid}" 2> "/dev/null"; then - echo -e "${red}NOK: Failed to get response headers immediately.${nc}" 1>&2; - WAIT_TEST_ERROR="1" +# Test that headers are sent before body. (We should actually never get a body) +APIV2_TEST_EXPECT_TIMEOUT=2 t POST "containers/${CTR}/wait?condition=next-exit" 999 +like "$(<$WORKDIR/curl.headers.out)" ".*HTTP.* 200 OK.*" \ + "Received headers from /wait" +if [[ -e $WORKDIR/curl.result.out ]]; then + _show_ok 0 "UNEXPECTED: curl on /wait returned results" fi -t POST "containers/${CTR}/wait?condition=removed" 200 & +# Test waiting for REMOVE. Like above, start a background trigger. +(sleep 2;podman container rm "${CTR}") & child_pid=$! -podman container rm "${CTR}" -wait "${child_pid}" -if [[ "${WAIT_TEST_ERROR}" ]] ; then - exit 1; -fi +t POST "containers/${CTR}/wait?condition=removed" 200 \ + .StatusCode=0 \ + .Error=null +wait "${child_pid}" diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index 80724a8d9..0e0f1cb18 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -134,6 +134,20 @@ t GET libpod/pods/json?filters='{"label":["testl' 400 \ t DELETE libpod/pods/foo 200 t DELETE "libpod/pods/foo (pod has already been deleted)" 404 -t_timeout 5 GET "libpod/pods/stats?stream=true&delay=1" 200 +# Expect this to time out +APIV2_TEST_EXPECT_TIMEOUT=5 t GET "libpod/pods/stats?stream=true&delay=1" 999 + +podman pod create --name=specgen + +TMPD=$(mktemp -d podman-apiv2-test.build.XXXXXXXX) + +podman generate spec -f ${TMPD}/myspec.json -c specgen + +t POST libpod/pods/create ${TMPD}/myspec.json 201 \ + .Id~[0-9a-f]\\{64\\} + +rm -rf $TMPD + +podman pod rm -fa # vim: filetype=sh diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at index 364b87c56..096df5516 100644 --- a/test/apiv2/45-system.at +++ b/test/apiv2/45-system.at @@ -25,6 +25,24 @@ t GET system/df 200 '.Volumes[0].Name=foo1' t GET libpod/system/df 200 '.Volumes[0].VolumeName=foo1' +# Verify that no containers reference the volume +t GET system/df 200 '.Volumes[0].UsageData.RefCount=0' + +# Make a container using the volume +podman pull $IMAGE &>/dev/null +t POST containers/create Image=$IMAGE Volumes='{"/test":{}}' HostConfig='{"Binds":["foo1:/test"]}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") + +# Verify that one container references the volume +t GET system/df 200 '.Volumes[0].UsageData.RefCount=1' + +# Remove the container +t DELETE containers/$cid?v=true 204 + +# Verify that no containers reference the volume +t GET system/df 200 '.Volumes[0].UsageData.RefCount=0' + # Create two more volumes to test pruneing t POST libpod/volumes/create \ Name=foo2 \ diff --git a/test/apiv2/70-short-names.at b/test/apiv2/70-short-names.at index bd7f8e7bd..952dd2ad1 100644 --- a/test/apiv2/70-short-names.at +++ b/test/apiv2/70-short-names.at @@ -9,7 +9,7 @@ t POST "images/create?fromImage=quay.io/libpod/alpine:latest" 200 .error~null .s # 14291 - let a short-name resolve to a *local* non Docker-Hub image. t POST containers/create Image=alpine 201 .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") -t GET containers/$cid/json 200 .Image="quay.io/libpod/alpine:latest" +t GET containers/$cid/json 200 .Config.Image="quay.io/libpod/alpine:latest" .Image~sha256:[0-9a-f]\\{64\\} podman rm -f $cid ########## TAG @@ -33,18 +33,8 @@ RUN touch /foo EOF tar --format=posix -C $TMPD -cvf ${CONTAINERFILE_TAR} containerfile &> /dev/null - curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ - -H "content-type: application/x-tar" \ - --dump-header "${TMPD}/headers.txt" \ - -o "${TMPD}/response.txt" \ - "http://$HOST:$PORT/build?dockerfile=containerfile&t=$tag" &> /dev/null - - if ! grep -q '200 OK' "${TMPD}/headers.txt"; then - cat "${TMPD}/headers.txt" - cat "${TMPD}/response.txt" - echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/x-tar)" - exit 1 - fi + t POST "/build?dockerfile=containerfile&t=$tag" $CONTAINERFILE_TAR 200 \ + .stream~".*Successfully tagged .*" rm -rf $TMPD t DELETE "images/$fqn" 200 diff --git a/test/apiv2/README.md b/test/apiv2/README.md index 63d1f5b13..712124d1b 100644 --- a/test/apiv2/README.md +++ b/test/apiv2/README.md @@ -46,6 +46,9 @@ with POST parameters if present, and compares return status and | +----------- POST params +--------------------------------- note the missing slash +Never, ever, ever, seriously _EVER_ `exit` from a test. Just don't. +That skips cleanup, and leaves the system in a broken state. + Notes: * If the endpoint has a leading slash (`/_ping`), `t` leaves it unchanged. @@ -61,14 +64,19 @@ of POST parameters in the form 'key=value', separated by spaces: `t` will convert the param list to JSON form for passing to the server. A numeric status code terminates processing of POST parameters. ** As a special case, when one POST argument is a string ending in `.tar`, -`t` will invoke `curl` with `--data-binary @PATH` and -set `Content-type: application/x-tar`. This is useful for `build` endpoints. +`.yaml`, or `.json`, `t` will invoke `curl` with `--data-binary @PATH` and +set `Content-type` as appropriate. This is useful for `build` endpoints. (To override `Content-type`, simply pass along an extra string argument matching `application/*`): t POST myentrypoint /mytmpdir/myfile.tar application/foo 400 +** Like above, when using PUT, `t` does `--upload-time` instead of +`--data-binary` * The final arguments are one or more expected string results. If an argument starts with a dot, `t` will invoke `jq` on the output to fetch that field, and will compare it to the right-hand side of the argument. If the separator is `=` (equals), `t` will require an exact match; if `~` (tilde), `t` will use `expr` to compare. + +* If your test expects `curl` to time out: + APIV2_TEST_EXPECT_TIMEOUT=5 t POST /foo 999 diff --git a/test/apiv2/python/rest_api/fixtures/api_testcase.py b/test/apiv2/python/rest_api/fixtures/api_testcase.py index f47136555..edb34b31e 100644 --- a/test/apiv2/python/rest_api/fixtures/api_testcase.py +++ b/test/apiv2/python/rest_api/fixtures/api_testcase.py @@ -20,7 +20,7 @@ class APITestCase(unittest.TestCase): APITestCase.podman = Podman() APITestCase.service = APITestCase.podman.open( - "system", "service", "tcp:localhost:8080", "--time=0" + "system", "service", "tcp://localhost:8080", "--time=0" ) # give the service some time to be ready... time.sleep(2) diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index a6cd93a1a..25596a9b7 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -359,8 +359,6 @@ class ContainerTestCase(APITestCase): self.assertEqual(2000, out["HostConfig"]["MemorySwap"]) self.assertEqual(1000, out["HostConfig"]["Memory"]) - - def execute_process(cmd): return subprocess.run( cmd, diff --git a/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py b/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py index 905c29683..2274f25bf 100644 --- a/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py +++ b/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py @@ -63,7 +63,7 @@ class TestApi(unittest.TestCase): podman(), "system", "service", - "tcp:localhost:8080", + "tcp://localhost:8080", "--log-level=debug", "--time=0", ], diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index 0c3c6e672..8132e6432 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -23,8 +23,6 @@ REGISTRY_IMAGE="${PODMAN_TEST_IMAGE_REGISTRY}/${PODMAN_TEST_IMAGE_USER}/registry ############################################################################### # BEGIN setup -USER=$PODMAN_ROOTLESS_USER -UID=$PODMAN_ROOTLESS_UID TMPDIR=${TMPDIR:-/tmp} WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX) @@ -56,9 +54,6 @@ fi # Path to podman binary PODMAN_BIN=${PODMAN:-${CONTAINERS_HELPER_BINARY_DIR}/podman} -# Timeout for streamed responses -CURL_TIMEOUT=0 - # Cleanup handlers clean_up_server() { if [ -n "$service_pid" ]; then @@ -112,6 +107,22 @@ function is() { _show_ok 0 "$testname" "$expect" "$actual" } +############ +# is_not # Simple disequality +############ +function is_not() { + local actual=$1 + local expect_not=$2 + local testname=$3 + + if [ "$actual" != "$expect_not" ]; then + # On success, include expected value; this helps readers understand + _show_ok 1 "$testname!=$expect" + return + fi + _show_ok 0 "$testname" "!= $expect" "$actual" +} + ########## # like # Compare, but allowing patterns ########## @@ -135,7 +146,8 @@ function like() { ############## function _show_ok() { local ok=$1 - local testname=$2 + # Exec tests include control characters; filter them out + local testname=$(tr -d \\012 <<<"$2"|cat -vT) # If output is a tty, colorize pass/fail local red= @@ -220,21 +232,6 @@ function jsonify() { } ####### -# t_timeout # Timeout wrapper for test helper -####### -function t_timeout() { - CURL_TIMEOUT=$1; shift - local min_runtime=$((CURL_TIMEOUT - 1)) - start=`date +%s` - t $@ - local end=`date +%s` - local runtime=$((end-start)) - if ! [[ "$runtime" -ge "$min_runtime" ]]; then - die "Error: Streaming time should be greater or equal to '$min_runtime'" - fi -} - -####### # t # Main test helper ####### function t() { @@ -245,23 +242,28 @@ function t() { local testname="$method $path" - if [[ $CURL_TIMEOUT != 0 ]]; then - local c_timeout=$CURL_TIMEOUT - curl_args+=("-m $CURL_TIMEOUT") - CURL_TIMEOUT=0 # 'consume' timeout - fi # POST and PUT requests may be followed by one or more key=value pairs. # Slurp the command line until we see a 3-digit status code. if [[ $method = "POST" || $method == "PUT" || $method = "DELETE" ]]; then local -a post_args + + if [[ $method = "POST" ]]; then + function _add_curl_args() { curl_args+=(--data-binary @$1); } + else + function _add_curl_args() { curl_args+=(--upload-file $1); } + fi + for arg; do case "$arg" in *=*) post_args+=("$arg"); shift;; - *.tar) curl_args+=(--data-binary @$arg); + *.json) _add_curl_args $arg; + content_type="application/json"; + shift;; + *.tar) _add_curl_args $arg; content_type="application/x-tar"; shift;; - *.yaml) curl_args+=(--data-binary @$arg); + *.yaml) _add_curl_args $arg; shift;; application/*) content_type="$arg"; shift;; @@ -270,8 +272,8 @@ function t() { esac done if [[ -z "$curl_args" ]]; then - curl_args=(-d $(jsonify ${post_args[@]})) - testname="$testname [${curl_args[@]}]" + curl_args=(-d $(jsonify ${post_args[*]})) + testname="$testname [${curl_args[*]}]" fi fi @@ -301,6 +303,11 @@ function t() { curl_args+=("--head") fi + # If this is set, we're *expecting* curl to time out + if [[ -n "$APIV2_TEST_EXPECT_TIMEOUT" ]]; then + curl_args+=("-m" $APIV2_TEST_EXPECT_TIMEOUT) + fi + local expected_code=$1; shift # Log every action we do @@ -316,9 +323,20 @@ function t() { --write-out '%{http_code}^%{content_type}^%{time_total}' \ -o $WORKDIR/curl.result.out "$url"); rc=$?; } || : + # Special case: this means we *expect and want* a timeout + if [[ -n "$APIV2_TEST_EXPECT_TIMEOUT" ]]; then + # Hardcoded. See curl(1) for list of exit codes + if [[ $rc -eq 28 ]]; then + _show_ok 1 "$testname: curl timed out (expected)" + else + _show_ok 0 "$testname: expected curl to time out; it did not" + fi + return + fi + # Any error from curl is instant bad news, from which we can't recover - if [[ $rc -ne 0 ]] && [[ $c_timeout -eq 0 ]]; then - die "curl failure ($rc) on $url - cannot continue" + if [[ $rc -ne 0 ]]; then + die "curl failure ($rc) on $url - cannot continue. args=${curl_args[*]}" fi # Show returned headers (without trailing ^M or empty lines) in log file. @@ -366,7 +384,7 @@ function t() { # Special case: if response code does not match, dump the response body # and skip all further subtests. - if [[ $actual_code != $expected_code ]]; then + if [[ "$actual_code" != "$expected_code" ]]; then echo -e "# response: $output" for i; do _show_ok skip "$testname: $i # skip - wrong return code" @@ -375,7 +393,13 @@ function t() { fi for i; do - if expr "$i" : "[^=~]\+=.*" >/dev/null; then + if expr "$i" : '[^\!]\+\!=.\+' >/dev/null; then + # Disequality on json field + json_field=$(expr "$i" : '\([^!]*\)!') + expect_not=$(expr "$i" : '[^\!]*\!=\(.*\)') + actual=$(jq -r "$json_field" <<<"$output") + is_not "$actual" "$expect_not" "$testname : $json_field" + elif expr "$i" : "[^=~]\+=.*" >/dev/null; then # Exact match on json field json_field=$(expr "$i" : "\([^=]*\)=") expect=$(expr "$i" : '[^=]*=\(.*\)') @@ -647,11 +671,11 @@ echo -e "collected ${#tests_to_run[@]} items\n" start_service -for i in ${tests_to_run[@]}; do +for i in "${tests_to_run[@]}"; do TEST_CONTEXT="[$(basename $i .at)]" # Clear output from 'podman' helper - >| $WORKDIR/output.log + truncate --size=0 $WORKDIR/output.log source $i done diff --git a/test/buildah-bud/apply-podman-deltas b/test/buildah-bud/apply-podman-deltas index 6578afc93..999f36bf9 100755 --- a/test/buildah-bud/apply-podman-deltas +++ b/test/buildah-bud/apply-podman-deltas @@ -152,6 +152,10 @@ errmsg "checking authfile: stat /tmp/nonexistent: no such file or directory" \ "Error: checking authfile: stat /tmp/nonexistent: no such file or directory" \ "bud with Containerfile should fail with nonexistent authfile" +errmsg "cannot find Containerfile or Dockerfile" \ + "no such file or directory" \ + "bud-github-context-from-commit" + ############################################################################### # BEGIN tests that don't make sense under podman due to fundamental differences @@ -216,7 +220,10 @@ skip_if_remote "--output option not implemented in podman-remote" \ "build with custom build output and output rootfs to tar" \ "build with custom build output and output rootfs to tar by pipe" \ "build with custom build output must fail for bad input" \ - "build with custom build output and output rootfs to tar with no additional step" + "build with custom build output and output rootfs to tar with no additional step" \ + "build with custom build output for single-stage-cached and output rootfs to directory" \ + "build with custom build output for multi-stage-cached and output rootfs to directory" \ + "build with custom build output for multi-stage and output rootfs to directory" # https://github.com/containers/podman/issues/14544 skip_if_remote "logfile not implemented on remote" "bud-logfile-with-split-logfile-by-platform" @@ -228,6 +235,10 @@ skip_if_remote "envariables do not automatically work with -remote." \ skip_if_remote "FIXME FIXME FIXME: does this test make sense in remote?" \ "build-test with OCI prestart hook" +# 2022-08-17 buildah PR 4190 +skip_if_remote "Explicit request in buildah PR 4190 to skip this on remote" \ + "build: test race in updating image name while performing parallel commits" + ############################################################################### # BEGIN tests which are skipped due to actual podman or podman-remote bugs. diff --git a/test/buildah-bud/buildah-tests.diff b/test/buildah-bud/buildah-tests.diff index 399042240..bf119421e 100644 --- a/test/buildah-bud/buildah-tests.diff +++ b/test/buildah-bud/buildah-tests.diff @@ -1,4 +1,4 @@ -From 6508e3df2a129554fdf8336d8a6f0cdcc6fd4832 Mon Sep 17 00:00:00 2001 +From d22e44c8fb1c87afb90391188733f7ce8fea005d Mon Sep 17 00:00:00 2001 From: Ed Santiago <santiago@redhat.com> Date: Tue, 9 Feb 2021 17:28:05 -0700 Subject: [PATCH] tweaks for running buildah tests under podman @@ -9,10 +9,10 @@ Signed-off-by: Ed Santiago <santiago@redhat.com> 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/tests/helpers.bash b/tests/helpers.bash -index e3087063..178a486e 100644 +index 8cb93c3a..72c1c797 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash -@@ -51,6 +51,23 @@ EOF +@@ -52,6 +52,23 @@ EOF ROOTDIR_OPTS="--root ${TEST_SCRATCH_DIR}/root --runroot ${TEST_SCRATCH_DIR}/runroot --storage-driver ${STORAGE_DRIVER}" BUILDAH_REGISTRY_OPTS="--registries-conf ${TEST_SOURCES}/registries.conf --registries-conf-dir ${TEST_SCRATCH_DIR}/registries.d --short-name-alias-conf ${TEST_SCRATCH_DIR}/cache/shortnames.conf" PODMAN_REGISTRY_OPTS="--registries-conf ${TEST_SOURCES}/registries.conf" @@ -36,7 +36,7 @@ index e3087063..178a486e 100644 } function starthttpd() { -@@ -94,6 +111,12 @@ function teardown_tests() { +@@ -95,6 +112,12 @@ function teardown_tests() { stop_git_daemon stop_registry @@ -49,7 +49,7 @@ index e3087063..178a486e 100644 # Workaround for #1991 - buildah + overlayfs leaks mount points. # Many tests leave behind /var/tmp/.../root/overlay and sub-mounts; # let's find those and clean them up, otherwise 'rm -rf' fails. -@@ -186,6 +209,10 @@ function podman() { +@@ -187,6 +210,10 @@ function podman() { command ${PODMAN_BINARY:-podman} ${PODMAN_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" } @@ -60,7 +60,7 @@ index e3087063..178a486e 100644 # There are various scenarios where we would like to execute `tests` as rootless user, however certain commands like `buildah mount` # do not work in rootless session since a normal user cannot mount a filesystem unless they're in a user namespace along with its # own mount namespace. In order to run such specific commands from a rootless session we must perform `buildah unshare`. -@@ -247,8 +274,36 @@ function run_buildah() { +@@ -248,8 +275,36 @@ function run_buildah() { --retry) retry=3; shift;; # retry network flakes esac @@ -98,7 +98,7 @@ index e3087063..178a486e 100644 # If session is rootless and `buildah mount` is invoked, perform unshare, # since normal user cannot mount a filesystem unless they're in a user namespace along with its own mount namespace. -@@ -262,8 +317,8 @@ function run_buildah() { +@@ -263,8 +318,8 @@ function run_buildah() { retry=$(( retry - 1 )) # stdout is only emitted upon error; this echo is to help a debugger @@ -109,7 +109,7 @@ index e3087063..178a486e 100644 # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" -@@ -595,6 +650,15 @@ function skip_if_no_docker() { +@@ -596,6 +651,15 @@ function skip_if_no_docker() { fi } @@ -126,5 +126,5 @@ index e3087063..178a486e 100644 daemondir=${TEST_SCRATCH_DIR}/git-daemon mkdir -p ${daemondir}/repo -- -2.35.3 +2.36.1 diff --git a/test/buildah-bud/make-new-buildah-diffs b/test/buildah-bud/make-new-buildah-diffs index 3d0a77008..f6404fa51 100644 --- a/test/buildah-bud/make-new-buildah-diffs +++ b/test/buildah-bud/make-new-buildah-diffs @@ -17,7 +17,7 @@ if [[ ! $whereami =~ test-buildah-v ]]; then fi # FIXME: check that git repo is buildah -git remote -v | grep -q [BUILDAHREPO] \ +git remote -v | grep -q '[BUILDAHREPO]' \ || die "This does not look like a buildah repo (git remote -v)" # We could do the commit automatically, but it's prudent to require human diff --git a/test/compose/test-compose b/test/compose/test-compose index 99d063c25..fe2da9532 100755 --- a/test/compose/test-compose +++ b/test/compose/test-compose @@ -64,7 +64,7 @@ function is() { local expect=$2 local testname=$3 - if [[ $actual = $expect ]]; then + if [[ "$actual" = "$expect" ]]; then # On success, include expected value; this helps readers understand _show_ok 1 "$testname=$expect" return @@ -303,12 +303,12 @@ n_tests=0 # We aren't really TAP 13; this helps logformatter recognize our output as BATS echo "TAP version 13" -for t in ${tests_to_run[@]}; do +for t in "${tests_to_run[@]}"; do testdir="$(dirname $t)" testname="$(basename $testdir)" if [ -e $test_dir/SKIP ]; then - local reason="$(<$test_dir/SKIP)" + reason="$(<$test_dir/SKIP)" if [ -n "$reason" ]; then reason=" - $reason" fi diff --git a/test/e2e/benchmarks_test.go b/test/e2e/benchmarks_test.go index 4be048de2..d1332665a 100644 --- a/test/e2e/benchmarks_test.go +++ b/test/e2e/benchmarks_test.go @@ -99,7 +99,7 @@ var _ = Describe("Podman Benchmark Suite", func() { } totalMemoryInKb := func() (total uint64) { - files, err := ioutil.ReadDir(timedir) + files, err := os.ReadDir(timedir) if err != nil { Fail(fmt.Sprintf("Error reading timing dir: %v", err)) } diff --git a/test/e2e/build/Containerfile.userns-auto b/test/e2e/build/Containerfile.userns-auto new file mode 100644 index 000000000..921610982 --- /dev/null +++ b/test/e2e/build/Containerfile.userns-auto @@ -0,0 +1,2 @@ +FROM alpine +RUN cat /proc/self/uid_map diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 8f5e1a0b6..a33936549 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -95,13 +95,15 @@ var _ = Describe("Podman checkpoint", func() { It("podman checkpoint bogus container", func() { session := podmanTest.Podman([]string{"container", "checkpoint", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("no such container")) }) It("podman restore bogus container", func() { session := podmanTest.Podman([]string{"container", "restore", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("no such container or image")) }) It("podman checkpoint a running container by id", func() { @@ -132,6 +134,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal(cid)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -156,6 +159,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal(cid)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -214,6 +218,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("test_name")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -221,6 +226,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("test_name")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -298,6 +304,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("second")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) @@ -310,6 +317,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("second")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) @@ -325,16 +333,20 @@ var _ = Describe("Podman checkpoint", func() { session1 := podmanTest.Podman(localRunString) session1.WaitWithDefaultTimeout() Expect(session1).Should(Exit(0)) + cid1 := session1.OutputToString() localRunString = getRunString([]string{"--name", "second", ALPINE, "top"}) session2 := podmanTest.Podman(localRunString) session2.WaitWithDefaultTimeout() Expect(session2).Should(Exit(0)) + cid2 := session2.OutputToString() result := podmanTest.Podman([]string{"container", "checkpoint", "-a"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid1)) + Expect(result.OutputToString()).To(ContainSubstring(cid2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) @@ -347,6 +359,8 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid1)) + Expect(result.OutputToString()).To(ContainSubstring(cid2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) @@ -573,6 +587,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -592,6 +607,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -611,6 +627,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -630,6 +647,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -647,6 +665,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("not supported")) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) @@ -692,6 +711,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -742,6 +762,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -784,6 +805,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -822,6 +844,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -872,6 +895,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -942,6 +966,7 @@ var _ = Describe("Podman checkpoint", func() { result = podmanTest.Podman([]string{"container", "checkpoint", cid, "-e", checkpointFileName}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -993,9 +1018,6 @@ var _ = Describe("Podman checkpoint", func() { if !criu.MemTrack() { Skip("system (architecture/kernel/CRIU) does not support memory tracking") } - if !strings.Contains(podmanTest.OCIRuntime, "runc") { - Skip("Test only works on runc 1.0-rc3 or higher.") - } localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() @@ -1029,9 +1051,6 @@ var _ = Describe("Podman checkpoint", func() { if !criu.MemTrack() { Skip("system (architecture/kernel/CRIU) does not support memory tracking") } - if !strings.Contains(podmanTest.OCIRuntime, "runc") { - Skip("Test only works on runc 1.0-rc3 or higher.") - } localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() @@ -1051,6 +1070,7 @@ var _ = Describe("Podman checkpoint", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -1097,6 +1117,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -1311,6 +1332,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -1642,6 +1664,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -1796,6 +1819,7 @@ var _ = Describe("Podman checkpoint", func() { // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) fixmeFixme14653(podmanTest, cid) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) diff --git a/test/e2e/cleanup_test.go b/test/e2e/cleanup_test.go new file mode 100644 index 000000000..f15f9bd5a --- /dev/null +++ b/test/e2e/cleanup_test.go @@ -0,0 +1,128 @@ +package integration + +import ( + "os" + + . "github.com/containers/podman/v4/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman container cleanup", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + SkipIfRemote("podman container cleanup is not supported in remote") + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman cleanup bogus container", func() { + session := podmanTest.Podman([]string{"container", "cleanup", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("no such container")) + }) + + It("podman cleanup container by id", func() { + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + session = podmanTest.Podman([]string{"container", "cleanup", cid}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(cid)) + }) + + It("podman cleanup container by short id", func() { + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + shortID := cid[0:10] + session = podmanTest.Podman([]string{"container", "cleanup", shortID}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(shortID)) + }) + + It("podman cleanup container by name", func() { + session := podmanTest.Podman([]string{"create", "--name", "foo", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"container", "cleanup", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal("foo")) + }) + + It("podman cleanup all containers", func() { + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"container", "cleanup", "--all"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(cid)) + }) + + It("podman cleanup latest container", func() { + SkipIfRemote("--latest flag n/a") + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"container", "cleanup", "--latest"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(cid)) + }) + + It("podman cleanup running container", func() { + session := podmanTest.RunTopContainer("running") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"container", "cleanup", "running"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("container state improper")) + }) + + It("podman cleanup paused container", func() { + SkipIfRootlessCgroupsV1("Pause is not supported in cgroups v1") + session := podmanTest.RunTopContainer("paused") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"pause", "paused"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"container", "cleanup", "paused"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("container state improper")) + }) +}) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 2d7c47a7f..690e2f22c 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -306,7 +306,7 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { // happens. So, use a podman-%s.sock-lock empty file as a marker. tries := 0 for { - uuid := stringid.GenerateNonCryptoID() + uuid := stringid.GenerateRandomID() lockPath := fmt.Sprintf("%s-%s.sock-lock", pathPrefix, uuid) lockFile, err := os.OpenFile(lockPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0700) if err == nil { @@ -571,7 +571,7 @@ func (s *PodmanSessionIntegration) InspectContainerToJSON() []define.InspectCont func (s *PodmanSessionIntegration) InspectPodToJSON() define.InspectPodData { var i define.InspectPodData err := jsoniter.Unmarshal(s.Out.Contents(), &i) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) return i } @@ -894,7 +894,7 @@ func generateNetworkConfig(p *PodmanTestIntegration) (string, string) { conf string ) // generate a random name to prevent conflicts with other tests - name := "net" + stringid.GenerateNonCryptoID() + name := "net" + stringid.GenerateRandomID() if p.NetworkBackend != Netavark { path = filepath.Join(p.NetworkConfigDir, fmt.Sprintf("%s.conflist", name)) conf = fmt.Sprintf(`{ @@ -1030,7 +1030,7 @@ func ncz(port int) bool { } func createNetworkName(name string) string { - return name + stringid.GenerateNonCryptoID()[:10] + return name + stringid.GenerateRandomID()[:10] } var IPRegex = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}` diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index c33f32ab4..94bb316b1 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -61,6 +61,8 @@ no_hosts=true network_cmd_options=["allow_host_loopback=true"] service_timeout=1234 +volume_plugin_timeout = 15 + # We need to ensure each test runs on a separate plugin instance... # For now, let's just make a bunch of plugin paths and have each test use one. [engine.volume_plugins] diff --git a/test/e2e/config_amd64.go b/test/e2e/config_amd64.go index f32542df8..ba7940d57 100644 --- a/test/e2e/config_amd64.go +++ b/test/e2e/config_amd64.go @@ -8,7 +8,7 @@ var ( CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, NGINX_IMAGE, REDIS_IMAGE, REGISTRY_IMAGE, INFRA_IMAGE, LABELS_IMAGE, HEALTHCHECK_IMAGE, UBI_INIT, UBI_MINIMAL, fedoraToolbox} //nolint:revive,stylecheck NGINX_IMAGE = "quay.io/libpod/alpine_nginx:latest" //nolint:revive,stylecheck BB_GLIBC = "docker.io/library/busybox:glibc" //nolint:revive,stylecheck - REGISTRY_IMAGE = "quay.io/libpod/registry:2.6" //nolint:revive,stylecheck + REGISTRY_IMAGE = "quay.io/libpod/registry:2.8" //nolint:revive,stylecheck LABELS_IMAGE = "quay.io/libpod/alpine_labels:latest" //nolint:revive,stylecheck UBI_MINIMAL = "registry.access.redhat.com/ubi8-minimal" //nolint:revive,stylecheck UBI_INIT = "registry.access.redhat.com/ubi8-init" //nolint:revive,stylecheck diff --git a/test/e2e/config_arm64.go b/test/e2e/config_arm64.go index c1e0afc47..32ed2f90d 100644 --- a/test/e2e/config_arm64.go +++ b/test/e2e/config_arm64.go @@ -8,7 +8,7 @@ var ( CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, NGINX_IMAGE, REDIS_IMAGE, REGISTRY_IMAGE, INFRA_IMAGE, LABELS_IMAGE, HEALTHCHECK_IMAGE, UBI_INIT, UBI_MINIMAL, fedoraToolbox} //nolint:revive,stylecheck NGINX_IMAGE = "quay.io/lsm5/alpine_nginx-aarch64:latest" //nolint:revive,stylecheck BB_GLIBC = "docker.io/library/busybox:glibc" //nolint:revive,stylecheck - REGISTRY_IMAGE = "quay.io/libpod/registry:2.6" //nolint:revive,stylecheck + REGISTRY_IMAGE = "quay.io/libpod/registry:2.8" //nolint:revive,stylecheck LABELS_IMAGE = "quay.io/libpod/alpine_labels:latest" //nolint:revive,stylecheck UBI_MINIMAL = "registry.access.redhat.com/ubi8-minimal" //nolint:revive,stylecheck UBI_INIT = "registry.access.redhat.com/ubi8-init" //nolint:revive,stylecheck diff --git a/test/e2e/container_clone_test.go b/test/e2e/container_clone_test.go index 94ccd6ffe..1ba5de1a3 100644 --- a/test/e2e/container_clone_test.go +++ b/test/e2e/container_clone_test.go @@ -87,6 +87,7 @@ var _ = Describe("Podman container clone", func() { }) It("podman container clone resource limits override", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") create := podmanTest.Podman([]string{"create", "--cpus=5", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).To(Exit(0)) @@ -292,4 +293,20 @@ var _ = Describe("Podman container clone", func() { Expect(ok).To(BeTrue()) }) + + It("podman container clone env test", func() { + session := podmanTest.Podman([]string{"run", "--name", "env_ctr", "-e", "ENV_TEST=123", ALPINE, "printenv", "ENV_TEST"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"container", "clone", "env_ctr"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"start", "-a", "env_ctr-clone"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("123")) + + }) }) diff --git a/test/e2e/container_create_volume_test.go b/test/e2e/container_create_volume_test.go index 6d9f13694..3c54691aa 100644 --- a/test/e2e/container_create_volume_test.go +++ b/test/e2e/container_create_volume_test.go @@ -58,7 +58,7 @@ func checkDataVolumeContainer(pTest *PodmanTestIntegration, image, cont, dest, d Expect(volList.OutputToStringArray()[0]).To(Equal(mntName)) // Check the mount source directory - files, err := ioutil.ReadDir(mntSource) + files, err := os.ReadDir(mntSource) Expect(err).To(BeNil()) if data == "" { diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go index 32deb04a8..f7ddfe50c 100644 --- a/test/e2e/create_staticmac_test.go +++ b/test/e2e/create_staticmac_test.go @@ -48,7 +48,7 @@ var _ = Describe("Podman run with --mac-address flag", func() { }) It("Podman run --mac-address with custom network", func() { - net := "n1" + stringid.GenerateNonCryptoID() + net := "n1" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 9679aad24..d5920dc3e 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -438,6 +438,7 @@ var _ = Describe("Podman create", func() { }) It("podman create with -m 1000000 sets swap to 2000000", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") numMem := 1000000 ctrName := "testCtr" session := podmanTest.Podman([]string{"create", "-t", "-m", fmt.Sprintf("%db", numMem), "--name", ctrName, ALPINE, "/bin/sh"}) @@ -452,6 +453,7 @@ var _ = Describe("Podman create", func() { }) It("podman create --cpus 5 sets nanocpus", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") numCpus := 5 nanoCPUs := numCpus * 1000000000 ctrName := "testCtr" @@ -593,7 +595,7 @@ var _ = Describe("Podman create", func() { pod.WaitWithDefaultTimeout() Expect(pod).Should(Exit(0)) - netName := "pod" + stringid.GenerateNonCryptoID() + netName := "pod" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/diff_test.go b/test/e2e/diff_test.go index a1f57f41b..c7f235e29 100644 --- a/test/e2e/diff_test.go +++ b/test/e2e/diff_test.go @@ -93,9 +93,9 @@ var _ = Describe("Podman diff", func() { }) It("podman image diff", func() { - file1 := "/" + stringid.GenerateNonCryptoID() - file2 := "/" + stringid.GenerateNonCryptoID() - file3 := "/" + stringid.GenerateNonCryptoID() + file1 := "/" + stringid.GenerateRandomID() + file2 := "/" + stringid.GenerateRandomID() + file3 := "/" + stringid.GenerateRandomID() // Create container image with the files containerfile := fmt.Sprintf(` @@ -152,8 +152,8 @@ RUN echo test }) It("podman diff container and image with same name", func() { - imagefile := "/" + stringid.GenerateNonCryptoID() - confile := "/" + stringid.GenerateNonCryptoID() + imagefile := "/" + stringid.GenerateRandomID() + confile := "/" + stringid.GenerateRandomID() // Create container image with the files containerfile := fmt.Sprintf(` diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index 528fa143d..bba42c3f4 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -42,10 +42,7 @@ var _ = Describe("Podman events", func() { // Perhaps a future version of this test would put events in a go func and send output back over a channel // while events occur. - // These tests are only known to work on Fedora ATM. Other distributions - // will be skipped. It("podman events", func() { - SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false"}) @@ -54,7 +51,6 @@ var _ = Describe("Podman events", func() { }) It("podman events with an event filter", func() { - SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start"}) @@ -81,7 +77,6 @@ var _ = Describe("Podman events", func() { }) It("podman events with a type and filter container=id", func() { - SkipIfNotFedora() _, ec, cid := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod", "--filter", fmt.Sprintf("container=%s", cid)}) @@ -91,7 +86,6 @@ var _ = Describe("Podman events", func() { }) It("podman events with a type", func() { - SkipIfNotFedora() setup := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:foobarpod", ALPINE, "top"}) setup.WaitWithDefaultTimeout() stop := podmanTest.Podman([]string{"pod", "stop", "foobarpod"}) @@ -110,7 +104,6 @@ var _ = Describe("Podman events", func() { }) It("podman events --since", func() { - SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1m"}) @@ -119,7 +112,6 @@ var _ = Describe("Podman events", func() { }) It("podman events --until", func() { - SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--until", "1h"}) @@ -128,7 +120,6 @@ var _ = Describe("Podman events", func() { }) It("podman events format", func() { - SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) @@ -153,12 +144,19 @@ var _ = Describe("Podman events", func() { event = events.Event{} err = json.Unmarshal([]byte(jsonArr[0]), &event) Expect(err).ToNot(HaveOccurred()) + + test = podmanTest.Podman([]string{"events", "--stream=false", "--filter=type=container", "--format", "ID: {{.ID}}"}) + test.WaitWithDefaultTimeout() + Expect(test).To(Exit(0)) + arr := test.OutputToStringArray() + Expect(len(arr)).To(BeNumerically(">", 1)) + Expect(arr[0]).To(MatchRegexp("ID: [a-fA-F0-9]{64}")) }) It("podman events --until future", func() { - name1 := stringid.GenerateNonCryptoID() - name2 := stringid.GenerateNonCryptoID() - name3 := stringid.GenerateNonCryptoID() + name1 := stringid.GenerateRandomID() + name2 := stringid.GenerateRandomID() + name3 := stringid.GenerateRandomID() session := podmanTest.Podman([]string{"create", "--name", name1, ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 845aa60ce..d8308aeea 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -3,6 +3,7 @@ package integration import ( "io/ioutil" "os" + "os/user" "path/filepath" "strconv" "strings" @@ -58,7 +59,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "top"}) + kube := podmanTest.Podman([]string{"kube", "generate", "top"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -71,6 +72,10 @@ var _ = Describe("Podman generate kube", func() { Expect(pod.Spec.Containers[0]).To(HaveField("WorkingDir", "")) Expect(pod.Spec.Containers[0].Env).To(BeNil()) Expect(pod).To(HaveField("Name", "top-pod")) + enableServiceLinks := false + Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks)) + automountServiceAccountToken := false + Expect(pod.Spec).To(HaveField("AutomountServiceAccountToken", &automountServiceAccountToken)) numContainers := 0 for range pod.Spec.Containers { @@ -115,7 +120,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "test"}) + kube := podmanTest.Podman([]string{"kube", "generate", "test"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -130,7 +135,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-ctr"}) + kube := podmanTest.Podman([]string{"kube", "generate", "-s", "test-ctr"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -165,6 +170,10 @@ var _ = Describe("Podman generate kube", func() { err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(pod.Spec).To(HaveField("HostNetwork", false)) + enableServiceLinks := false + Expect(pod.Spec).To(HaveField("EnableServiceLinks", &enableServiceLinks)) + automountServiceAccountToken := false + Expect(pod.Spec).To(HaveField("AutomountServiceAccountToken", &automountServiceAccountToken)) numContainers := 0 for range pod.Spec.Containers { @@ -182,7 +191,7 @@ var _ = Describe("Podman generate kube", func() { pod2.WaitWithDefaultTimeout() Expect(pod2).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "pod2"}) + kube := podmanTest.Podman([]string{"kube", "generate", "pod1", "pod2"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -203,7 +212,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "toppod"}) + kube := podmanTest.Podman([]string{"kube", "generate", "toppod"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -262,6 +271,39 @@ var _ = Describe("Podman generate kube", func() { Expect(numContainers).To(Equal(1)) }) + It("podman generate kube on pod with user namespace", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + name := u.Name + if name == "root" { + name = "containers" + } + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + podSession := podmanTest.Podman([]string{"pod", "create", "--name", "testPod", "--userns=auto"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession).Should(Exit(0)) + + session := podmanTest.Podman([]string{"create", "--name", "topcontainer", "--pod", "testPod", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "testPod"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod := new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + expected := false + Expect(pod.Spec).To(HaveField("HostUsers", &expected)) + }) + It("podman generate kube on pod with host network", func() { podSession := podmanTest.Podman([]string{"pod", "create", "--name", "testHostNetwork", "--network", "host"}) podSession.WaitWithDefaultTimeout() @@ -398,7 +440,7 @@ var _ = Describe("Podman generate kube", func() { ctr2Session.WaitWithDefaultTimeout() Expect(ctr2Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -490,17 +532,18 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate kube on pod with memory limit", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") podName := "testMemoryLimit" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) podSession.WaitWithDefaultTimeout() Expect(podSession).Should(Exit(0)) ctr1Name := "ctr1" - ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--memory", "10Mi", ALPINE, "top"}) + ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--memory", "10M", ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() Expect(ctr1Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -515,6 +558,7 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate kube on pod with cpu limit", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") podName := "testCpuLimit" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) podSession.WaitWithDefaultTimeout() @@ -548,6 +592,11 @@ var _ = Describe("Podman generate kube", func() { It("podman generate kube on pod with ports", func() { podName := "test" + + lock4 := GetPortLock("4000") + defer lock4.Unlock() + lock5 := GetPortLock("5000") + defer lock5.Unlock() podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "4000:4000", "-p", "5000:5000"}) podSession.WaitWithDefaultTimeout() Expect(podSession).Should(Exit(0)) @@ -562,7 +611,7 @@ var _ = Describe("Podman generate kube", func() { ctr2Session.WaitWithDefaultTimeout() Expect(ctr2Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -665,7 +714,7 @@ var _ = Describe("Podman generate kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring("100:200")) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") - kube := podmanTest.Podman([]string{"generate", "kube", "-f", outputFile, podName}) + kube := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -708,7 +757,7 @@ var _ = Describe("Podman generate kube", func() { pod := new(v1.Pod) err = yaml.Unmarshal(b, pod) Expect(err).To(BeNil()) - Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix+vol1, HaveSuffix("z"))) + Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1+":"+"z")) rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "test1"}) rm.WaitWithDefaultTimeout() @@ -788,7 +837,7 @@ var _ = Describe("Podman generate kube", func() { Expect(session).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") - kube := podmanTest.Podman([]string{"generate", "kube", podName, "-f", outputFile}) + kube := podmanTest.Podman([]string{"kube", "generate", podName, "-f", outputFile}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -857,7 +906,7 @@ var _ = Describe("Podman generate kube", func() { pod2.WaitWithDefaultTimeout() Expect(pod2).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"}) + kube := podmanTest.Podman([]string{"kube", "generate", "top1", "top2"}) kube.WaitWithDefaultTimeout() Expect(kube).To(ExitWithError()) }) @@ -911,7 +960,7 @@ var _ = Describe("Podman generate kube", func() { top.WaitWithDefaultTimeout() Expect(top).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "pod1"}) + kube := podmanTest.Podman([]string{"kube", "generate", "pod1"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -973,7 +1022,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube = podmanTest.Podman([]string{"generate", "kube", "test1"}) + kube = podmanTest.Podman([]string{"kube", "generate", "test1"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -1026,7 +1075,7 @@ ENTRYPOINT ["sleep"]` session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube = podmanTest.Podman([]string{"generate", "kube", "testpod-2"}) + kube = podmanTest.Podman([]string{"kube", "generate", "testpod-2"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -1228,4 +1277,27 @@ USER test1` Expect(pod.Spec.Containers[0].Env).To(HaveLen(2)) }) + + It("podman generate kube omit secret if empty", func() { + dir, err := os.MkdirTemp(tempdir, "podman") + Expect(err).Should(BeNil()) + + defer os.RemoveAll(dir) + + podCreate := podmanTest.Podman([]string{"run", "-d", "--pod", "new:" + "noSecretsPod", "--name", "noSecretsCtr", "--volume", dir + ":/foobar", ALPINE}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "noSecretsPod"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + Expect(kube.OutputToString()).ShouldNot(ContainSubstring("secret")) + + pod := new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + Expect(pod.Spec.Volumes[0].Secret).To(BeNil()) + }) }) diff --git a/test/e2e/generate_spec_test.go b/test/e2e/generate_spec_test.go new file mode 100644 index 000000000..9188b5222 --- /dev/null +++ b/test/e2e/generate_spec_test.go @@ -0,0 +1,82 @@ +package integration + +import ( + "os" + "path/filepath" + + . "github.com/containers/podman/v4/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman generate spec", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + SkipIfRemote("podman generate spec is not supported on the remote client") + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman generate spec bogus should fail", func() { + session := podmanTest.Podman([]string{"generate", "spec", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitWithError()) + }) + + It("podman generate spec basic usage", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") + session := podmanTest.Podman([]string{"create", "--cpus", "5", "--name", "specgen", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"generate", "spec", "--compact", "specgen"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman generate spec file", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CgroupsV1") + session := podmanTest.Podman([]string{"create", "--cpus", "5", "--name", "specgen", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"generate", "spec", "--filename", filepath.Join(tempdir, "out.json"), "specgen"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + path := filepath.Join(tempdir, "out.json") + + exec := SystemExec("cat", []string{path}) + exec.WaitWithDefaultTimeout() + Expect(exec.OutputToString()).Should(ContainSubstring("specgen-clone")) + Expect(exec.OutputToString()).Should(ContainSubstring("500000")) + + }) + + It("generate spec pod", func() { + session := podmanTest.Podman([]string{"pod", "create", "--cpus", "5", "--name", "podspecgen"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"generate", "spec", "--compact", "podspecgen"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) +}) diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 45a2f1f86..347440faf 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -3,6 +3,7 @@ package integration import ( "io/ioutil" "os" + "strings" . "github.com/containers/podman/v4/test/utils" . "github.com/onsi/ginkgo" @@ -220,19 +221,20 @@ var _ = Describe("Podman generate systemd", func() { Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) - Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) - Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) - Expect(session.OutputToString()).To(ContainSubstring(" start foo-1")) - Expect(session.OutputToString()).To(ContainSubstring("-infra")) // infra container - Expect(session.OutputToString()).To(ContainSubstring("# container-foo-2.service")) - Expect(session.OutputToString()).To(ContainSubstring(" stop -t 42 foo-2")) - Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("PIDFile=")) - Expect(session.OutputToString()).To(ContainSubstring("/userdata/conmon.pid")) - + output := session.OutputToString() + Expect(output).To(ContainSubstring("# pod-foo.service")) + Expect(output).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) + Expect(output).To(ContainSubstring("# container-foo-1.service")) + Expect(output).To(ContainSubstring(" start foo-1")) + Expect(output).To(ContainSubstring("-infra")) // infra container + Expect(output).To(ContainSubstring("# container-foo-2.service")) + Expect(output).To(ContainSubstring(" stop -t 42 foo-2")) + Expect(output).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(output).To(ContainSubstring("PIDFile=")) + Expect(output).To(ContainSubstring("/userdata/conmon.pid")) + Expect(strings.Count(output, "RequiresMountsFor="+podmanTest.RunRoot)).To(Equal(3)) // The podman commands in the unit should not contain the root flags - Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) + Expect(output).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd pod --name --files", func() { @@ -468,7 +470,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p-foo.service")) }) @@ -492,7 +494,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p_foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=con_foo-1.service con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=con_foo-1.service con_foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("# con_foo-1.service")) Expect(session.OutputToString()).To(ContainSubstring("# con_foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p_foo.service")) @@ -518,7 +520,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session1.OutputToString()).To(ContainSubstring("# foo.service")) - Expect(session1.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session1.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session1.OutputToString()).To(ContainSubstring("# container-foo-1.service")) Expect(session1.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) @@ -529,7 +531,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session2.OutputToString()).To(ContainSubstring("# foo.service")) - Expect(session2.OutputToString()).To(ContainSubstring("Requires=foo-1.service foo-2.service")) + Expect(session2.OutputToString()).To(ContainSubstring("Wants=foo-1.service foo-2.service")) Expect(session2.OutputToString()).To(ContainSubstring("# foo-1.service")) Expect(session2.OutputToString()).To(ContainSubstring("# foo-2.service")) Expect(session2.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) @@ -560,9 +562,9 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo")) + Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --exit-policy=stop --name foo")) Expect(session.OutputToString()).To(ContainSubstring("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id")) Expect(session.OutputToString()).To(ContainSubstring("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10")) Expect(session.OutputToString()).To(ContainSubstring("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")) @@ -600,4 +602,75 @@ var _ = Describe("Podman generate systemd", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" --label key={{someval}}")) }) + + It("podman generate systemd --env", func() { + session := podmanTest.RunTopContainer("test") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "foo=bar", "-e", "hoge=fuga", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("Environment=foo=bar")) + Expect(session.OutputToString()).To(ContainSubstring("Environment=hoge=fuga")) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "=bar", "-e", "hoge=fuga", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("invalid environment variable")) + + // Use -e/--env option with --new option + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "foo=bar", "-e", "hoge=fuga", "--new", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("Environment=foo=bar")) + Expect(session.OutputToString()).To(ContainSubstring("Environment=hoge=fuga")) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "foo=bar", "-e", "=fuga", "--new", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("invalid environment variable")) + + // Escape systemd arguments + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "BAR=my test", "-e", "USER=%a", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("\"BAR=my test\"")) + Expect(session.OutputToString()).To(ContainSubstring("USER=%%a")) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "BAR=my test", "-e", "USER=%a", "--new", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("\"BAR=my test\"")) + Expect(session.OutputToString()).To(ContainSubstring("USER=%%a")) + + // Specify the environment variables without a value + os.Setenv("FOO1", "BAR1") + os.Setenv("FOO2", "BAR2") + os.Setenv("FOO3", "BAR3") + defer os.Unsetenv("FOO1") + defer os.Unsetenv("FOO2") + defer os.Unsetenv("FOO3") + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "FOO1", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("BAR1")) + Expect(session.OutputToString()).NotTo(ContainSubstring("BAR2")) + Expect(session.OutputToString()).NotTo(ContainSubstring("BAR3")) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "FOO*", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("BAR1")) + Expect(session.OutputToString()).To(ContainSubstring("BAR2")) + Expect(session.OutputToString()).To(ContainSubstring("BAR3")) + + session = podmanTest.Podman([]string{"generate", "systemd", "--env", "FOO*", "--new", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("BAR1")) + Expect(session.OutputToString()).To(ContainSubstring("BAR2")) + Expect(session.OutputToString()).To(ContainSubstring("BAR3")) + }) }) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index fd4e763f9..969f83b19 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -317,6 +317,12 @@ HEALTHCHECK CMD ls -l / 2>&1`, ALPINE) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + // Check if image inspect contains CMD-SHELL generated by healthcheck. + session = podmanTest.Podman([]string{"image", "inspect", "--format", "{{.Config.Healthcheck}}", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("CMD-SHELL")) + run := podmanTest.Podman([]string{"run", "-dt", "--name", "hctest", "test", "ls"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go index 77fe810bd..2c275d974 100644 --- a/test/e2e/image_scp_test.go +++ b/test/e2e/image_scp_test.go @@ -3,9 +3,11 @@ package integration import ( "io/ioutil" "os" + "path/filepath" "github.com/containers/common/pkg/config" . "github.com/containers/podman/v4/test/utils" + "github.com/containers/storage/pkg/homedir" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -56,6 +58,9 @@ var _ = Describe("podman image scp", func() { }) It("podman image scp with proper connection", func() { + if _, err := os.Stat(filepath.Join(homedir.Get(), ".ssh", "known_hosts")); err != nil { + Skip("known_hosts does not exist or is not accessible") + } cmd := []string{"system", "connection", "add", "--default", "QA", diff --git a/test/e2e/image_sign_test.go b/test/e2e/image_sign_test.go index 3c819a7d2..5568acc01 100644 --- a/test/e2e/image_sign_test.go +++ b/test/e2e/image_sign_test.go @@ -1,7 +1,6 @@ package integration import ( - "io/ioutil" "os" "os/exec" "path/filepath" @@ -69,7 +68,7 @@ var _ = Describe("Podman image sign", func() { session := podmanTest.Podman([]string{"image", "sign", "--all", "--directory", sigDir, "--sign-by", "foo@bar.com", "docker://library/alpine"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - fInfos, err := ioutil.ReadDir(filepath.Join(sigDir, "library")) + fInfos, err := os.ReadDir(filepath.Join(sigDir, "library")) Expect(err).To(BeNil()) Expect(len(fInfos)).To(BeNumerically(">", 1), "len(fInfos)") }) diff --git a/test/e2e/init_test.go b/test/e2e/init_test.go index ccc102fa3..25b9e079a 100644 --- a/test/e2e/init_test.go +++ b/test/e2e/init_test.go @@ -52,6 +52,7 @@ var _ = Describe("Podman init", func() { init := podmanTest.Podman([]string{"init", cid}) init.WaitWithDefaultTimeout() Expect(init).Should(Exit(0)) + Expect(init.OutputToString()).To(Equal(cid)) result := podmanTest.Podman([]string{"inspect", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -67,6 +68,7 @@ var _ = Describe("Podman init", func() { init := podmanTest.Podman([]string{"init", name}) init.WaitWithDefaultTimeout() Expect(init).Should(Exit(0)) + Expect(init.OutputToString()).To(Equal(name)) result := podmanTest.Podman([]string{"inspect", name}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -79,9 +81,11 @@ var _ = Describe("Podman init", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + cid := session.OutputToString() init := podmanTest.Podman([]string{"init", "--latest"}) init.WaitWithDefaultTimeout() Expect(init).Should(Exit(0)) + Expect(init.OutputToString()).To(Equal(cid)) result := podmanTest.Podman([]string{"inspect", "--latest"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) @@ -93,15 +97,21 @@ var _ = Describe("Podman init", func() { session := podmanTest.Podman([]string{"create", "--name", "test1", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + cid := session.OutputToString() session2 := podmanTest.Podman([]string{"create", "--name", "test2", ALPINE, "ls"}) session2.WaitWithDefaultTimeout() Expect(session2).Should(Exit(0)) + cid2 := session2.OutputToString() session3 := podmanTest.Podman([]string{"run", "--name", "test3", "-d", ALPINE, "top"}) session3.WaitWithDefaultTimeout() Expect(session3).Should(Exit(0)) + cid3 := session3.OutputToString() init := podmanTest.Podman([]string{"init", "--all"}) init.WaitWithDefaultTimeout() Expect(init).Should(Exit(0)) + Expect(init.OutputToString()).To(ContainSubstring(cid)) + Expect(init.OutputToString()).To(ContainSubstring(cid2)) + Expect(init.OutputToString()).To(ContainSubstring(cid3)) result := podmanTest.Podman([]string{"inspect", "test1"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go index 3ae130c6d..60c53e27e 100644 --- a/test/e2e/login_logout_test.go +++ b/test/e2e/login_logout_test.go @@ -52,15 +52,15 @@ var _ = Describe("Podman login and logout", func() { } } - session := podmanTest.Podman([]string{"run", "--entrypoint", "htpasswd", "registry:2.6", "-Bbn", "podmantest", "test"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) + htpasswd := SystemExec("htpasswd", []string{"-Bbn", "podmantest", "test"}) + htpasswd.WaitWithDefaultTimeout() + Expect(htpasswd).Should(Exit(0)) f, err := os.Create(filepath.Join(authPath, "htpasswd")) Expect(err).ToNot(HaveOccurred()) defer f.Close() - _, err = f.WriteString(session.OutputToString()) + _, err = f.WriteString(htpasswd.OutputToString()) Expect(err).ToNot(HaveOccurred()) err = f.Sync() Expect(err).ToNot(HaveOccurred()) @@ -80,12 +80,12 @@ var _ = Describe("Podman login and logout", func() { setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), filepath.Join(certDirPath, "ca.crt")}) setup.WaitWithDefaultTimeout() - session = podmanTest.Podman([]string{"run", "-d", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), + session := podmanTest.Podman([]string{"run", "-d", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), "-e", strings.Join([]string{"REGISTRY_HTTP_ADDR=0.0.0.0", strconv.Itoa(port)}, ":"), "--name", "registry", "-v", strings.Join([]string{authPath, "/auth:Z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "-v", strings.Join([]string{certPath, "/certs:Z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", - "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", "registry:2.6"}) + "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", REGISTRY_IMAGE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -249,7 +249,7 @@ var _ = Describe("Podman login and logout", func() { strings.Join([]string{authPath, "/auth:z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "-v", strings.Join([]string{certPath, "/certs:z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", - "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", "registry:2.6"}) + "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", REGISTRY_IMAGE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 14dd6b6b8..c680cae2a 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -376,7 +376,7 @@ var _ = Describe("Podman logs", func() { skipIfJournaldInContainer() cname := "log-test" - content := stringid.GenerateNonCryptoID() + content := stringid.GenerateRandomID() // use printf to print no extra newline logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "printf", content}) logc.WaitWithDefaultTimeout() diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go index 893210a1f..e38499257 100644 --- a/test/e2e/manifest_test.go +++ b/test/e2e/manifest_test.go @@ -46,9 +46,23 @@ var _ = Describe("Podman manifest", func() { processTestResult(f) }) It("create w/o image", func() { - session := podmanTest.Podman([]string{"manifest", "create", "foo"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) + for _, amend := range []string{"--amend", "-a"} { + session := podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "create", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{"manifest", "create", amend, "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "rm", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } }) It("create w/ image", func() { @@ -173,13 +187,15 @@ var _ = Describe("Podman manifest", func() { session = podmanTest.Podman([]string{"manifest", "add", "foo", imageListInstance}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"manifest", "annotate", "--arch", "bar", "foo", imageListARM64InstanceDigest}) + session = podmanTest.Podman([]string{"manifest", "annotate", "--annotation", "hello=world", "--arch", "bar", "foo", imageListARM64InstanceDigest}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`)) + // Check added annotation + Expect(session.OutputToString()).To(ContainSubstring(`"hello": "world"`)) }) It("remove digest", func() { @@ -316,7 +332,7 @@ var _ = Describe("Podman manifest", func() { blobsDir := filepath.Join(dest, "blobs", "sha256") - blobs, err := ioutil.ReadDir(blobsDir) + blobs, err := os.ReadDir(blobsDir) Expect(err).To(BeNil()) for _, f := range blobs { @@ -334,6 +350,33 @@ var _ = Describe("Podman manifest", func() { Expect(foundZstdFile).To(BeTrue()) }) + It("push progress", func() { + SkipIfRemote("manifest push to dir not supported in remote mode") + + session := podmanTest.Podman([]string{"manifest", "create", "foo", imageList}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + dest := filepath.Join(podmanTest.TempDir, "pushed") + err := os.MkdirAll(dest, os.ModePerm) + Expect(err).To(BeNil()) + defer func() { + os.RemoveAll(dest) + }() + + session = podmanTest.Podman([]string{"push", "foo", "-q", "dir:" + dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.ErrorToString()).To(BeEmpty()) + + session = podmanTest.Podman([]string{"push", "foo", "dir:" + dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output := session.ErrorToString() + Expect(output).To(ContainSubstring("Writing manifest list to image destination")) + Expect(output).To(ContainSubstring("Storing list signatures")) + }) + It("authenticated push", func() { registryOptions := &podmanRegistry.Options{ Image: "docker-archive:" + imageTarPath(REGISTRY_IMAGE), @@ -377,6 +420,11 @@ var _ = Describe("Podman manifest", func() { push = podmanTest.Podman([]string{"manifest", "push", "--tls-verify=false", "--creds=" + registry.User + ":" + registry.Password, "foo", "localhost:" + registry.Port + "/credstest"}) push.WaitWithDefaultTimeout() Expect(push).Should(Exit(0)) + output := push.ErrorToString() + Expect(output).To(ContainSubstring("Copying blob ")) + Expect(output).To(ContainSubstring("Copying config ")) + Expect(output).To(ContainSubstring("Writing manifest to image destination")) + Expect(output).To(ContainSubstring("Storing signatures")) push = podmanTest.Podman([]string{"manifest", "push", "--tls-verify=false", "--creds=podmantest:wrongpasswd", "foo", "localhost:" + registry.Port + "/credstest"}) push.WaitWithDefaultTimeout() diff --git a/test/e2e/mount_rootless_test.go b/test/e2e/mount_rootless_test.go index 994a5899b..b0452deda 100644 --- a/test/e2e/mount_rootless_test.go +++ b/test/e2e/mount_rootless_test.go @@ -52,9 +52,16 @@ var _ = Describe("Podman mount", func() { Expect(setup).Should(Exit(0)) cid := setup.OutputToString() - session := podmanTest.Podman([]string{"unshare", PODMAN_BINARY, "mount", cid}) + // command: podman <options> unshare podman <options> mount cid + args := []string{"unshare", podmanTest.PodmanBinary} + opts := podmanTest.PodmanMakeOptions([]string{"mount", cid}, false, false) + args = append(args, opts...) + + // container root file system location is /tmp/... because "--root /tmp/..." + session := podmanTest.Podman(args) session.WaitWithDefaultTimeout() - Expect(setup).Should(Exit(0)) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("/tmp")) }) It("podman image mount", func() { @@ -71,8 +78,15 @@ var _ = Describe("Podman mount", func() { setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) - session := podmanTest.Podman([]string{"unshare", PODMAN_BINARY, "image", "mount", ALPINE}) + // command: podman <options> unshare podman <options> image mount ALPINE + args := []string{"unshare", podmanTest.PodmanBinary} + opts := podmanTest.PodmanMakeOptions([]string{"image", "mount", ALPINE}, false, false) + args = append(args, opts...) + + // image location is /tmp/... because "--root /tmp/..." + session := podmanTest.Podman(args) session.WaitWithDefaultTimeout() - Expect(setup).Should(Exit(0)) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("/tmp")) }) }) diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index ece1b519d..6e6a7fb39 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -41,7 +41,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad container name in network disconnect should result in error", func() { - netName := "aliasTest" + stringid.GenerateNonCryptoID() + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -53,7 +53,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("network disconnect with net mode slirp4netns should result in error", func() { - netName := "slirp" + stringid.GenerateNonCryptoID() + netName := "slirp" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -72,7 +72,7 @@ var _ = Describe("Podman network connect and disconnect", func() { It("podman network disconnect", func() { SkipIfRootlessCgroupsV1("stats not supported under rootless CgroupsV1") - netName := "aliasTest" + stringid.GenerateNonCryptoID() + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -128,7 +128,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad container name in network connect should result in error", func() { - netName := "aliasTest" + stringid.GenerateNonCryptoID() + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -140,7 +140,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("network connect with net mode slirp4netns should result in error", func() { - netName := "slirp" + stringid.GenerateNonCryptoID() + netName := "slirp" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -157,8 +157,8 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(con.ErrorToString()).To(ContainSubstring(`"slirp4netns" is not supported: invalid network mode`)) }) - It("podman connect on a container that already is connected to the network should error", func() { - netName := "aliasTest" + stringid.GenerateNonCryptoID() + It("podman connect on a container that already is connected to the network should error after init", func() { + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -177,12 +177,20 @@ var _ = Describe("Podman network connect and disconnect", func() { con := podmanTest.Podman([]string{"network", "connect", netName, "test"}) con.WaitWithDefaultTimeout() - Expect(con).Should(ExitWithError()) + Expect(con).Should(Exit(0)) + + init := podmanTest.Podman([]string{"init", "test"}) + init.WaitWithDefaultTimeout() + Expect(init).Should(Exit(0)) + + con2 := podmanTest.Podman([]string{"network", "connect", netName, "test"}) + con2.WaitWithDefaultTimeout() + Expect(con2).Should(ExitWithError()) }) It("podman network connect", func() { SkipIfRootlessCgroupsV1("stats not supported under rootless CgroupsV1") - netName := "aliasTest" + stringid.GenerateNonCryptoID() + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -198,7 +206,7 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(exec).Should(Exit(0)) // Create a second network - newNetName := "aliasTest" + stringid.GenerateNonCryptoID() + newNetName := "aliasTest" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", newNetName, "--subnet", "10.11.100.0/24"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -256,13 +264,13 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network connect when not running", func() { - netName1 := "connect1" + stringid.GenerateNonCryptoID() + netName1 := "connect1" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) defer podmanTest.removeNetwork(netName1) - netName2 := "connect2" + stringid.GenerateNonCryptoID() + netName2 := "connect2" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", netName2}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -295,7 +303,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network connect and run with network ID", func() { - netName := "ID" + stringid.GenerateNonCryptoID() + netName := "ID" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -315,7 +323,7 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(exec).Should(Exit(0)) // Create a second network - newNetName := "ID2" + stringid.GenerateNonCryptoID() + newNetName := "ID2" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", newNetName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -342,13 +350,13 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network disconnect when not running", func() { - netName1 := "aliasTest" + stringid.GenerateNonCryptoID() + netName1 := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) defer podmanTest.removeNetwork(netName1) - netName2 := "aliasTest" + stringid.GenerateNonCryptoID() + netName2 := "aliasTest" + stringid.GenerateRandomID() session2 := podmanTest.Podman([]string{"network", "create", netName2}) session2.WaitWithDefaultTimeout() Expect(session2).Should(Exit(0)) @@ -387,7 +395,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network disconnect and run with network ID", func() { - netName := "aliasTest" + stringid.GenerateNonCryptoID() + netName := "aliasTest" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 69966b07e..f7855581b 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -41,7 +41,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with name and subnet", func() { - netName := "subnet-" + stringid.GenerateNonCryptoID() + netName := "subnet-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--ip-range", "10.11.12.0/26", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -84,7 +84,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with name and IPv6 subnet", func() { - netName := "ipv6-" + stringid.GenerateNonCryptoID() + netName := "ipv6-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:1:2:3:4::/64", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -123,7 +123,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with name and IPv6 flag (dual-stack)", func() { - netName := "dual-" + stringid.GenerateNonCryptoID() + netName := "dual-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:3:2::/64", "--ipv6", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -156,7 +156,7 @@ var _ = Describe("Podman network create", func() { // create a second network to check the auto assigned ipv4 subnet does not overlap // https://github.com/containers/podman/issues/11032 - netName2 := "dual-" + stringid.GenerateNonCryptoID() + netName2 := "dual-" + stringid.GenerateRandomID() nc = podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:10:3:2::/64", "--ipv6", netName2}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -204,13 +204,13 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with invalid subnet", func() { - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/17000", stringid.GenerateNonCryptoID()}) + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/17000", stringid.GenerateRandomID()}) nc.WaitWithDefaultTimeout() Expect(nc).To(ExitWithError()) }) It("podman network create with ipv4 subnet and ipv6 flag", func() { - name := stringid.GenerateNonCryptoID() + name := stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--ipv6", name}) nc.WaitWithDefaultTimeout() Expect(nc).To(Exit(0)) @@ -224,7 +224,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with empty subnet and ipv6 flag", func() { - name := stringid.GenerateNonCryptoID() + name := stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--ipv6", name}) nc.WaitWithDefaultTimeout() Expect(nc).To(Exit(0)) @@ -238,19 +238,19 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with invalid IP", func() { - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.0/17000", stringid.GenerateNonCryptoID()}) + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.0/17000", stringid.GenerateRandomID()}) nc.WaitWithDefaultTimeout() Expect(nc).To(ExitWithError()) }) It("podman network create with invalid gateway for subnet", func() { - nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--gateway", "192.168.1.1", stringid.GenerateNonCryptoID()}) + nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", "--gateway", "192.168.1.1", stringid.GenerateRandomID()}) nc.WaitWithDefaultTimeout() Expect(nc).To(ExitWithError()) }) It("podman network create two networks with same name should fail", func() { - netName := "same-" + stringid.GenerateNonCryptoID() + netName := "same-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -262,13 +262,13 @@ var _ = Describe("Podman network create", func() { }) It("podman network create two networks with same subnet should fail", func() { - netName1 := "sub1-" + stringid.GenerateNonCryptoID() + netName1 := "sub1-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.13.0/24", netName1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName1) Expect(nc).Should(Exit(0)) - netName2 := "sub2-" + stringid.GenerateNonCryptoID() + netName2 := "sub2-" + stringid.GenerateRandomID() ncFail := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.13.0/24", netName2}) ncFail.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -276,13 +276,13 @@ var _ = Describe("Podman network create", func() { }) It("podman network create two IPv6 networks with same subnet should fail", func() { - netName1 := "subipv61-" + stringid.GenerateNonCryptoID() + netName1 := "subipv61-" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:4:4:4::/64", "--ipv6", netName1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName1) Expect(nc).Should(Exit(0)) - netName2 := "subipv62-" + stringid.GenerateNonCryptoID() + netName2 := "subipv62-" + stringid.GenerateRandomID() ncFail := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:4:4:4::/64", "--ipv6", netName2}) ncFail.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -296,7 +296,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with mtu option", func() { - net := "mtu-test" + stringid.GenerateNonCryptoID() + net := "mtu-test" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--opt", "mtu=9000", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -309,7 +309,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with vlan option", func() { - net := "vlan-test" + stringid.GenerateNonCryptoID() + net := "vlan-test" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--opt", "vlan=9", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -322,7 +322,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with invalid option", func() { - net := "invalid-test" + stringid.GenerateNonCryptoID() + net := "invalid-test" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--opt", "foo=bar", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -331,7 +331,7 @@ var _ = Describe("Podman network create", func() { It("podman CNI network create with internal should not have dnsname", func() { SkipIfNetavark(podmanTest) - net := "internal-test" + stringid.GenerateNonCryptoID() + net := "internal-test" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--internal", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -349,7 +349,7 @@ var _ = Describe("Podman network create", func() { It("podman Netavark network create with internal should have dnsname", func() { SkipIfCNI(podmanTest) - net := "internal-test" + stringid.GenerateNonCryptoID() + net := "internal-test" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--internal", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -375,7 +375,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with multiple subnets", func() { - name := "subnets-" + stringid.GenerateNonCryptoID() + name := "subnets-" + stringid.GenerateRandomID() subnet1 := "10.10.0.0/24" subnet2 := "10.10.1.0/24" nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--subnet", subnet2, name}) @@ -393,7 +393,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with multiple subnets dual stack", func() { - name := "subnets-" + stringid.GenerateNonCryptoID() + name := "subnets-" + stringid.GenerateRandomID() subnet1 := "10.10.2.0/24" subnet2 := "fd52:2a5a:747e:3acd::/64" nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--subnet", subnet2, name}) @@ -411,7 +411,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create with multiple subnets dual stack with gateway and range", func() { - name := "subnets-" + stringid.GenerateNonCryptoID() + name := "subnets-" + stringid.GenerateRandomID() subnet1 := "10.10.3.0/24" gw1 := "10.10.3.10" range1 := "10.10.3.0/26" @@ -436,7 +436,7 @@ var _ = Describe("Podman network create", func() { }) It("podman network create invalid options with multiple subnets", func() { - name := "subnets-" + stringid.GenerateNonCryptoID() + name := "subnets-" + stringid.GenerateRandomID() subnet1 := "10.10.3.0/24" gw1 := "10.10.3.10" gw2 := "fd52:2a5a:747e:3acf::10" diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index d4f60d3e4..b2f50ca55 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -113,7 +113,7 @@ var _ = Describe("Podman network", func() { }) It("podman network list --filter labels", func() { - net1 := "labelnet" + stringid.GenerateNonCryptoID() + net1 := "labelnet" + stringid.GenerateRandomID() label1 := "testlabel1=abc" label2 := "abcdef" session := podmanTest.Podman([]string{"network", "create", "--label", label1, net1}) @@ -121,7 +121,7 @@ var _ = Describe("Podman network", func() { defer podmanTest.removeNetwork(net1) Expect(session).Should(Exit(0)) - net2 := "labelnet" + stringid.GenerateNonCryptoID() + net2 := "labelnet" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", "--label", label1, "--label", label2, net2}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net2) @@ -141,7 +141,7 @@ var _ = Describe("Podman network", func() { }) It("podman network list --filter invalid value", func() { - net := "net" + stringid.GenerateNonCryptoID() + net := "net" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -294,7 +294,7 @@ var _ = Describe("Podman network", func() { }) It("podman inspect container single CNI network", func() { - netName := "net-" + stringid.GenerateNonCryptoID() + netName := "net-" + stringid.GenerateRandomID() network := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.50.0/24", netName}) network.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -324,13 +324,13 @@ var _ = Describe("Podman network", func() { }) It("podman inspect container two CNI networks (container not running)", func() { - netName1 := "net1-" + stringid.GenerateNonCryptoID() + netName1 := "net1-" + stringid.GenerateRandomID() network1 := podmanTest.Podman([]string{"network", "create", netName1}) network1.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName1) Expect(network1).Should(Exit(0)) - netName2 := "net2-" + stringid.GenerateNonCryptoID() + netName2 := "net2-" + stringid.GenerateRandomID() network2 := podmanTest.Podman([]string{"network", "create", netName2}) network2.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -361,13 +361,13 @@ var _ = Describe("Podman network", func() { }) It("podman inspect container two CNI networks", func() { - netName1 := "net1-" + stringid.GenerateNonCryptoID() + netName1 := "net1-" + stringid.GenerateRandomID() network1 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.0/25", netName1}) network1.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName1) Expect(network1).Should(Exit(0)) - netName2 := "net2-" + stringid.GenerateNonCryptoID() + netName2 := "net2-" + stringid.GenerateRandomID() network2 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.128/26", netName2}) network2.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -403,7 +403,7 @@ var _ = Describe("Podman network", func() { It("podman network remove after disconnect when container initially created with the network", func() { container := "test" - network := "foo" + stringid.GenerateNonCryptoID() + network := "foo" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", network}) session.WaitWithDefaultTimeout() @@ -430,7 +430,7 @@ var _ = Describe("Podman network", func() { }) It("podman network remove --force with pod", func() { - netName := "net-" + stringid.GenerateNonCryptoID() + netName := "net-" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName) @@ -466,13 +466,13 @@ var _ = Describe("Podman network", func() { }) It("podman network remove with two networks", func() { - netName1 := "net1-" + stringid.GenerateNonCryptoID() + netName1 := "net1-" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName1) Expect(session).Should(Exit(0)) - netName2 := "net2-" + stringid.GenerateNonCryptoID() + netName2 := "net2-" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", netName2}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(netName2) @@ -591,7 +591,7 @@ var _ = Describe("Podman network", func() { It("podman network create/remove macvlan", func() { // Netavark currently does not do dhcp so the this test fails SkipIfNetavark(podmanTest) - net := "macvlan" + stringid.GenerateNonCryptoID() + net := "macvlan" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "--macvlan", "lo", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -605,7 +605,7 @@ var _ = Describe("Podman network", func() { It("podman network create/remove macvlan as driver (-d) no device name", func() { // Netavark currently does not do dhcp so the this test fails SkipIfNetavark(podmanTest) - net := "macvlan" + stringid.GenerateNonCryptoID() + net := "macvlan" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -632,7 +632,7 @@ var _ = Describe("Podman network", func() { It("podman network create/remove macvlan as driver (-d) with device name", func() { // Netavark currently does not do dhcp so the this test fails SkipIfNetavark(podmanTest) - net := "macvlan" + stringid.GenerateNonCryptoID() + net := "macvlan" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", "-o", "parent=lo", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -659,7 +659,7 @@ var _ = Describe("Podman network", func() { }) It("podman network exists", func() { - net := "net" + stringid.GenerateNonCryptoID() + net := "net" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -669,13 +669,13 @@ var _ = Describe("Podman network", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"network", "exists", stringid.GenerateNonCryptoID()}) + session = podmanTest.Podman([]string{"network", "exists", stringid.GenerateRandomID()}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) }) It("podman network create macvlan with network info and options", func() { - net := "macvlan" + stringid.GenerateNonCryptoID() + net := "macvlan" + stringid.GenerateRandomID() nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", "-o", "parent=lo", "-o", "mtu=1500", "--gateway", "192.168.1.254", "--subnet", "192.168.1.0/24", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -711,7 +711,7 @@ var _ = Describe("Podman network", func() { if IsRemote() { podmanTest.RestartRemoteService() } - net1 := "macvlan" + stringid.GenerateNonCryptoID() + "net1" + net1 := "macvlan" + stringid.GenerateRandomID() + "net1" nc := podmanTest.Podman([]string{"network", "create", net1}) nc.WaitWithDefaultTimeout() @@ -764,7 +764,7 @@ var _ = Describe("Podman network", func() { // Run a container on one of them // Network Prune // Check that one has been pruned, other remains - net := "macvlan" + stringid.GenerateNonCryptoID() + net := "macvlan" + stringid.GenerateRandomID() net1 := net + "1" net2 := net + "2" nc := podmanTest.Podman([]string{"network", "create", net1}) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 1b4eefd45..26460c937 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -380,6 +380,9 @@ spec: restartPolicy: {{ .RestartPolicy }} hostname: {{ .Hostname }} hostNetwork: {{ .HostNetwork }} +{{ if .HostUsers }} + hostUsers: {{ .HostUsers }} +{{ end }} hostAliases: {{ range .HostAliases }} - hostnames: @@ -509,6 +512,9 @@ spec: volumes: {{ range . }} - name: {{ .Name }} + {{- if (eq .VolumeType "EmptyDir") }} + emptyDir: {} + {{- end }} {{- if (eq .VolumeType "HostPath") }} hostPath: path: {{ .HostPath.Path }} @@ -841,6 +847,7 @@ type Pod struct { RestartPolicy string Hostname string HostNetwork bool + HostUsers *bool HostAliases []HostAlias Ctrs []*Ctr InitCtrs []*Ctr @@ -965,6 +972,12 @@ func withHostNetwork() podOption { } } +func withHostUsers(val bool) podOption { + return func(pod *Pod) { + pod.HostUsers = &val + } +} + // Deployment describes the options a kube yaml can be configured at deployment level type Deployment struct { Name string @@ -1242,12 +1255,15 @@ type ConfigMap struct { Optional bool } +type EmptyDir struct{} + type Volume struct { VolumeType string Name string HostPath PersistentVolumeClaim ConfigMap + EmptyDir } // getHostPathVolume takes a type and a location for a HostPath @@ -1289,6 +1305,14 @@ func getConfigMapVolume(vName string, items []map[string]string, optional bool) } } +func getEmptyDirVolume() *Volume { + return &Volume{ + VolumeType: "EmptyDir", + Name: defaultVolName, + EmptyDir: EmptyDir{}, + } +} + type Env struct { Name string Value string @@ -2409,7 +2433,7 @@ spec: err := generateKubeYaml("deployment", deployment, kubeYaml) Expect(err).To(BeNil()) - net := "playkube" + stringid.GenerateNonCryptoID() + net := "playkube" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.31.0/24", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -2451,8 +2475,8 @@ spec: err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) - net1 := "net1" + stringid.GenerateNonCryptoID() - net2 := "net2" + stringid.GenerateNonCryptoID() + net1 := "net1" + stringid.GenerateRandomID() + net2 := "net2" + stringid.GenerateRandomID() net := podmanTest.Podman([]string{"network", "create", "--subnet", "10.0.11.0/24", net1}) net.WaitWithDefaultTimeout() @@ -2482,7 +2506,7 @@ spec: It("podman play kube test with network portbindings", func() { ip := "127.0.0.100" - port := "5000" + port := "8087" ctr := getCtr(withHostIP(ip, port), withImage(BB)) pod := getPod(withCtr(ctr)) @@ -2496,7 +2520,7 @@ spec: inspect := podmanTest.Podman([]string{"port", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(Equal("5000/tcp -> 127.0.0.100:5000")) + Expect(inspect.OutputToString()).To(Equal("8087/tcp -> 127.0.0.100:8087")) }) It("podman play kube test with nonexistent empty HostPath type volume", func() { @@ -2762,6 +2786,43 @@ VOLUME %s`, ALPINE, hostPathDir+"/") Expect(kube).Should(Exit(0)) }) + It("podman play kube with emptyDir volume", func() { + podName := "test-pod" + ctrName1 := "vol-test-ctr" + ctrName2 := "vol-test-ctr-2" + ctr1 := getCtr(withVolumeMount("/test-emptydir", false), withImage(BB), withName(ctrName1)) + ctr2 := getCtr(withVolumeMount("/test-emptydir-2", false), withImage(BB), withName(ctrName2)) + pod := getPod(withPodName(podName), withVolume(getEmptyDirVolume()), withCtr(ctr1), withCtr(ctr2)) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + emptyDirCheck1 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName1, "ls", "/test-emptydir"}) + emptyDirCheck1.WaitWithDefaultTimeout() + Expect(emptyDirCheck1).Should(Exit(0)) + + emptyDirCheck2 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName2, "ls", "/test-emptydir-2"}) + emptyDirCheck2.WaitWithDefaultTimeout() + Expect(emptyDirCheck2).Should(Exit(0)) + + volList1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + volList1.WaitWithDefaultTimeout() + Expect(volList1).Should(Exit(0)) + Expect(volList1.OutputToString()).To(Equal(defaultVolName)) + + remove := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + remove.WaitWithDefaultTimeout() + Expect(remove).Should(Exit(0)) + + volList2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + volList2.WaitWithDefaultTimeout() + Expect(volList2).Should(Exit(0)) + Expect(volList2.OutputToString()).To(Equal("")) + }) + It("podman play kube applies labels to pods", func() { var numReplicas int32 = 5 expectedLabelKey := "key1" @@ -3732,8 +3793,7 @@ ENV OPENJ9_JAVA_OPTIONS=%q Expect((inspect.InspectContainerToJSON()[0]).HostConfig.LogConfig.Tag).To(Equal("{{.ImageName}}")) }) - // Check that --userns=auto creates a user namespace - It("podman play kube --userns=auto", func() { + It("podman play kube using a user namespace", func() { u, err := user.Current() Expect(err).To(BeNil()) name := u.Name @@ -3780,6 +3840,26 @@ ENV OPENJ9_JAVA_OPTIONS=%q usernsInCtr.WaitWithDefaultTimeout() Expect(usernsInCtr).Should(Exit(0)) Expect(string(usernsInCtr.Out.Contents())).To(Not(Equal(string(initialUsernsConfig)))) + + // Now try with hostUsers in the pod spec + for _, hostUsers := range []bool{true, false} { + pod = getPod(withHostUsers(hostUsers)) + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube = podmanTest.PodmanNoCache([]string{"play", "kube", "--replace", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + usernsInCtr = podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "cat", "/proc/self/uid_map"}) + usernsInCtr.WaitWithDefaultTimeout() + Expect(usernsInCtr).Should(Exit(0)) + if hostUsers { + Expect(string(usernsInCtr.Out.Contents())).To(Equal(string(initialUsernsConfig))) + } else { + Expect(string(usernsInCtr.Out.Contents())).To(Not(Equal(string(initialUsernsConfig)))) + } + } }) // Check the block devices are exposed inside container diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 351317cc5..cefdee40a 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -118,4 +118,21 @@ var _ = Describe("Podman pod inspect", func() { Expect(inspectOut.OutputToString()).To(ContainSubstring(macAddr)) }) + + It("podman inspect two pods", func() { + _, ec, podid1 := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + _, ec, podid2 := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"pod", "inspect", podid1, podid2}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(BeValidJSON()) + podData := inspect.InspectPodArrToJSON() + Expect(podData).To(HaveLen(2)) + Expect(podData[0]).To(HaveField("ID", podid1)) + Expect(podData[1]).To(HaveField("ID", podid2)) + }) }) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 97ca5ff94..20335f322 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -294,7 +294,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps filter network", func() { - net := stringid.GenerateNonCryptoID() + net := stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -333,12 +333,12 @@ var _ = Describe("Podman ps", func() { Expect(session.OutputToString()).To(Equal("podman")) } - net1 := stringid.GenerateNonCryptoID() + net1 := stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", net1}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) defer podmanTest.removeNetwork(net1) - net2 := stringid.GenerateNonCryptoID() + net2 := stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", net2}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index a5eab7eed..364ef54d5 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -318,4 +318,31 @@ var _ = Describe("Podman pod rm", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) }) + + It("podman pod rm pod with infra container and running container", func() { + podName := "testPod" + ctrName := "testCtr" + + ctrAndPod := podmanTest.Podman([]string{"run", "-d", "--pod", fmt.Sprintf("new:%s", podName), "--name", ctrName, ALPINE, "top"}) + ctrAndPod.WaitWithDefaultTimeout() + Expect(ctrAndPod).Should(Exit(0)) + + removePod := podmanTest.Podman([]string{"pod", "rm", "-a"}) + removePod.WaitWithDefaultTimeout() + Expect(removePod).Should(Not(Exit(0))) + + ps := podmanTest.Podman([]string{"pod", "ps"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring(podName)) + + removePodForce := podmanTest.Podman([]string{"pod", "rm", "-af"}) + removePodForce.WaitWithDefaultTimeout() + Expect(removePodForce).Should(Exit(0)) + + ps2 := podmanTest.Podman([]string{"pod", "ps"}) + ps2.WaitWithDefaultTimeout() + Expect(ps2).Should(Exit(0)) + Expect(ps2.OutputToString()).To(Not(ContainSubstring(podName))) + }) }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 1100a5d90..cc037545c 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -817,7 +817,7 @@ var _ = Describe("Podman ps", func() { }) It("podman ps filter network", func() { - net := stringid.GenerateNonCryptoID() + net := stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -860,12 +860,12 @@ var _ = Describe("Podman ps", func() { Expect(actual).To(Equal("podman")) } - net1 := stringid.GenerateNonCryptoID() + net1 := stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", net1}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) defer podmanTest.removeNetwork(net1) - net2 := stringid.GenerateNonCryptoID() + net2 := stringid.GenerateRandomID() session = podmanTest.Podman([]string{"network", "create", net2}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index 12f14fdc8..ba717f393 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -545,4 +545,18 @@ var _ = Describe("Podman pull", func() { Expect(data[0]).To(HaveField("Os", runtime.GOOS)) Expect(data[0]).To(HaveField("Architecture", "arm64")) }) + + It("podman pull progress", func() { + session := podmanTest.Podman([]string{"pull", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output := session.ErrorToString() + Expect(output).To(ContainSubstring("Getting image source signatures")) + Expect(output).To(ContainSubstring("Copying blob ")) + + session = podmanTest.Podman([]string{"pull", "-q", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.ErrorToString()).To(BeEmpty()) + }) }) diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index f2a103f6b..a73b7c87b 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "strings" @@ -77,7 +78,7 @@ var _ = Describe("Podman push", func() { blobsDir := filepath.Join(bbdir, "blobs/sha256") - blobs, err := ioutil.ReadDir(blobsDir) + blobs, err := os.ReadDir(blobsDir) Expect(err).To(BeNil()) for _, f := range blobs { @@ -136,6 +137,45 @@ var _ = Describe("Podman push", func() { Expect(fi.Name()).To(Equal("digestfile.txt")) Expect(push2).Should(Exit(0)) } + + if !IsRemote() { // Remote does not support signing + By("pushing and pulling with sigstore signatures") + // Ideally, this should set SystemContext.RegistriesDirPath, but Podman currently doesn’t + // expose that as an option. So, for now, modify /etc/directly, and skip testing sigstore if + // we don’t have permission to do so. + systemRegistriesDAddition := "/etc/containers/registries.d/podman-test-only-temporary-addition.yaml" + cmd := exec.Command("cp", "testdata/sigstore-registries.d-fragment.yaml", systemRegistriesDAddition) + output, err := cmd.CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "Skipping sigstore tests because /etc/containers/registries.d isn’t writable: %s", string(output)) + } else { + defer func() { + err := os.Remove(systemRegistriesDAddition) + Expect(err).ToNot(HaveOccurred()) + }() + + // Verify that the policy rejects unsigned images + push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/sigstore-signed"}) + push.WaitWithDefaultTimeout() + Expect(push).Should(Exit(0)) + Expect(len(push.ErrorToString())).To(Equal(0)) + + pull := podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", "sign/policy.json", "localhost:5000/sigstore-signed"}) + pull.WaitWithDefaultTimeout() + Expect(pull).To(ExitWithError()) + Expect(pull.ErrorToString()).To(ContainSubstring("A signature was required, but no signature exists")) + + // Sign an image, and verify it is accepted. + push = podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", "--sign-by-sigstore-private-key", "testdata/sigstore-key.key", "--sign-passphrase-file", "testdata/sigstore-key.key.pass", ALPINE, "localhost:5000/sigstore-signed"}) + push.WaitWithDefaultTimeout() + Expect(push).Should(Exit(0)) + Expect(len(push.ErrorToString())).To(Equal(0)) + + pull = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", "sign/policy.json", "localhost:5000/sigstore-signed"}) + pull.WaitWithDefaultTimeout() + Expect(pull).Should(Exit(0)) + } + } }) It("podman push to local registry with authorization", func() { @@ -167,20 +207,20 @@ var _ = Describe("Podman push", func() { } lock := GetPortLock("5000") defer lock.Unlock() - session := podmanTest.Podman([]string{"run", "--entrypoint", "htpasswd", REGISTRY_IMAGE, "-Bbn", "podmantest", "test"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) + htpasswd := SystemExec("htpasswd", []string{"-Bbn", "podmantest", "test"}) + htpasswd.WaitWithDefaultTimeout() + Expect(htpasswd).Should(Exit(0)) f, err := os.Create(filepath.Join(authPath, "htpasswd")) Expect(err).ToNot(HaveOccurred()) defer f.Close() - _, err = f.WriteString(session.OutputToString()) + _, err = f.WriteString(htpasswd.OutputToString()) Expect(err).ToNot(HaveOccurred()) err = f.Sync() Expect(err).ToNot(HaveOccurred()) - session = podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry", "-v", + session := podmanTest.Podman([]string{"run", "-d", "-p", "5000:5000", "--name", "registry", "-v", strings.Join([]string{authPath, "/auth"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "-v", strings.Join([]string{certPath, "/certs"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index b3052623b..9df884292 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -1,6 +1,8 @@ package integration import ( + "fmt" + "io/ioutil" "os" "time" @@ -33,13 +35,13 @@ var _ = Describe("Podman restart", func() { }) - It("Podman restart bogus container", func() { + It("podman restart bogus container", func() { session := podmanTest.Podman([]string{"start", "123"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) }) - It("Podman restart stopped container by name", func() { + It("podman restart stopped container by name", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"}) @@ -53,7 +55,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) }) - It("Podman restart stopped container by ID", func() { + It("podman restart stopped container by ID", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -73,7 +75,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) }) - It("Podman restart running container", func() { + It("podman restart running container", func() { _ = podmanTest.RunTopContainer("test1") ok := WaitForContainer(podmanTest) Expect(ok).To(BeTrue()) @@ -88,7 +90,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) }) - It("Podman container restart running container", func() { + It("podman container restart running container", func() { _ = podmanTest.RunTopContainer("test1") ok := WaitForContainer(podmanTest) Expect(ok).To(BeTrue()) @@ -103,7 +105,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) }) - It("Podman restart multiple containers", func() { + It("podman restart multiple containers", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) @@ -121,7 +123,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) - It("Podman restart the latest container", func() { + It("podman restart the latest container", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) @@ -144,7 +146,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) - It("Podman restart non-stop container with short timeout", func() { + It("podman restart non-stop container with short timeout", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", "--env", "STOPSIGNAL=SIGKILL", ALPINE, "sleep", "999"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -157,7 +159,7 @@ var _ = Describe("Podman restart", func() { Expect(timeSince).To(BeNumerically(">", 2*time.Second)) }) - It("Podman restart --all", func() { + It("podman restart --all", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) @@ -177,7 +179,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) - It("Podman restart --all --running", func() { + It("podman restart --all --running", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) @@ -197,7 +199,7 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) - It("Podman restart a container in a pod and hosts should not duplicated", func() { + It("podman restart a container in a pod and hosts should not duplicated", func() { // Fixes: https://github.com/containers/podman/issues/8921 _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) @@ -226,7 +228,7 @@ var _ = Describe("Podman restart", func() { Expect(beforeRestart.OutputToString()).To(Equal(afterRestart.OutputToString())) }) - It("podman restart --all", func() { + It("podman restart all stopped containers with --all", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -247,4 +249,113 @@ var _ = Describe("Podman restart", func() { Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) + + It("podman restart --cidfile", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "cid" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToStringArray()[0] + + session = podmanTest.Podman([]string{"start", cid}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + result := podmanTest.Podman([]string{"restart", "--cidfile", tmpFile}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid)) + }) + + It("podman restart multiple --cidfile", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile1 := tmpDir + "cid-1" + tmpFile2 := tmpDir + "cid-2" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"run", "--cidfile", tmpFile1, "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid1 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session = podmanTest.Podman([]string{"run", "--cidfile", tmpFile2, "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid2 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(2)) + + result := podmanTest.Podman([]string{"restart", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + Expect(output).To(ContainSubstring(cid2)) + Expect(podmanTest.NumberOfContainers()).To(Equal(2)) + }) + + It("podman restart invalid --latest and --cidfile and --all", func() { + SkipIfRemote("--latest flag n/a") + result := podmanTest.Podman([]string{"restart", "--cidfile", "foobar", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("cannot be used together")) + result = podmanTest.Podman([]string{"restart", "--cidfile", "foobar", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("cannot be used together")) + result = podmanTest.Podman([]string{"restart", "--cidfile", "foobar", "--all", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("cannot be used together")) + result = podmanTest.Podman([]string{"restart", "--latest", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("cannot be used together")) + }) + + It("podman pause --filter", func() { + session1 := podmanTest.RunTopContainer("") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid1 := session1.OutputToString() + + session1 = podmanTest.RunTopContainer("") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid2 := session1.OutputToString() + + session1 = podmanTest.RunTopContainer("") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid3 := session1.OutputToString() + shortCid3 := cid3[0:5] + + session1 = podmanTest.Podman([]string{"restart", cid1, "-f", "status=test"}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(125)) + + session1 = podmanTest.Podman([]string{"restart", "-a", "--filter", fmt.Sprintf("id=%swrongid", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(HaveLen(0)) + + session1 = podmanTest.Podman([]string{"restart", "-a", "--filter", fmt.Sprintf("id=%s", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid3)) + + session1 = podmanTest.Podman([]string{"restart", "-f", fmt.Sprintf("id=%s", cid2)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid2)) + }) }) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 7dbe5fed8..e76451824 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -1,6 +1,7 @@ package integration import ( + "fmt" "io/ioutil" "os" @@ -51,6 +52,7 @@ var _ = Describe("Podman rm", func() { result := podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(2)) + Expect(result.ErrorToString()).To(ContainSubstring("containers cannot be removed without force")) }) It("podman rm created container", func() { @@ -140,11 +142,9 @@ var _ = Describe("Podman rm", func() { output := result.OutputToString() Expect(output).To(ContainSubstring(cid)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) - }) It("podman rm --cidfile", func() { - tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) tmpFile := tmpDir + "cid" @@ -166,7 +166,6 @@ var _ = Describe("Podman rm", func() { }) It("podman rm multiple --cidfile", func() { - tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) tmpFile1 := tmpDir + "cid-1" @@ -201,18 +200,22 @@ var _ = Describe("Podman rm", func() { result := podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--latest"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("--all, --latest, and --cidfile cannot be used together")) result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("--all, --latest, and --cidfile cannot be used together")) result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all", "--latest"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("--all, --latest, and --cidfile cannot be used together")) result = podmanTest.Podman([]string{"rm", "--latest", "--all"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("--all and --latest cannot be used together")) }) It("podman rm --all", func() { @@ -242,10 +245,17 @@ var _ = Describe("Podman rm", func() { session = podmanTest.Podman([]string{"rm", "bogus", cid}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + Expect(session.ErrorToString()).To(ContainSubstring("\"bogus\" found: no such container")) + if IsRemote() { + Expect(session.OutputToString()).To(BeEquivalentTo(cid)) + } session = podmanTest.Podman([]string{"rm", "--ignore", "bogus", cid}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + if !IsRemote() { + Expect(session.OutputToString()).To(BeEquivalentTo(cid)) + } Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) @@ -253,6 +263,7 @@ var _ = Describe("Podman rm", func() { session := podmanTest.Podman([]string{"rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + Expect(session.ErrorToString()).To(ContainSubstring("\"bogus\" found: no such container")) }) It("podman rm bogus container and a running container", func() { @@ -263,10 +274,12 @@ var _ = Describe("Podman rm", func() { session = podmanTest.Podman([]string{"rm", "bogus", "test1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + Expect(session.ErrorToString()).To(ContainSubstring("\"bogus\" found: no such container")) session = podmanTest.Podman([]string{"rm", "test1", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + Expect(session.ErrorToString()).To(ContainSubstring("\"bogus\" found: no such container")) }) It("podman rm --ignore bogus container and a running container", func() { @@ -274,12 +287,52 @@ var _ = Describe("Podman rm", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"rm", "-t", "0", "--force", "--ignore", "bogus", "test1"}) + session = podmanTest.Podman([]string{"rm", "--ignore", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) + Expect(session).Should(Exit(2)) + Expect(session.ErrorToString()).To(ContainSubstring("containers cannot be removed without force")) - session = podmanTest.Podman([]string{"rm", "--ignore", "test1", "bogus"}) + session = podmanTest.Podman([]string{"rm", "-t", "0", "--force", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(BeEquivalentTo("test1")) + }) + + It("podman rm --filter", func() { + session1 := podmanTest.RunTopContainer("test1") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid1 := session1.OutputToString() + + session1 = podmanTest.RunTopContainer("test2") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid2 := session1.OutputToString() + + session1 = podmanTest.RunTopContainer("test3") + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid3 := session1.OutputToString() + shortCid3 := cid3[0:5] + + session1 = podmanTest.Podman([]string{"rm", cid1, "-f", "--filter", "status=running"}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(125)) + Expect(session1.ErrorToString()).To(ContainSubstring("--filter takes no arguments")) + + session1 = podmanTest.Podman([]string{"rm", "-a", "-f", "--filter", fmt.Sprintf("id=%swrongid", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(HaveLen(0)) + + session1 = podmanTest.Podman([]string{"rm", "-a", "-f", "--filter", fmt.Sprintf("id=%s", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid3)) + + session1 = podmanTest.Podman([]string{"rm", "-f", "--filter", fmt.Sprintf("id=%s", cid2)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid2)) }) }) diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index d1a0cd6f5..f87f65c34 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -307,4 +307,82 @@ RUN touch %s`, CIRROS_IMAGE, imageName) } wg.Wait() }) + + It("podman rmi --no-prune with dangling parents", func() { + podmanTest.AddImageToRWStore(ALPINE) + session := podmanTest.Podman([]string{"create", "--name", "c_test1", ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test1", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--name", "c_test2", "test1", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test2", "test2"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + imageID2 := session.OutputToString() + + session = podmanTest.Podman([]string{"create", "--name", "c_test3", "test2", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test3", "test3"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + imageID3 := session.OutputToString() + + session = podmanTest.Podman([]string{"untag", "test2"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"untag", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"rmi", "-f", "--no-prune", "test3"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(imageID3)) + Expect(session.OutputToString()).NotTo(ContainSubstring(imageID2)) + }) + + It("podman rmi --no-prune with undangling parents", func() { + podmanTest.AddImageToRWStore(ALPINE) + session := podmanTest.Podman([]string{"create", "--name", "c_test1", ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test1", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--name", "c_test2", "test1", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test2", "test2"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + imageID2 := session.OutputToString() + + session = podmanTest.Podman([]string{"create", "--name", "c_test3", "test2", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"commit", "-q", "c_test3", "test3"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + imageID3 := session.OutputToString() + + session = podmanTest.Podman([]string{"rmi", "-f", "--no-prune", "test3"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(imageID3)) + Expect(session.OutputToString()).NotTo(ContainSubstring(imageID2)) + }) }) diff --git a/test/e2e/run_cleanup_test.go b/test/e2e/run_cleanup_test.go index ea2caf907..5aa81140d 100644 --- a/test/e2e/run_cleanup_test.go +++ b/test/e2e/run_cleanup_test.go @@ -36,7 +36,7 @@ var _ = Describe("Podman run exit", func() { It("podman run -d mount cleanup test", func() { SkipIfRemote("podman-remote does not support mount") - SkipIfRootless("TODO rootless podman mount requires podman unshare first") + SkipIfRootless("rootless podman mount requires podman unshare first") result := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) result.WaitWithDefaultTimeout() @@ -69,6 +69,49 @@ var _ = Describe("Podman run exit", func() { pmount.WaitWithDefaultTimeout() Expect(pmount).Should(Exit(0)) Expect(pmount.OutputToString()).NotTo(ContainSubstring(cid)) + }) + + It("podman run -d mount cleanup rootless test", func() { + SkipIfRemote("podman-remote does not support mount") + SkipIfNotRootless("Use unshare in rootless only") + + result := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) + result.WaitWithDefaultTimeout() + cid := result.OutputToString() + Expect(result).Should(Exit(0)) + + mount := podmanTest.Podman([]string{"unshare", "mount"}) + mount.WaitWithDefaultTimeout() + Expect(mount).Should(Exit(0)) + Expect(mount.OutputToString()).To(ContainSubstring(cid)) + + // command: podman <options> unshare podman <options> image mount ALPINE + args := []string{"unshare", podmanTest.PodmanBinary} + opts := podmanTest.PodmanMakeOptions([]string{"mount", "--no-trunc"}, false, false) + args = append(args, opts...) + + pmount := podmanTest.Podman(args) + pmount.WaitWithDefaultTimeout() + Expect(pmount).Should(Exit(0)) + Expect(pmount.OutputToString()).To(ContainSubstring(cid)) + stop := podmanTest.Podman([]string{"stop", cid}) + stop.WaitWithDefaultTimeout() + Expect(stop).Should(Exit(0)) + + // We have to force cleanup so the unmount happens + podmanCleanupSession := podmanTest.Podman([]string{"container", "cleanup", cid}) + podmanCleanupSession.WaitWithDefaultTimeout() + Expect(podmanCleanupSession).Should(Exit(0)) + + mount = podmanTest.Podman([]string{"unshare", "mount"}) + mount.WaitWithDefaultTimeout() + Expect(mount).Should(Exit(0)) + Expect(mount.OutputToString()).NotTo(ContainSubstring(cid)) + + pmount = podmanTest.Podman(args) + pmount.WaitWithDefaultTimeout() + Expect(pmount).Should(Exit(0)) + Expect(pmount.OutputToString()).NotTo(ContainSubstring(cid)) }) }) diff --git a/test/e2e/run_cpu_test.go b/test/e2e/run_cpu_test.go index e57eb3b26..19bb735ff 100644 --- a/test/e2e/run_cpu_test.go +++ b/test/e2e/run_cpu_test.go @@ -138,4 +138,20 @@ var _ = Describe("Podman run cpu", func() { result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) }) + + It("podman run invalid cpu-rt-period with cgroupsv2", func() { + SkipIfCgroupV1("testing options that only work in cgroup v2") + result := podmanTest.Podman([]string{"run", "--rm", "--cpu-rt-period=5000", ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.ErrorToString()).To(ContainSubstring("Realtime period not supported on cgroups V2 systems")) + }) + + It("podman run invalid cpu-rt-runtime with cgroupsv2", func() { + SkipIfCgroupV1("testing options that only work in cgroup v2") + result := podmanTest.Podman([]string{"run", "--rm", "--cpu-rt-runtime=5000", ALPINE, "ls"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.ErrorToString()).To(ContainSubstring("Realtime runtime not supported on cgroups V2 systems")) + }) }) diff --git a/test/e2e/run_env_test.go b/test/e2e/run_env_test.go index bab52efc5..2b2d67f57 100644 --- a/test/e2e/run_env_test.go +++ b/test/e2e/run_env_test.go @@ -58,6 +58,13 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/bin")) + // Verify environ keys with spaces do not blow up podman command + os.Setenv("FOO BAR", "BAZ") + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + os.Unsetenv("FOO BAR") + os.Setenv("FOO", "BAR") session = podmanTest.Podman([]string{"run", "--rm", "--env", "FOO", ALPINE, "printenv", "FOO"}) session.WaitWithDefaultTimeout() @@ -82,6 +89,17 @@ var _ = Describe("Podman run", func() { Expect(session.OutputToString()).To(ContainSubstring("HOSTNAME")) }) + It("podman run with --env-merge", func() { + dockerfile := `FROM quay.io/libpod/alpine:latest +ENV hello=world +` + podmanTest.BuildImage(dockerfile, "test", "false") + session := podmanTest.Podman([]string{"run", "--rm", "--env-merge", "hello=${hello}-earth", "test", "env"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("world-earth")) + }) + It("podman run --env-host environment test", func() { env := append(os.Environ(), "FOO=BAR") session := podmanTest.PodmanAsUser([]string{"run", "--rm", "--env-host", ALPINE, "/bin/printenv", "FOO"}, 0, 0, "", env) diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go index 083020f08..3f611040b 100644 --- a/test/e2e/run_memory_test.go +++ b/test/e2e/run_memory_test.go @@ -66,6 +66,24 @@ var _ = Describe("Podman run memory", func() { Expect(session.OutputToString()).To(Equal("41943040")) }) + It("podman run memory-swap test", func() { + var ( + session *PodmanSessionIntegration + expect string + ) + + if CGROUPSV2 { + session = podmanTest.Podman([]string{"run", "--memory=20m", "--memory-swap=30M", "--net=none", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.swap.max"}) + expect = "10485760" + } else { + session = podmanTest.Podman([]string{"run", "--memory=20m", "--memory-swap=30M", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) + expect = "31457280" + } + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(expect)) + }) + for _, limit := range []string{"0", "15", "100"} { limit := limit // Keep this value in a proper scope testName := fmt.Sprintf("podman run memory-swappiness test(%s)", limit) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 1ad78c950..78e4a62c0 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -869,7 +869,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) }) It("podman run in custom CNI network with --static-ip", func() { - netName := stringid.GenerateNonCryptoID() + netName := stringid.GenerateRandomID() ipAddr := "10.25.30.128" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName}) create.WaitWithDefaultTimeout() @@ -884,7 +884,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) It("podman CNI network works across user ns", func() { SkipIfNetavark(podmanTest) - netName := stringid.GenerateNonCryptoID() + netName := stringid.GenerateRandomID() create := podmanTest.Podman([]string{"network", "create", netName}) create.WaitWithDefaultTimeout() Expect(create).Should(Exit(0)) @@ -933,7 +933,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) }) It("podman run with new:pod and static-ip", func() { - netName := stringid.GenerateNonCryptoID() + netName := stringid.GenerateRandomID() ipAddr := "10.25.40.128" podname := "testpod" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.40.0/24", netName}) @@ -1121,7 +1121,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) // see https://github.com/containers/podman/issues/12972 It("podman run check network-alias works on networks without dns", func() { - net := "dns" + stringid.GenerateNonCryptoID() + net := "dns" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", "--disable-dns", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) @@ -1135,7 +1135,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) It("podman run with ipam none driver", func() { // Test fails, issue #13931 SkipIfNetavark(podmanTest) - net := "ipam" + stringid.GenerateNonCryptoID() + net := "ipam" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"network", "create", "--ipam-driver=none", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go index 8207f6d0b..057fbf775 100644 --- a/test/e2e/run_staticip_test.go +++ b/test/e2e/run_staticip_test.go @@ -66,7 +66,7 @@ var _ = Describe("Podman run with --ip flag", func() { }) It("Podman run with specified static IPv6 has correct IP", func() { - netName := "ipv6-" + stringid.GenerateNonCryptoID() + netName := "ipv6-" + stringid.GenerateRandomID() ipv6 := "fd46:db93:aa76:ac37::10" net := podmanTest.Podman([]string{"network", "create", "--subnet", "fd46:db93:aa76:ac37::/64", netName}) net.WaitWithDefaultTimeout() @@ -105,6 +105,13 @@ var _ = Describe("Podman run with --ip flag", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) + // We need to set "no_proxy" in proxy environment + if env, found := os.LookupEnv("no_proxy"); found { + defer os.Setenv("no_proxy", env) + } else { + defer os.Unsetenv("no_proxy") + } + os.Setenv("no_proxy", ip) for retries := 20; retries > 0; retries-- { response, err := http.Get(fmt.Sprintf("http://%s", ip)) if err == nil && response.StatusCode == http.StatusOK { diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index c7a0b3f2b..3fbdd4339 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -222,7 +222,7 @@ var _ = Describe("Podman run", func() { It("podman run a container with a --rootfs", func() { rootfs := filepath.Join(tempdir, "rootfs") uls := filepath.Join("/", "usr", "local", "share") - uniqueString := stringid.GenerateNonCryptoID() + uniqueString := stringid.GenerateRandomID() testFilePath := filepath.Join(uls, uniqueString) tarball := filepath.Join(tempdir, "rootfs.tar") @@ -848,7 +848,7 @@ USER bin`, BB) err = ioutil.WriteFile(hookJSONPath, []byte(hookJSON), 0644) Expect(err).ToNot(HaveOccurred()) - random := stringid.GenerateNonCryptoID() + random := stringid.GenerateRandomID() hookScript := fmt.Sprintf(`#!/bin/sh echo -n %s >%s @@ -945,7 +945,7 @@ echo -n %s >%s session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "id"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("uid=1234(1234) gid=0(root)")) + Expect(session.OutputToString()).To(Equal("uid=1234(1234) gid=0(root) groups=0(root)")) }) It("podman run with user (integer, in /etc/passwd)", func() { @@ -966,14 +966,14 @@ echo -n %s >%s session := podmanTest.Podman([]string{"run", "--rm", "--user=mail:21", ALPINE, "id"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp)")) + Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp) groups=21(ftp)")) }) It("podman run with user:group (integer:groupname)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=8:ftp", ALPINE, "id"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp)")) + Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp) groups=21(ftp)")) }) It("podman run with user, verify caps dropped", func() { @@ -984,6 +984,14 @@ echo -n %s >%s Expect("0000000000000000").To(Equal(capEff[1])) }) + It("podman run with user, verify group added", func() { + session := podmanTest.Podman([]string{"run", "--rm", "--user=1000:1000", ALPINE, "grep", "Groups:", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + groups := strings.Split(session.OutputToString(), " ")[1] + Expect("1000").To(Equal(groups)) + }) + It("podman run with attach stdin outputs container ID", func() { session := podmanTest.Podman([]string{"run", "--attach", "stdin", ALPINE, "printenv"}) session.WaitWithDefaultTimeout() @@ -1235,9 +1243,8 @@ USER mail`, BB) }) It("podman run --mount type=bind,bind-nonrecursive", func() { - // crun: mount `/` to `/host`: Invalid argument - SkipIfRootless("FIXME: rootless users are not allowed to mount bind-nonrecursive (Could this be a Kernel bug?") - session := podmanTest.Podman([]string{"run", "--mount", "type=bind,bind-nonrecursive,slave,src=/,target=/host", fedoraMinimal, "findmnt", "-nR", "/host"}) + SkipIfRootless("FIXME: rootless users are not allowed to mount bind-nonrecursive") + session := podmanTest.Podman([]string{"run", "--mount", "type=bind,bind-nonrecursive,private,src=/sys,target=/host-sys", fedoraMinimal, "findmnt", "-nR", "/host-sys"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).To(HaveLen(1)) diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index 613727118..016f67bf6 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -8,6 +8,7 @@ import ( "strings" . "github.com/containers/podman/v4/test/utils" + "github.com/containers/storage" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -42,6 +43,33 @@ var _ = Describe("Podman UserNS support", func() { }) + // Note: Lot of tests for build with --userns=auto are already there in buildah + // but they are skipped in podman CI because bud tests are executed in rootfull + // environment ( where mappings for the `containers` user is not present in /etc/subuid ) + // causing them to skip hence this is a redundant test for sanity to make sure + // we don't break this feature for podman-remote. + It("podman build with --userns=auto", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + name := u.Name + if name == "root" { + name = "containers" + } + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.userns-auto", "-t", "test", "--userns=auto"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + // `1024` is the default size or length of the range of user IDs + // that is mapped between the two user namespaces by --userns=auto. + Expect(session.OutputToString()).To(ContainSubstring(fmt.Sprintf("%d", storage.AutoUserNsMinSize))) + }) + It("podman uidmapping and gidmapping", func() { session := podmanTest.Podman([]string{"run", "--uidmap=0:100:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"}) session.WaitWithDefaultTimeout() @@ -85,6 +113,16 @@ var _ = Describe("Podman UserNS support", func() { Expect(session).Should(Exit(0)) uid := fmt.Sprintf("%d", os.Geteuid()) Expect(session.OutputToString()).To(ContainSubstring(uid)) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id:uid=10,gid=12", "alpine", "sh", "-c", "echo $(id -u):$(id -g)"}) + session.WaitWithDefaultTimeout() + if os.Geteuid() == 0 { + Expect(session).Should(Exit(125)) + return + } + + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("10:12")) }) It("podman --userns=keep-id check passwd", func() { @@ -157,6 +195,8 @@ var _ = Describe("Podman UserNS support", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) l := session.OutputToString() + // `1024` is the default size or length of the range of user IDs + // that is mapped between the two user namespaces by --userns=auto. Expect(l).To(ContainSubstring("1024")) m[l] = l } @@ -307,6 +347,30 @@ var _ = Describe("Podman UserNS support", func() { } }) + + It("podman --userns= conflicts with ui[dg]map and sub[ug]idname", func() { + session := podmanTest.Podman([]string{"run", "--userns=host", "--uidmap=0:1:500", "alpine", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive")) + + session = podmanTest.Podman([]string{"run", "--userns=host", "--gidmap=0:200:5000", "alpine", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.ErrorToString()).To(ContainSubstring("--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive")) + + // with sub[ug]idname we don't check for the error output since the error message could be different, depending on the + // system configuration since the specified user could not be defined and cause a different earlier error. + // In any case, make sure the command doesn't succeed. + session = podmanTest.Podman([]string{"run", "--userns=private", "--subuidname=containers", "alpine", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Not(Exit(0))) + + session = podmanTest.Podman([]string{"run", "--userns=private", "--subgidname=containers", "alpine", "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Not(Exit(0))) + }) + It("podman PODMAN_USERNS", func() { SkipIfNotRootless("keep-id only works in rootless mode") diff --git a/test/e2e/run_working_dir_test.go b/test/e2e/run_working_dir_test.go index ff91a420f..84792481f 100644 --- a/test/e2e/run_working_dir_test.go +++ b/test/e2e/run_working_dir_test.go @@ -46,6 +46,15 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(126)) }) + It("podman run a container using a --workdir under a bind mount", func() { + volume, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"run", "--volume", fmt.Sprintf("%s:/var_ovl/:O", volume), "--workdir", "/var_ovl/log", ALPINE, "true"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman run a container on an image with a workdir", func() { dockerfile := fmt.Sprintf(`FROM %s RUN mkdir -p /home/foobar /etc/foobar; chown bin:bin /etc/foobar diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go index 94c363dd4..afb723a63 100644 --- a/test/e2e/save_test.go +++ b/test/e2e/save_test.go @@ -153,6 +153,9 @@ var _ = Describe("Podman save", func() { defer os.Setenv("GNUPGHOME", origGNUPGHOME) port := 5000 + portlock := GetPortLock(strconv.Itoa(port)) + defer portlock.Unlock() + session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), REGISTRY_IMAGE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) diff --git a/test/e2e/secret_test.go b/test/e2e/secret_test.go index ed328d84a..902f422bd 100644 --- a/test/e2e/secret_test.go +++ b/test/e2e/secret_test.go @@ -40,7 +40,7 @@ var _ = Describe("Podman secret", func() { err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) Expect(err).To(BeNil()) - session := podmanTest.Podman([]string{"secret", "create", "--driver-opts", "opt1=val", "a", secretFilePath}) + session := podmanTest.Podman([]string{"secret", "create", "-d", "file", "--driver-opts", "opt1=val", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() Expect(session).Should(Exit(0)) @@ -49,7 +49,7 @@ var _ = Describe("Podman secret", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(secrID)) - inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Driver.Options}}", secrID}) + inspect = podmanTest.Podman([]string{"secret", "inspect", "-f", "{{.Spec.Driver.Options}}", secrID}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring("opt1:val")) @@ -145,6 +145,36 @@ var _ = Describe("Podman secret", func() { }) + It("podman secret ls --quiet", func() { + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) + Expect(err).To(BeNil()) + + secretName := "a" + + session := podmanTest.Podman([]string{"secret", "create", secretName, secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + secretID := session.OutputToString() + + list := podmanTest.Podman([]string{"secret", "ls", "-q"}) + list.WaitWithDefaultTimeout() + Expect(list).Should(Exit(0)) + Expect(list.OutputToString()).To(Equal(secretID)) + + list = podmanTest.Podman([]string{"secret", "ls", "--quiet"}) + list.WaitWithDefaultTimeout() + Expect(list).Should(Exit(0)) + Expect(list.OutputToString()).To(Equal(secretID)) + + // Prefer format over quiet + list = podmanTest.Podman([]string{"secret", "ls", "-q", "--format", "{{.Name}}"}) + list.WaitWithDefaultTimeout() + Expect(list).Should(Exit(0)) + Expect(list.OutputToString()).To(Equal(secretName)) + + }) + It("podman secret ls with filters", func() { secretFilePath := filepath.Join(podmanTest.TempDir, "secret") err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) @@ -170,27 +200,33 @@ var _ = Describe("Podman secret", func() { list := podmanTest.Podman([]string{"secret", "ls", "--filter", fmt.Sprintf("name=%s", secret1)}) list.WaitWithDefaultTimeout() Expect(list).Should(Exit(0)) - Expect(list.OutputToStringArray()).To(HaveLen(2), ContainSubstring(secret1)) + Expect(list.OutputToStringArray()).To(HaveLen(2)) + Expect(list.OutputToStringArray()[1]).To(ContainSubstring(secret1)) list = podmanTest.Podman([]string{"secret", "ls", "--filter", fmt.Sprintf("name=%s", secret2)}) list.WaitWithDefaultTimeout() Expect(list).Should(Exit(0)) - Expect(list.OutputToStringArray()).To(HaveLen(2), ContainSubstring(secret2)) + Expect(list.OutputToStringArray()).To(HaveLen(2)) + Expect(list.OutputToStringArray()[1]).To(ContainSubstring(secret2)) list = podmanTest.Podman([]string{"secret", "ls", "--filter", fmt.Sprintf("id=%s", secrID1)}) list.WaitWithDefaultTimeout() Expect(list).Should(Exit(0)) - Expect(list.OutputToStringArray()).To(HaveLen(2), ContainSubstring(secrID1)) + Expect(list.OutputToStringArray()).To(HaveLen(2)) + Expect(list.OutputToStringArray()[1]).To(ContainSubstring(secrID1)) list = podmanTest.Podman([]string{"secret", "ls", "--filter", fmt.Sprintf("id=%s", secrID2)}) list.WaitWithDefaultTimeout() Expect(list).Should(Exit(0)) - Expect(list.OutputToStringArray()).To(HaveLen(2), ContainSubstring(secrID2)) + Expect(list.OutputToStringArray()).To(HaveLen(2)) + Expect(list.OutputToStringArray()[1]).To(ContainSubstring(secrID2)) list = podmanTest.Podman([]string{"secret", "ls", "--filter", fmt.Sprintf("name=%s,name=%s", secret1, secret2)}) list.WaitWithDefaultTimeout() Expect(list).Should(Exit(0)) - Expect(list.OutputToStringArray()).To(HaveLen(3), ContainSubstring(secret1), ContainSubstring(secret2)) + Expect(list.OutputToStringArray()).To(HaveLen(3)) + Expect(list.OutputToString()).To(ContainSubstring(secret1)) + Expect(list.OutputToString()).To(ContainSubstring(secret2)) }) It("podman secret ls with Go template", func() { diff --git a/test/e2e/sign/key.gpg b/test/e2e/sign/key.gpg Binary files differindex 32968fc04..725bdfb7d 100644 --- a/test/e2e/sign/key.gpg +++ b/test/e2e/sign/key.gpg diff --git a/test/e2e/sign/policy.json b/test/e2e/sign/policy.json index ab01137bf..812c14989 100644 --- a/test/e2e/sign/policy.json +++ b/test/e2e/sign/policy.json @@ -12,6 +12,12 @@ "keyType": "GPGKeys", "keyPath": "/tmp/key.gpg" } + ], + "localhost:5000/sigstore-signed": [ + { + "type": "sigstoreSigned", + "keyPath": "testdata/sigstore-key.pub" + } ] } } diff --git a/test/e2e/sign/secret-key.asc b/test/e2e/sign/secret-key.asc Binary files differindex 23c0d05c3..f018a3ce5 100644 --- a/test/e2e/sign/secret-key.asc +++ b/test/e2e/sign/secret-key.asc diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 73af9d12c..f3e8cc015 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -1,6 +1,7 @@ package integration import ( + "fmt" "io/ioutil" "os" "strconv" @@ -99,23 +100,6 @@ var _ = Describe("Podman start", func() { Expect(session.OutputToString()).To(Equal(shortID)) }) - It("podman container start single container by short id", func() { - session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - cid := session.OutputToString() - shortID := cid[0:10] - session = podmanTest.Podman([]string{"container", "start", shortID}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal(shortID)) - - session = podmanTest.Podman([]string{"stop", shortID}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal(shortID)) - }) - It("podman start single container by name", func() { name := "foobar99" session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) @@ -124,9 +108,6 @@ var _ = Describe("Podman start", func() { session = podmanTest.Podman([]string{"start", name}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - if podmanTest.RemoteTest { - Skip("Container-start name check doesn't work on remote client. It always returns the full ID.") - } Expect(session.OutputToString()).To(Equal(name)) }) @@ -231,4 +212,42 @@ var _ = Describe("Podman start", func() { _, err = strconv.Atoi(containerPID) // Make sure it's a proper integer Expect(err).To(BeNil()) }) + + It("podman start container --filter", func() { + session1 := podmanTest.Podman([]string{"container", "create", ALPINE}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid1 := session1.OutputToString() + + session1 = podmanTest.Podman([]string{"container", "create", ALPINE}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid2 := session1.OutputToString() + + session1 = podmanTest.Podman([]string{"container", "create", ALPINE}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + cid3 := session1.OutputToString() + shortCid3 := cid3[0:5] + + session1 = podmanTest.Podman([]string{"start", cid1, "-f", "status=running"}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(HaveLen(0)) + + session1 = podmanTest.Podman([]string{"start", "--all", "--filter", fmt.Sprintf("id=%swrongid", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(HaveLen(0)) + + session1 = podmanTest.Podman([]string{"start", "--all", "--filter", fmt.Sprintf("id=%s", shortCid3)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid3)) + + session1 = podmanTest.Podman([]string{"start", "-f", fmt.Sprintf("id=%s", cid2)}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + Expect(session1.OutputToString()).To(BeEquivalentTo(cid2)) + }) }) diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index 3000a819f..981c00316 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -79,9 +79,10 @@ var _ = Describe("Podman stats", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "\"{{.ID}}\""}) + session = podmanTest.Podman([]string{"stats", "--all", "--no-trunc", "--no-stream", "--format", "\"{{.ID}}\""}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray()[0])).Should(BeEquivalentTo(66)) }) It("podman stats with GO template", func() { diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 7a258466a..23abb6d92 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -69,6 +69,19 @@ var _ = Describe("Podman stop", func() { Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) + It("podman stop single container by short id", func() { + session := podmanTest.RunTopContainer("test1") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + shortID := cid[0:10] + + session = podmanTest.Podman([]string{"stop", shortID}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(shortID)) + }) + It("podman stop container by name", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() @@ -198,9 +211,13 @@ var _ = Describe("Podman stop", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + cid := session.OutputToString() + session = podmanTest.Podman([]string{"stop", "-l", "-t", "1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(cid)) + finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() Expect(finalCtrs).Should(Exit(0)) diff --git a/test/e2e/testdata/sigstore-key.key b/test/e2e/testdata/sigstore-key.key new file mode 100644 index 000000000..c4eed76a8 --- /dev/null +++ b/test/e2e/testdata/sigstore-key.key @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 +OCwicCI6MX0sInNhbHQiOiI2ckxVcEl1M1pTallrY3dua1pNVktuTHNDUjRENTJv +Y3J5Wmh2anZ4L1VrPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJMTVpkeTNBL285NS9SektUZGR3RURhODJTVThVcDdlKyJ9LCJj +aXBoZXJ0ZXh0IjoiNkkzUlRCc1IwRXpHZWs0SE5LazlVdlpyMEp6Y1Bxemw0ZkEr +SitJdHlCc0RBSkcyNmhESnFLUDFuQkJTUE5XdHpJRzJUVzQ5Z2hObEJmQy9qYVNk +eFo2QmhXYk9ldlY0MDB4WjVNZ1oyVHdGSnJxaE9HK0JMdmNvanVkc2tOUFpJTlpE +LytFZVBIYTRlRVJPTWhnSWlTRC9BYTd3eitlc2trVjkrN216Y3N2RVRiTTJTZGd6 +L3daMUtqV3FlOUc2MWlXSTJPSm1rRlhxQWc9PSJ9 +-----END ENCRYPTED COSIGN PRIVATE KEY----- diff --git a/test/e2e/testdata/sigstore-key.key.pass b/test/e2e/testdata/sigstore-key.key.pass new file mode 100644 index 000000000..beb5c7687 --- /dev/null +++ b/test/e2e/testdata/sigstore-key.key.pass @@ -0,0 +1 @@ +sigstore pass diff --git a/test/e2e/testdata/sigstore-key.pub b/test/e2e/testdata/sigstore-key.pub new file mode 100644 index 000000000..1f470f72b --- /dev/null +++ b/test/e2e/testdata/sigstore-key.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEX/AWbBiFPuAU5+ys+Ce8YFPhTr1a +nM7A8h6NrQi6w8w8/4dJCzlGH4SN+P93nopATs6jDXs4Lpc2/tiA1SBmzA== +-----END PUBLIC KEY----- diff --git a/test/e2e/testdata/sigstore-registries.d-fragment.yaml b/test/e2e/testdata/sigstore-registries.d-fragment.yaml new file mode 100644 index 000000000..d79f4c935 --- /dev/null +++ b/test/e2e/testdata/sigstore-registries.d-fragment.yaml @@ -0,0 +1,3 @@ +docker: + localhost:5000/sigstore-signed: + use-sigstore-attachments: true diff --git a/test/e2e/top_test.go b/test/e2e/top_test.go index 66bb887dc..5f51742d1 100644 --- a/test/e2e/top_test.go +++ b/test/e2e/top_test.go @@ -133,4 +133,15 @@ var _ = Describe("Podman top", func() { Expect(result).Should(Exit(125)) }) + It("podman top on privileged container", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"top", cid, "capeff"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToStringArray()).To(Equal([]string{"EFFECTIVE CAPS", "full"})) + }) }) diff --git a/test/e2e/update_test.go b/test/e2e/update_test.go new file mode 100644 index 000000000..97dadd04c --- /dev/null +++ b/test/e2e/update_test.go @@ -0,0 +1,200 @@ +package integration + +import ( + "github.com/containers/common/pkg/cgroupv2" + . "github.com/containers/podman/v4/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman update", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + Expect(err).To(BeNil()) + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman update container all options v1", func() { + SkipIfCgroupV2("testing flags that only work in cgroup v1") + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + commonArgs := []string{ + "update", + "--cpus", "5", + "--cpuset-cpus", "0", + "--cpu-shares", "123", + "--cpuset-mems", "0", + "--memory", "1G", + "--memory-swap", "2G", + "--memory-reservation", "2G", + "--memory-swappiness", "50", ctrID} + + session = podmanTest.Podman(commonArgs) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // checking cpu quota from --cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + + // checking cpuset-cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking cpuset-mems + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking memory limit + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking memory-swap + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("2147483648")) + + // checking cpu-shares + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.shares"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("123")) + + }) + + It("podman update container all options v2", func() { + SkipIfCgroupV1("testing flags that only work in cgroup v2") + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + commonArgs := []string{ + "update", + "--cpus", "5", + "--cpuset-cpus", "0", + "--cpu-shares", "123", + "--cpuset-mems", "0", + "--memory", "1G", + "--memory-swap", "2G", + "--memory-reservation", "2G", + "--blkio-weight", "123", + "--device-read-bps", "/dev/zero:10mb", + "--device-write-bps", "/dev/zero:10mb", + "--device-read-iops", "/dev/zero:1000", + "--device-write-iops", "/dev/zero:1000", + ctrID} + + session = podmanTest.Podman(commonArgs) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID = session.OutputToString() + + // checking cpu quota and period + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + + // checking blkio weight + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.bfq.weight"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("123")) + + // checking device-read/write-bps/iops + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/io.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("rbps=10485760 wbps=10485760 riops=1000 wiops=1000")) + + // checking cpuset-cpus + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.cpus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking cpuset-mems + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpuset.mems"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("0")) + + // checking memory limit + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking memory-swap + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/memory.swap.max"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("1073741824")) + + // checking cpu-shares + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.weight"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("5")) + }) + + It("podman update keep original resources if not overridden", func() { + SkipIfRootless("many of these handlers are not enabled while rootless in CI") + session := podmanTest.Podman([]string{"run", "-dt", "--cpus", "5", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "update", + "--memory", "1G", + session.OutputToString(), + }) + + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrID := session.OutputToString() + + if v2, _ := cgroupv2.Enabled(); v2 { + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu.max"}) + } else { + session = podmanTest.Podman([]string{"exec", "-it", ctrID, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) + } + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(ContainSubstring("500000")) + }) +}) diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go index 7a975f6a5..499283cab 100644 --- a/test/e2e/volume_create_test.go +++ b/test/e2e/volume_create_test.go @@ -162,19 +162,4 @@ var _ = Describe("Podman volume create", func() { Expect(inspectOpts).Should(Exit(0)) Expect(inspectOpts.OutputToString()).To(Equal(optionStrFormatExpect)) }) - - It("podman create volume with o=timeout", func() { - volName := "testVol" - timeout := 10 - timeoutStr := "10" - session := podmanTest.Podman([]string{"volume", "create", "--opt", fmt.Sprintf("o=timeout=%d", timeout), volName}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - - inspectTimeout := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Timeout }}", volName}) - inspectTimeout.WaitWithDefaultTimeout() - Expect(inspectTimeout).Should(Exit(0)) - Expect(inspectTimeout.OutputToString()).To(Equal(timeoutStr)) - - }) }) diff --git a/test/e2e/volume_exists_test.go b/test/e2e/volume_exists_test.go index 0de574968..0a550bfcb 100644 --- a/test/e2e/volume_exists_test.go +++ b/test/e2e/volume_exists_test.go @@ -34,7 +34,7 @@ var _ = Describe("Podman volume exists", func() { }) It("podman volume exists", func() { - vol := "vol" + stringid.GenerateNonCryptoID() + vol := "vol" + stringid.GenerateRandomID() session := podmanTest.Podman([]string{"volume", "create", vol}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -43,7 +43,7 @@ var _ = Describe("Podman volume exists", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"volume", "exists", stringid.GenerateNonCryptoID()}) + session = podmanTest.Podman([]string{"volume", "exists", stringid.GenerateRandomID()}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) }) diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go index dcfb13f4e..24ea50b26 100644 --- a/test/e2e/volume_ls_test.go +++ b/test/e2e/volume_ls_test.go @@ -75,7 +75,10 @@ var _ = Describe("Podman volume ls", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.OutputToStringArray()).To(HaveLen(1), session.OutputToString()) + arr := session.OutputToStringArray() + Expect(arr).To(HaveLen(2)) + Expect(arr[0]).To(ContainSubstring("NAME")) + Expect(arr[1]).To(ContainSubstring("myvol")) }) It("podman ls volume with --filter flag", func() { diff --git a/test/e2e/volume_plugin_test.go b/test/e2e/volume_plugin_test.go index b585f8dd8..33cdcce5b 100644 --- a/test/e2e/volume_plugin_test.go +++ b/test/e2e/volume_plugin_test.go @@ -212,19 +212,19 @@ testvol5 = "/run/docker/plugins/testvol5.sock"`), 0o644) plugin.WaitWithDefaultTimeout() Expect(plugin).Should(Exit(0)) - localvol := "local-" + stringid.GenerateNonCryptoID() + localvol := "local-" + stringid.GenerateRandomID() // create local volume session := podmanTest.Podman([]string{"volume", "create", localvol}) session.WaitWithDefaultTimeout() Expect(session).To(Exit(0)) - vol1 := "vol1-" + stringid.GenerateNonCryptoID() + vol1 := "vol1-" + stringid.GenerateRandomID() session = podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, vol1}) session.WaitWithDefaultTimeout() Expect(session).To(Exit(0)) // now create volume in plugin without podman - vol2 := "vol2-" + stringid.GenerateNonCryptoID() + vol2 := "vol2-" + stringid.GenerateRandomID() plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "create", vol2}) plugin.WaitWithDefaultTimeout() Expect(plugin).Should(Exit(0)) @@ -256,4 +256,38 @@ Removed: Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol2)) Expect(session.ErrorToString()).To(Equal("")) // make no errors are shown }) + + It("volume driver timeouts test", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + err := os.Mkdir(pluginStatePath, 0755) + Expect(err).ToNot(HaveOccurred()) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol6" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin).Should(Exit(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Timeout }}", volName}) + volInspect.WaitWithDefaultTimeout() + Expect(volInspect).Should(Exit(0)) + Expect(volInspect.OutputToString()).To(ContainSubstring("15")) + + volName2 := "testVolume2" + create2 := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, "--opt", "o=timeout=3", volName2}) + create2.WaitWithDefaultTimeout() + Expect(create2).Should(Exit(0)) + + volInspect2 := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Timeout }}", volName2}) + volInspect2.WaitWithDefaultTimeout() + Expect(volInspect2).Should(Exit(0)) + Expect(volInspect2.OutputToString()).To(ContainSubstring("3")) + }) }) diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats index cf37fc07c..378edc013 100644 --- a/test/system/001-basic.bats +++ b/test/system/001-basic.bats @@ -29,6 +29,12 @@ function setup() { local built=$(expr "$output" : ".*Built: \+\(.*\)" | head -n1) local built_t=$(date --date="$built" +%s) assert "$built_t" -gt 1546300800 "Preposterous 'Built' time in podman version" + + run_podman -v + is "$output" "podman.*version \+" "'Version line' in output" + + run_podman --config foobar version + is "$output" ".*The --config flag is ignored by Podman. Exists for Docker compatibility\+" "verify warning for --config option" } @test "podman info" { @@ -182,14 +188,79 @@ See 'podman version --help'" "podman version --remote" @test "podman --log-level recognizes log levels" { run_podman 1 --log-level=telepathic info is "$output" 'Log Level "telepathic" is not supported.*' + run_podman --log-level=trace info + if ! is_remote; then + # podman-remote does not do any trace logging + assert "$output" =~ " level=trace " "log-level=trace" + fi + assert "$output" =~ " level=debug " "log-level=trace includes debug" + assert "$output" =~ " level=info " "log-level=trace includes info" + assert "$output" !~ " level=warn" "log-level=trace does not show warn" + run_podman --log-level=debug info + assert "$output" !~ " level=trace " "log-level=debug does not show trace" + assert "$output" =~ " level=debug " "log-level=debug" + assert "$output" =~ " level=info " "log-level=debug includes info" + assert "$output" !~ " level=warn" "log-level=debug does not show warn" + run_podman --log-level=info info + assert "$output" !~ " level=trace " "log-level=info does not show trace" + assert "$output" !~ " level=debug " "log-level=info does not show debug" + assert "$output" =~ " level=info " "log-level=info" + run_podman --log-level=warn info + assert "$output" !~ " level=" "log-level=warn shows no logs at all" + + # Force a warning (local podman only; podman-remote doesn't check versions) + if ! is_remote; then + run_podman --log-level=warn --storage-opt=mount_program=/bin/false info + assert "$output" =~ " level=warning msg=\"Failed to retrieve " \ + "log-level=warn" + + # confirm that default level is "warn", by invoking without --log-level + run_podman --storage-opt=mount_program=/bin/false info + assert "$output" =~ " level=warning msg=\"Failed to retrieve " \ + "default log level includes warning messages" + fi + run_podman --log-level=warning info + assert "$output" !~ " level=" "log-level=warning shows no logs at all" + run_podman --log-level=error info - run_podman --log-level=fatal info - run_podman --log-level=panic info + assert "$output" !~ " level=" "log-level=error shows no logs at all" + + # error, fatal, panic: + if is_remote; then + # podman-remote does not grok --runtime; all we can do is test parsing + for level in error fatal panic; do + run_podman --log-level=$level info + assert "$output" !~ " level=" \ + "log-level=$level shows no logs at all" + done + else + # local podman only + run_podman --log-level=error --storage-opt=mount_program=/bin/false --runtime=/bin/false info + assert "$output" =~ " level=error msg=\"Getting info on OCI runtime " \ + "log-level=error shows " + assert "$output" !~ " level=warn" \ + "log-level=error does not show warnings" + + run_podman --log-level=fatal --storage-opt=mount_program=/bin/false --runtime=/bin/false info + assert "$output" !~ " level=" "log-level=fatal shows no logs at all" + + run_podman --log-level=panic --storage-opt=mount_program=/bin/false --runtime=/bin/false info + assert "$output" !~ " level=" "log-level=panic shows no logs at all" + fi + + # docker compat + run_podman --debug info + assert "$output" =~ " level=debug " "podman --debug gives debug output" + run_podman -D info + assert "$output" =~ " level=debug " "podman -D gives debug output" + + run_podman 1 --debug --log-level=panic info + is "$output" "Setting --log-level and --debug is not allowed" } # vim: filetype=sh diff --git a/test/system/010-images.bats b/test/system/010-images.bats index aa390f236..16ee681a3 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -259,8 +259,8 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z run_podman 2 rmi -a is "$output" "Error: 2 errors occurred: -.** image used by .*: image is in use by a container -.** image used by .*: image is in use by a container" +.** image used by .*: image is in use by a container: consider listing external containers and force-removing image +.** image used by .*: image is in use by a container: consider listing external containers and force-removing image" run_podman rmi -af is "$output" "Untagged: $IMAGE @@ -292,7 +292,7 @@ Deleted: $pauseID" "infra images gets removed as well" pauseID=$output run_podman 2 rmi $pauseImage - is "$output" "Error: image used by .* image is in use by a container" + is "$output" "Error: image used by .* image is in use by a container: consider listing external containers and force-removing image" run_podman rmi -f $pauseImage is "$output" "Untagged: $pauseImage diff --git a/test/system/015-help.bats b/test/system/015-help.bats index dd5a7ed44..927645f29 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -121,7 +121,7 @@ function check_help() { # Exceptions: these commands don't work rootless if is_rootless; then # "pause is not supported for rootless containers" - if [ "$cmd" = "pause" -o "$cmd" = "unpause" ]; then + if [[ "$cmd" = "pause" ]] || [[ "$cmd" = "unpause" ]]; then continue fi # "network rm" too @@ -162,17 +162,17 @@ function check_help() { # Any command that takes subcommands, prints its help and errors if called # without one. - dprint "podman $@" + dprint "podman $*" run_podman '?' "$@" is "$status" 125 "'podman $*' without any subcommand - exit status" - is "$output" ".*Usage:.*Error: missing command '.*$@ COMMAND'" \ + is "$output" ".*Usage:.*Error: missing command '.*$* COMMAND'" \ "'podman $*' without any subcommand - expected error message" # Assume that 'NoSuchCommand' is not a command - dprint "podman $@ NoSuchCommand" + dprint "podman $* NoSuchCommand" run_podman '?' "$@" NoSuchCommand is "$status" 125 "'podman $* NoSuchCommand' - exit status" - is "$output" "Error: unrecognized command .*$@ NoSuchCommand" \ + is "$output" "Error: unrecognized command .*$* NoSuchCommand" \ "'podman $* NoSuchCommand' - expected error message" # This can happen if the output of --help changes, such as between diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 908c169ee..b1ce91d14 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -56,7 +56,12 @@ echo $rand | 0 | $rand @test "podman run --memory=0 runtime option" { run_podman run --memory=0 --rm $IMAGE echo hello - is "$output" "hello" "failed to run when --memory is set to 0" + if is_rootless && ! is_cgroupsv2; then + is "${lines[0]}" "Resource limits are not supported and ignored on cgroups V1 rootless systems" "--memory is not supported" + is "${lines[1]}" "hello" "--memory is ignored" + else + is "$output" "hello" "failed to run when --memory is set to 0" + fi } # 'run --preserve-fds' passes a number of additional file descriptors into the container @@ -418,7 +423,7 @@ json-file | f # Invalid log-driver argument run_podman 125 run --log-driver=InvalidDriver $IMAGE true - is "$output" "Error: error running container create option: invalid log driver: invalid argument" \ + is "$output" "Error: running container create option: invalid log driver: invalid argument" \ "--log-driver InvalidDriver" } diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 6b8d5fbc5..6e84e10fc 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -36,13 +36,28 @@ function _log_test_tail() { run_podman run -d --log-driver=$driver $IMAGE sh -c "echo test1; echo test2" cid="$output" - run_podman logs --tail 1 $cid - is "$output" "test2" "logs should only show last line" + run_podman wait $cid + run_podman logs --tail 1 --timestamps $cid + log1="$output" + assert "$log1" =~ "^[0-9-]+T[0-9:.]+([\+-][0-9:]+|Z) test2" \ + "logs should only show last line" + + # Sigh. I hate doing this, but podman-remote --timestamp only has 1-second + # resolution (regular podman has sub-second). For the timestamps-differ + # check below, we need to force a different second. + if is_remote; then + sleep 2 + fi run_podman restart $cid + run_podman wait $cid + + run_podman logs -t --tail 1 $cid + log2="$output" + assert "$log2" =~ "^[0-9-]+T[0-9:.]+([\+-][0-9:]+|Z) test2" \ + "logs, after restart, shows only last line" - run_podman logs --tail 1 $cid - is "$output" "test2" "logs should only show last line after restart" + assert "$log2" != "$log1" "log timestamps should differ" run_podman rm $cid } diff --git a/test/system/045-start.bats b/test/system/045-start.bats index ad8483bba..773a0acd2 100644 --- a/test/system/045-start.bats +++ b/test/system/045-start.bats @@ -40,6 +40,8 @@ load helpers @test "podman start --filter - start only containers that match the filter" { run_podman run -d $IMAGE /bin/true cid="$output" + run_podman wait $cid + run_podman start --filter restart-policy=always $cid is "$output" "" "CID of restart-policy=always container" @@ -66,4 +68,20 @@ load helpers is "$output" "$cid_exited_0" } +@test "podman start print IDs or raw input" { + # start --all must print the IDs + run_podman create $IMAGE top + ctrID="$output" + run_podman start --all + is "$output" "$ctrID" + + # start $input must print $input + cname=$(random_string) + run_podman create --name $cname $IMAGE top + run_podman start $cname + is "$output" $cname + + run_podman rm -t 0 -f $ctrID $cname +} + # vim: filetype=sh diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index 39002512b..a21a036c2 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -59,6 +59,22 @@ load helpers is "${lines[3]}" "c4--Created.*" "ps -a, created container (unaffected)" } +@test "podman stop print IDs or raw input" { + # stop -a must print the IDs + run_podman run -d $IMAGE top + ctrID="$output" + run_podman stop --all + is "$output" "$ctrID" + + # stop $input must print $input + cname=$(random_string) + run_podman run -d --name $cname $IMAGE top + run_podman stop $cname + is "$output" $cname + + run_podman rm -t 0 -f $ctrID $cname +} + # #9051 : podman stop --ignore was not working with podman-remote @test "podman stop --ignore" { name=thiscontainerdoesnotexist diff --git a/test/system/055-rm.bats b/test/system/055-rm.bats index dcd679a1f..613c60c96 100644 --- a/test/system/055-rm.bats +++ b/test/system/055-rm.bats @@ -18,6 +18,7 @@ load helpers # Remove container; now 'inspect' should fail run_podman rm $rand + is "$output" "$rand" "display raw input" run_podman 125 inspect $rand } diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 8f5abd228..c8ad8468c 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -436,7 +436,7 @@ load helpers run_podman cp cpcontainer:$src $destdir$dest is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description" is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description" - rm -rf $destdir/* + rm -rf ${destdir:?}/* done < <(parse_table "$tests") run_podman kill cpcontainer run_podman rm -t 0 -f cpcontainer @@ -456,7 +456,7 @@ load helpers run_podman cp cpcontainer:$src $destdir$dest is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description" is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description" - rm -rf $destdir/* + rm -rf ${destdir:?}/* done < <(parse_table "$tests") touch $destdir/testfile diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 9fddbaa21..87979483e 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -541,7 +541,7 @@ Labels.$label_name | $label_value this-file-does-not-match-anything-in-ignore-file comment ) - for f in ${files[@]}; do + for f in "${files[@]}"; do # The magic '##-' strips off the '-' prefix echo "$f" > $tmpdir/${f##-} done diff --git a/test/system/090-events.bats b/test/system/090-events.bats index ceb53ae73..90b9b3b9c 100644 --- a/test/system/090-events.bats +++ b/test/system/090-events.bats @@ -22,24 +22,25 @@ load helpers # Now filter just by container name, no label run_podman events --filter type=container --filter container=$cname --filter event=start --stream=false - is "$output" "$expect" "filtering just by label" + is "$output" "$expect" "filtering just by container" } @test "truncate events" { cname=test-$(random_string 30 | tr A-Z a-z) - labelname=$(random_string 10) - labelvalue=$(random_string 15) run_podman run -d --name=$cname --rm $IMAGE echo hi id="$output" - expect="$id" run_podman events --filter container=$cname --filter event=start --stream=false is "$output" ".* $id " "filtering by container name full id" - truncID=$(expr substr "$id" 1 12) + truncID=${id:0:12} run_podman events --filter container=$cname --filter event=start --stream=false --no-trunc=false is "$output" ".* $truncID " "filtering by container name trunc id" + + # --no-trunc does not affect --format; we always get the full ID + run_podman events --filter container=$cname --filter event=died --stream=false --format='{{.ID}}--{{.Image}}' --no-trunc=false + assert "$output" = "${id}--${IMAGE}" } @test "image events" { @@ -64,7 +65,8 @@ load helpers run_podman --events-backend=file tag $IMAGE $tag run_podman --events-backend=file untag $IMAGE $tag run_podman --events-backend=file tag $IMAGE $tag - run_podman --events-backend=file rmi $tag + run_podman --events-backend=file rmi -f $imageID + run_podman --events-backend=file load -i $tarball run_podman --events-backend=file events --stream=false --filter type=image --since $t0 is "$output" ".*image push $imageID dir:$pushedDir @@ -74,8 +76,29 @@ load helpers .*image tag $imageID $tag .*image untag $imageID $tag:latest .*image tag $imageID $tag -.*image remove $imageID $tag.*" \ +.*image untag $imageID $IMAGE +.*image untag $imageID $tag:latest +.*image remove $imageID $imageID" \ "podman events" + + # With --format we can check the _exact_ output, not just substrings + local -a expect=("push--dir:$pushedDir" + "save--$tarball" + "loadfromarchive--$tarball" + "pull--docker-archive:$tarball" + "tag--$tag" + "untag--$tag:latest" + "tag--$tag" + "untag--$IMAGE" + "untag--$tag:latest" + "remove--$imageID" + "loadfromarchive--$tarball" + ) + run_podman --events-backend=file events --stream=false --filter type=image --since $t0 --format '{{.Status}}--{{.Name}}' + for i in $(seq 0 ${#expect[@]}); do + assert "${lines[$i]}" = "${expect[$i]}" "events, line $i" + done + assert "${#lines[@]}" = "${#expect[@]}" "Total lines of output" } function _events_disjunctive_filters() { @@ -109,7 +132,8 @@ function _events_disjunctive_filters() { is "$output" "hi" "Should support events-backend=file" run_podman 125 --events-backend=file logs --follow test - is "$output" "Error: using --follow with the journald --log-driver but without the journald --events-backend (file) is not supported" "Should fail with reasonable error message when events-backend and events-logger do not match" + is "$output" "Error: using --follow with the journald --log-driver but without the journald --events-backend (file) is not supported" \ + "Should fail with reasonable error message when events-backend and events-logger do not match" } @@ -134,7 +158,7 @@ function _populate_events_file() { local events_file=$1 truncate --size=0 $events_file for i in {0..99}; do - printf '{"Name":"busybox","Status":"pull","Time":"2022-04-06T11:26:42.7236679%02d+02:00","Type":"image","Attributes":null}\n' $i >> $events_file + printf '{"Name":"busybox","Status":"pull","Time":"2022-04-06T11:26:42.7236679%02d+02:00","Type":"image","Attributes":null}\n' $i >> $events_file done } @@ -146,7 +170,6 @@ function _populate_events_file() { # Config without a limit eventsFile=$PODMAN_TMPDIR/events.txt - _populate_events_file $eventsFile containersConf=$PODMAN_TMPDIR/containers.conf cat >$containersConf <<EOF [engine] @@ -154,6 +177,11 @@ events_logger="file" events_logfile_path="$eventsFile" EOF + # Check that a non existing event file does not cause a hang (#15688) + CONTAINERS_CONF=$containersConf run_podman events --stream=false + + _populate_events_file $eventsFile + # Create events *without* a limit and make sure that it has not been # rotated/truncated. contentBefore=$(head -n100 $eventsFile) @@ -190,6 +218,34 @@ EOF # Make sure that `podman events` can read the file, and that it returns the # same amount of events. We checked the contents before. CONTAINERS_CONF=$containersConf run_podman events --stream=false --since="2022-03-06T11:26:42.723667984+02:00" - is "$(wc -l <$eventsFile)" "$(wc -l <<<$output)" "all events are returned" + assert "${#lines[@]}" = 51 "Number of events returned" is "${lines[-2]}" ".* log-rotation $eventsFile" } + +# Prior to #15633, container labels would not appear in 'die' log events +@test "events - labels included in container die" { + skip_if_remote "remote does not support --events-backend" + local cname=c$(random_string 15) + local lname=l$(random_string 10) + local lvalue="v$(random_string 10) $(random_string 5)" + + run_podman 17 --events-backend=file run --rm \ + --name=$cname \ + --label=$lname="$lvalue" \ + $IMAGE sh -c 'exit 17' + run_podman --events-backend=file events \ + --filter=container=$cname \ + --filter=status=died \ + --stream=false \ + --format="{{.Attributes.$lname}}" + assert "$output" = "$lvalue" "podman-events output includes container label" +} + +@test "events - backend none should error" { + skip_if_remote "remote does not support --events-backend" + + run_podman 125 --events-backend none events + is "$output" "Error: cannot read events with the \"none\" backend" "correct error message" + run_podman 125 --events-backend none events --stream=false + is "$output" "Error: cannot read events with the \"none\" backend" "correct error message" +} diff --git a/test/system/150-login.bats b/test/system/150-login.bats index b57bb44ab..b85007f0b 100644 --- a/test/system/150-login.bats +++ b/test/system/150-login.bats @@ -122,7 +122,7 @@ function setup() { --password-stdin \ $registry <<< "x${PODMAN_LOGIN_PASS}" is "$output" \ - "Error: error logging into \"$registry\": invalid username/password" \ + "Error: logging into \"$registry\": invalid username/password" \ 'output from podman login' } diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats index 6829c6a78..08baaf468 100644 --- a/test/system/160-volumes.bats +++ b/test/system/160-volumes.bats @@ -315,11 +315,11 @@ EOF # List available volumes for pruning after using 1,2,3 run_podman volume prune <<< N - is "$(echo $(sort <<<${lines[@]:1:3}))" "${v[4]} ${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use, lists 4,5,6" + is "$(echo $(sort <<<${lines[*]:1:3}))" "${v[4]} ${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use, lists 4,5,6" # List available volumes for pruning after using 1,2,3 and filtering; see #8913 run_podman volume prune --filter label=mylabel <<< N - is "$(echo $(sort <<<${lines[@]:1:2}))" "${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use and 4 filtered out, lists 5,6" + is "$(echo $(sort <<<${lines[*]:1:2}))" "${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use and 4 filtered out, lists 5,6" # prune should remove v4 run_podman volume prune --force diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index cbbd62ffb..9bbd56fef 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -61,7 +61,7 @@ function teardown() { @test "podman pod create - custom infra image" { - skip_if_remote "CONTAINERS_CONF only effects server side" + skip_if_remote "CONTAINERS_CONF only affects server side" image="i.do/not/exist:image" tmpdir=$PODMAN_TMPDIR/pod-test mkdir -p $tmpdir @@ -435,7 +435,7 @@ EOF run_podman pod rm $podID run_podman 125 pod create --exit-policy invalid - is "$output" "Error: .*error running pod create option: invalid pod exit policy: \"invalid\"" "invalid exit policy" + is "$output" "Error: .*running pod create option: invalid pod exit policy: \"invalid\"" "invalid exit policy" # Test exit-policy behaviour run_podman pod create --exit-policy continue @@ -478,10 +478,10 @@ spec: } @test "pod resource limits" { - # FIXME: #15074 - possible flake on aarch64 skip_if_remote "resource limits only implemented on non-remote" skip_if_rootless "resource limits only work with root" skip_if_cgroupsv1 "resource limits only meaningful on cgroups V2" + skip_if_aarch64 "FIXME: #15074 - flakes often on aarch64" # create loopback device lofile=${PODMAN_TMPDIR}/disk.img @@ -492,30 +492,24 @@ spec: lomajmin=$(losetup -l --noheadings --output MAJ:MIN $LOOPDEVICE | tr -d ' ') run grep -w bfq /sys/block/$(basename ${LOOPDEVICE})/queue/scheduler if [ $status -ne 0 ]; then + losetup -d $LOOPDEVICE + LOOPDEVICE= skip "BFQ scheduler is not supported on the system" - if [ -f ${lofile} ]; then - run_podman '?' rm -t 0 --all --force --ignore - - while read path dev; do - if [[ "$path" == "$lofile" ]]; then - losetup -d $dev - fi - done < <(losetup -l --noheadings --output BACK-FILE,NAME) - rm ${lofile} - fi fi echo bfq > /sys/block/$(basename ${LOOPDEVICE})/queue/scheduler + # FIXME: #15464: blkio-weight-device not working expected_limits=" cpu.max | 500000 100000 memory.max | 5242880 memory.swap.max | 1068498944 +io.bfq.weight | default 50 io.max | $lomajmin rbps=1048576 wbps=1048576 riops=max wiops=max " for cgm in systemd cgroupfs; do local name=resources-$cgm - run_podman --cgroup-manager=$cgm pod create --name=$name --cpus=5 --memory=5m --memory-swap=1g --cpu-shares=1000 --cpuset-cpus=0 --cpuset-mems=0 --device-read-bps=${LOOPDEVICE}:1mb --device-write-bps=${LOOPDEVICE}:1mb --blkio-weight-device=${LOOPDEVICE}:123 --blkio-weight=50 + run_podman --cgroup-manager=$cgm pod create --name=$name --cpus=5 --memory=5m --memory-swap=1g --cpu-shares=1000 --cpuset-cpus=0 --cpuset-mems=0 --device-read-bps=${LOOPDEVICE}:1mb --device-write-bps=${LOOPDEVICE}:1mb --blkio-weight=50 run_podman --cgroup-manager=$cgm pod start $name run_podman pod inspect --format '{{.CgroupPath}}' $name local cgroup_path="$output" diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index c502ad669..a1b24d293 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -20,44 +20,8 @@ function _check_health { done } - @test "podman healthcheck" { - # Create an image with a healthcheck script; said script will - # pass until the file /uh-oh gets created (by us, via exec) - cat >${PODMAN_TMPDIR}/healthcheck <<EOF -#!/bin/sh - -if test -e /uh-oh; then - echo "Uh-oh on stdout!" - echo "Uh-oh on stderr!" >&2 - exit 1 -else - echo "Life is Good on stdout" - echo "Life is Good on stderr" >&2 - exit 0 -fi -EOF - - cat >${PODMAN_TMPDIR}/entrypoint <<EOF -#!/bin/sh - -while :; do - sleep 1 -done -EOF - - cat >${PODMAN_TMPDIR}/Containerfile <<EOF -FROM $IMAGE - -COPY healthcheck /healthcheck -COPY entrypoint /entrypoint - -RUN chmod 755 /healthcheck /entrypoint - -CMD ["/entrypoint"] -EOF - - run_podman build -t healthcheck_i ${PODMAN_TMPDIR} + _build_health_check_image healthcheck_i # Run that healthcheck image. run_podman run -d --name healthcheck_c \ @@ -66,6 +30,9 @@ EOF --health-retries 3 \ healthcheck_i + run_podman inspect healthcheck_c --format "{{.Config.HealthcheckOnFailureAction}}" + is "$output" "none" "default on-failure action is none" + # We can't check for 'starting' because a 1-second interval is too # short; it could run healthcheck before we get to our first check. # @@ -109,4 +76,57 @@ Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\" run_podman rmi healthcheck_i } +@test "podman healthcheck --health-on-failure" { + run_podman 125 create --health-on-failure=kill $IMAGE + is "$output" "Error: cannot set on-failure action to kill without a health check" + + ctr="healthcheck_c" + img="healthcheck_i" + + for policy in none kill restart stop;do + if [[ $policy == "none" ]];then + # Do not remove the /uh-oh file for `none` as we want to + # demonstrate that no action was taken + _build_health_check_image $img + else + _build_health_check_image $img cleanfile + fi + + # Run that healthcheck image. + run_podman run -d --name $ctr \ + --health-cmd /healthcheck \ + --health-on-failure=$policy \ + $img + + # healthcheck should succeed + run_podman healthcheck run $ctr + + # Now cause the healthcheck to fail + run_podman exec $ctr touch /uh-oh + + # healthcheck should now fail, with exit status 1 and 'unhealthy' output + run_podman 1 healthcheck run $ctr + is "$output" "unhealthy" "output from 'podman healthcheck run'" + + run_podman inspect $ctr --format "{{.State.Status}} {{.Config.HealthcheckOnFailureAction}}" + if [[ $policy == "restart" ]];then + # Container has been restarted and health check works again + is "$output" "running $policy" "container has been restarted" + run_podman healthcheck run $ctr + elif [[ $policy == "none" ]];then + # Container is still running and health check still broken + is "$output" "running $policy" "container continued running" + run_podman 1 healthcheck run $ctr + is "$output" "unhealthy" "output from 'podman healthcheck run'" + else + # kill and stop yield the container into a non-running state + is "$output" ".* $policy" "container was stopped/killed" + assert "$output" != "running $policy" + fi + + run_podman rm -f -t0 $ctr + run_podman rmi -f $img + done +} + # vim: filetype=sh diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index 9a91501dd..ddec3a492 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -33,7 +33,10 @@ function teardown() { # Helper to start a systemd service running a container function service_setup() { - run_podman generate systemd --new $cname + run_podman generate systemd \ + -e http_proxy -e https_proxy -e no_proxy \ + -e HTTP_PROXY -e HTTPS_PROXY -e NO_PROXY \ + --new $cname echo "$output" > "$UNIT_FILE" run_podman rm $cname @@ -73,6 +76,18 @@ function service_cleanup() { # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate - systemd - basic" { + # Flakes with "ActiveState=failed (expected =inactive)" + if is_ubuntu; then + skip "FIXME: 2022-09-01: requires conmon-2.1.4, ubuntu has 2.1.3" + fi + + # Warn when a custom restart policy is used without --new (see #15284) + run_podman create --restart=always $IMAGE + cid="$output" + run_podman generate systemd $cid + is "$output" ".*Container $cid has restart policy .*always.* which can lead to issues on shutdown.*" "generate systemd emits warning" + run_podman rm -f $cid + cname=$(random_string) # See #7407 for --pull=always. run_podman create --pull=always --name $cname --label "io.containers.autoupdate=registry" $IMAGE \ @@ -295,25 +310,68 @@ LISTEN_FDNAMES=listen_fdnames" | sort) run_podman network rm -f $netname } -@test "podman-kube@.service template" { - # If running from a podman source directory, build and use the source - # version of the play-kube-@ unit file - unit_name="podman-kube@.service" - unit_file="contrib/systemd/system/${unit_name}" - if [[ -e ${unit_file}.in ]]; then - echo "# [Building & using $unit_name from source]" >&3 - # Force regenerating unit file (existing one may have /usr/bin path) - rm -f $unit_file - BINDIR=$(dirname $PODMAN) make $unit_file - cp $unit_file $UNIT_DIR/$unit_name - fi +@test "podman create --health-on-failure=kill" { + img="healthcheck_i" + _build_health_check_image $img + + cname=$(random_string) + run_podman create --name $cname \ + --health-cmd /healthcheck \ + --health-on-failure=kill \ + --restart=on-failure \ + $img + + # run container in systemd unit + service_setup + + run_podman container inspect $cname --format "{{.ID}}" + oldID="$output" + + run_podman healthcheck run $cname + + # Now cause the healthcheck to fail + run_podman exec $cname touch /uh-oh + # healthcheck should now fail, with exit status 1 and 'unhealthy' output + run_podman 1 healthcheck run $cname + is "$output" "unhealthy" "output from 'podman healthcheck run'" + + # What is expected to happen now: + # 1) The container gets killed as the health check has failed + # 2) Systemd restarts the service as the restart policy is set to "on-failure" + # 3) The /uh-oh file is gone and $cname has another ID + + # Wait at most 10 seconds for the service to be restarted + local timeout=10 + while [[ $timeout -gt 1 ]]; do + run_podman '?' container inspect $cname + if [[ $status == 0 ]]; then + if [[ "$output" != "$oldID" ]]; then + break + fi + fi + sleep 1 + let timeout=$timeout-1 + done + + run_podman healthcheck run $cname + + # stop systemd container + service_cleanup + run_podman rmi -f $img +} + +@test "podman-kube@.service template" { + install_kube_template # Create the YAMl file yaml_source="$PODMAN_TMPDIR/test.yaml" cat >$yaml_source <<EOF apiVersion: v1 kind: Pod metadata: + annotations: + io.containers.autoupdate: "local" + io.containers.autoupdate/b: "registry" labels: app: test name: test_pod @@ -322,8 +380,11 @@ spec: - command: - top image: $IMAGE - name: test - resources: {} + name: a + - command: + - top + image: $IMAGE + name: b EOF # Dispatch the YAML file @@ -344,6 +405,12 @@ EOF run_podman 125 container rm $service_container is "$output" "Error: container .* is the service container of pod(s) .* and cannot be removed without removing the pod(s)" + # Add a simple `auto-update --dry-run` test here to avoid too much redundancy + # with 255-auto-update.bats + run_podman auto-update --dry-run --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,false,local.*" "global auto-update policy gets applied" + is "$output" ".*$service_name,.* (test_pod-b),$IMAGE,false,registry.*" "container-specified auto-update policy gets applied" + # Kill the pod and make sure the service is not running. # The restart policy is set to "never" since there is no # design yet for propagating exit codes up to the service diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index 6cee939fb..76f6b02e8 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -115,6 +115,7 @@ function _confirm_update() { # Image has already been pulled, so this shouldn't take too long local timeout=5 while [[ $timeout -gt 0 ]]; do + sleep 1 run_podman '?' inspect --format "{{.Image}}" $cname if [[ $status != 0 ]]; then if [[ $output =~ (no such object|does not exist in database): ]]; then @@ -126,7 +127,7 @@ function _confirm_update() { elif [[ $output != $old_iid ]]; then return fi - sleep 1 + timeout=$((timeout - 1)) done die "Timed out waiting for $cname to update; old IID=$old_iid" @@ -234,6 +235,8 @@ function _confirm_update() { _confirm_update $cname $ori_image } +# This test can fail in dev. environment because of SELinux. +# quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman auto-update - label io.containers.autoupdate=local with rollback" { # sdnotify fails with runc 1.0.0-3-dev2 on Ubuntu. Let's just # assume that we work only with crun, nothing else. @@ -264,8 +267,6 @@ EOF # Generate a healthy image that will run correctly. run_podman build -t quay.io/libpod/$image -f $dockerfile1 - podman image inspect --format "{{.ID}}" $image - oldID="$output" generate_service $image local /runme --sdnotify=container noTag _wait_service_ready container-$cname.service @@ -275,7 +276,7 @@ EOF # Generate an unhealthy image that will fail. run_podman build -t quay.io/libpod/$image -f $dockerfile2 - podman image inspect --format "{{.ID}}" $image + run_podman image inspect --format "{{.ID}}" $image newID="$output" run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" @@ -373,6 +374,12 @@ After=network-online.target [Service] Type=oneshot ExecStart=/usr/bin/podman auto-update +Environment="http_proxy=${http_proxy}" +Environment="HTTP_PROXY=${HTTP_PROXY}" +Environment="https_proxy=${https_proxy}" +Environment="HTTPS_PROXY=${HTTPS_PROXY}" +Environment="no_proxy=${no_proxy}" +Environment="NO_PROXY=${NO_PROXY}" [Install] WantedBy=default.target @@ -407,4 +414,97 @@ EOF _confirm_update $cname $ori_image } +@test "podman-kube@.service template with rollback" { + # sdnotify fails with runc 1.0.0-3-dev2 on Ubuntu. Let's just + # assume that we work only with crun, nothing else. + # [copied from 260-sdnotify.bats] + runtime=$(podman_runtime) + if [[ "$runtime" != "crun" ]]; then + skip "this test only works with crun, not $runtime" + fi + + install_kube_template + + dockerfile1=$PODMAN_TMPDIR/Dockerfile.1 + cat >$dockerfile1 <<EOF +FROM quay.io/libpod/fedora:31 +RUN echo -e "#!/bin/sh\n\ +printenv NOTIFY_SOCKET; echo READY; systemd-notify --ready;\n\ +trap 'echo Received SIGTERM, finishing; exit' SIGTERM; echo WAITING; while :; do sleep 0.1; done" \ +>> /runme +RUN chmod +x /runme +EOF + + dockerfile2=$PODMAN_TMPDIR/Dockerfile.2 + cat >$dockerfile2 <<EOF +FROM quay.io/libpod/fedora:31 +RUN echo -e "#!/bin/sh\n\ +exit 1" >> /runme +RUN chmod +x /runme +EOF + local_image=localhost/image:$(random_string 10) + + # Generate a healthy image that will run correctly. + run_podman build -t $local_image -f $dockerfile1 + run_podman image inspect --format "{{.ID}}" $local_image + oldID="$output" + + # Create the YAMl file + yaml_source="$PODMAN_TMPDIR/test.yaml" + cat >$yaml_source <<EOF +apiVersion: v1 +kind: Pod +metadata: + annotations: + io.containers.autoupdate: "registry" + io.containers.autoupdate/b: "local" + io.containers.sdnotify/b: "container" + labels: + app: test + name: test_pod +spec: + containers: + - command: + - top + image: $IMAGE + name: a + - command: + - /runme + image: $local_image + name: b +EOF + + # Dispatch the YAML file + service_name="podman-kube@$(systemd-escape $yaml_source).service" + systemctl start $service_name + systemctl is-active $service_name + + # Make sure the containers are properly configured + run_podman auto-update --dry-run --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,false,registry.*" "global auto-update policy gets applied" + is "$output" ".*$service_name,.* (test_pod-b),$local_image,false,local.*" "container-specified auto-update policy gets applied" + + # Generate a broken image that will fail. + run_podman build -t $local_image -f $dockerfile2 + run_podman image inspect --format "{{.ID}}" $local_image + newID="$output" + + assert "$oldID" != "$newID" "broken image really is a new one" + + # Make sure container b sees the new image + run_podman auto-update --dry-run --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,false,registry.*" "global auto-update policy gets applied" + is "$output" ".*$service_name,.* (test_pod-b),$local_image,pending,local.*" "container b sees the new image" + + # Now update and check for the rollback + run_podman auto-update --format "{{.Unit}},{{.Container}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*$service_name,.* (test_pod-a),$IMAGE,rolled back,registry.*" "container a was rolled back as the update of b failed" + is "$output" ".*$service_name,.* (test_pod-b),$local_image,rolled back,local.*" "container b was rolled back as its update has failed" + + # Clean up + systemctl stop $service_name + run_podman rmi -f $(pause_image) $local_image $newID $oldID + rm -f $UNIT_DIR/$unit_name +} + # vim: filetype=sh diff --git a/test/system/260-sdnotify.bats b/test/system/260-sdnotify.bats index cd7b1262a..6c3ef7f3f 100644 --- a/test/system/260-sdnotify.bats +++ b/test/system/260-sdnotify.bats @@ -88,7 +88,13 @@ function _assert_mainpid_is_conmon() { export NOTIFY_SOCKET=$PODMAN_TMPDIR/ignore.sock _start_socat - run_podman 1 run --rm --sdnotify=ignore $IMAGE printenv NOTIFY_SOCKET + run_podman create --rm --sdnotify=ignore $IMAGE printenv NOTIFY_SOCKET + cid="$output" + + run_podman container inspect $cid --format "{{.Config.SdNotifyMode}} {{.Config.SdNotifySocket}}" + is "$output" "ignore " "NOTIFY_SOCKET is not set with 'ignore' mode" + + run_podman 1 start --attach $cid is "$output" "" "\$NOTIFY_SOCKET in container" is "$(< $_SOCAT_LOG)" "" "nothing received on socket" @@ -106,6 +112,9 @@ function _assert_mainpid_is_conmon() { cid="$output" wait_for_ready $cid + run_podman container inspect $cid --format "{{.Config.SdNotifyMode}} {{.Config.SdNotifySocket}}" + is "$output" "conmon $NOTIFY_SOCKET" + run_podman container inspect sdnotify_conmon_c --format "{{.State.ConmonPid}}" mainPID="$output" @@ -113,6 +122,7 @@ function _assert_mainpid_is_conmon() { is "$output" "READY" "\$NOTIFY_SOCKET in container" # The 'echo's help us debug failed runs + wait_for_file $_SOCAT_LOG run cat $_SOCAT_LOG echo "socat log:" echo "$output" @@ -132,7 +142,7 @@ READY=1" "sdnotify sent MAINPID and READY" # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "sdnotify : container" { - skip_if_aarch64 "FIXME: #15074 - fails on aarch64 non-remote" + skip_if_aarch64 "FIXME: #15277 sdnotify doesn't work on aarch64" # Sigh... we need to pull a humongous image because it has systemd-notify. # (IMPORTANT: fedora:32 and above silently removed systemd-notify; this # caused CI to hang. That's why we explicitly require fedora:31) @@ -147,13 +157,18 @@ READY=1" "sdnotify sent MAINPID and READY" _start_socat run_podman run -d --sdnotify=container $_FEDORA \ - sh -c 'printenv NOTIFY_SOCKET;echo READY;systemd-notify --ready;while ! test -f /stop;do sleep 0.1;done' + sh -c 'printenv NOTIFY_SOCKET; echo READY; while ! test -f /stop;do sleep 0.1;done;systemd-notify --ready' cid="$output" wait_for_ready $cid + run_podman container inspect $cid --format "{{.Config.SdNotifyMode}} {{.Config.SdNotifySocket}}" + is "$output" "container $NOTIFY_SOCKET" + run_podman logs $cid is "${lines[0]}" "/run/notify/notify.sock" "NOTIFY_SOCKET is passed to container" + run_podman container inspect $cid --format "{{.State.ConmonPid}}" + mainPID="$output" # With container, READY=1 isn't necessarily the last message received; # just look for it anywhere in received messages run cat $_SOCAT_LOG @@ -161,19 +176,25 @@ READY=1" "sdnotify sent MAINPID and READY" echo "socat log:" echo "$output" - is "$output" ".*READY=1" "received READY=1 through notify socket" - - _assert_mainpid_is_conmon "$output" + is "$output" "MAINPID=$mainPID" "Container is not ready yet, so we only know the main PID" # Done. Stop container, clean up. run_podman exec $cid touch /stop run_podman wait $cid + + wait_for_file $_SOCAT_LOG + run cat $_SOCAT_LOG + echo "socat log:" + echo "$output" + is "$output" "MAINPID=$mainPID +READY=1" + run_podman rm $cid run_podman rmi $_FEDORA _stop_socat } -@test "sdnotify : play kube" { +@test "sdnotify : play kube - no policies" { # Create the YAMl file yaml_source="$PODMAN_TMPDIR/test.yaml" cat >$yaml_source <<EOF @@ -202,8 +223,15 @@ EOF _start_socat run_podman play kube --service-container=true $yaml_source + + # Make sure the containers have the correct policy. + run_podman container inspect test_pod-test $service_container --format "{{.Config.SdNotifyMode}}" + is "$output" "ignore +ignore" + run_podman container inspect $service_container --format "{{.State.ConmonPid}}" mainPID="$output" + wait_for_file $_SOCAT_LOG # The 'echo's help us debug failed runs run cat $_SOCAT_LOG echo "socat log:" @@ -216,9 +244,116 @@ READY=1" "sdnotify sent MAINPID and READY" # Clean up pod and pause image run_podman play kube --down $PODMAN_TMPDIR/test.yaml - run_podman version --format "{{.Server.Version}}-{{.Server.Built}}" - podman rmi -f localhost/podman-pause:$output + run_podman rmi $(pause_image) } +@test "sdnotify : play kube - with policies" { + skip_if_aarch64 "FIXME: #15277 sdnotify doesn't work on aarch64" + + # Sigh... we need to pull a humongous image because it has systemd-notify. + # (IMPORTANT: fedora:32 and above silently removed systemd-notify; this + # caused CI to hang. That's why we explicitly require fedora:31) + # FIXME: is there a smaller image we could use? + local _FEDORA="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/fedora:31" + # Pull that image. Retry in case of flakes. + run_podman pull $_FEDORA || \ + run_podman pull $_FEDORA || \ + run_podman pull $_FEDORA + + # Create the YAMl file + yaml_source="$PODMAN_TMPDIR/test.yaml" + cat >$yaml_source <<EOF +apiVersion: v1 +kind: Pod +metadata: + labels: + app: test + name: test_pod + annotations: + io.containers.sdnotify: "container" + io.containers.sdnotify/b: "conmon" +spec: + containers: + - command: + - /bin/sh + - -c + - 'printenv NOTIFY_SOCKET; echo READY; while ! test -f /stop;do sleep 0.1;done;systemd-notify --ready' + image: $_FEDORA + name: a + - command: + - /bin/sh + - -c + - 'echo READY; top' + image: $IMAGE + name: b +EOF + container_a="test_pod-a" + container_b="test_pod-b" + + # The name of the service container is predictable: the first 12 characters + # of the hash of the YAML file followed by the "-service" suffix + yaml_sha=$(sha256sum $yaml_source) + service_container="${yaml_sha:0:12}-service" + + export NOTIFY_SOCKET=$PODMAN_TMPDIR/conmon.sock + _start_socat + + # Run `play kube` in the background as it will wait for all containers to + # send the READY=1 message. + timeout --foreground -v --kill=10 60 \ + $PODMAN play kube --service-container=true $yaml_source &>/dev/null & + + # Wait for both containers to be running + for i in $(seq 1 20); do + run_podman "?" container wait $container_a $container_b --condition="running" + if [[ $status == 0 ]]; then + break + fi + sleep 0.5 + # Just for debugging + run_podman ps -a + done + if [[ $status != 0 ]]; then + die "container $container_a and/or $container_b did not start" + fi + + # Make sure the containers have the correct policy + run_podman container inspect $container_a $container_b $service_container --format "{{.Config.SdNotifyMode}}" + is "$output" "container +conmon +ignore" + + is "$(< $_SOCAT_LOG)" "" "nothing received on socket" + + # Make sure the container received a "proxy" socket and is not using the + # one of `kube play` + run_podman container inspect $container_a --format "{{.Config.SdNotifySocket}}" + assert "$output" != $NOTIFY_SOCKET + + run_podman logs $container_a + is "${lines[0]}" "/run/notify/notify.sock" "NOTIFY_SOCKET is passed to container" + + # Instruct the container to send the READY + run_podman exec $container_a /bin/touch /stop + + run_podman container inspect $service_container --format "{{.State.ConmonPid}}" + main_pid="$output" + + run_podman container wait $container_a + wait_for_file $_SOCAT_LOG + # The 'echo's help us debug failed runs + run cat $_SOCAT_LOG + echo "socat log:" + echo "$output" + + is "$output" "MAINPID=$main_pid +READY=1" "sdnotify sent MAINPID and READY" + + _stop_socat + + # Clean up pod and pause image + run_podman play kube --down $yaml_source + run_podman rmi $_FEDORA $(pause_image) +} # vim: filetype=sh diff --git a/test/system/272-system-connection.bats b/test/system/272-system-connection.bats index e9e9a01ea..e937a7273 100644 --- a/test/system/272-system-connection.bats +++ b/test/system/272-system-connection.bats @@ -95,12 +95,12 @@ $c2[ ]\+tcp://localhost:54321[ ]\+true" \ # we need for the server. ${PODMAN%%-remote*} --root ${PODMAN_TMPDIR}/root \ --runroot ${PODMAN_TMPDIR}/runroot \ - system service -t 99 tcp:localhost:$_SERVICE_PORT & + system service -t 99 tcp://localhost:$_SERVICE_PORT & _SERVICE_PID=$! wait_for_port localhost $_SERVICE_PORT _run_podman_remote info --format '{{.Host.RemoteSocket.Path}}' - is "$output" "tcp:localhost:$_SERVICE_PORT" \ + is "$output" "tcp://localhost:$_SERVICE_PORT" \ "podman info works, and talks to the correct server" _run_podman_remote info --format '{{.Store.GraphRoot}}' diff --git a/test/system/280-update.bats b/test/system/280-update.bats new file mode 100644 index 000000000..c7037c286 --- /dev/null +++ b/test/system/280-update.bats @@ -0,0 +1,130 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Tests for podman update +# + +load helpers + +LOOPDEVICE= + +function teardown() { + if [[ -n "$LOOPDEVICE" ]]; then + losetup -d $LOOPDEVICE + LOOPDEVICE= + fi + basic_teardown +} + + +@test "podman update - test all options" { + + local cgv=1 + if is_cgroupsv2; then + cgv=2; + fi + + # Need a block device for blkio-weight-device testing + local pass_loop_device= + if ! is_rootless; then + if is_cgroupsv2; then + lofile=${PODMAN_TMPDIR}/disk.img + fallocate -l 1k ${lofile} + LOOPDEVICE=$(losetup --show -f $lofile) + pass_loop_device="--device $LOOPDEVICE" + + # Get maj:min (tr needed because losetup seems to use %2d) + lomajmin=$(losetup -l --noheadings --output MAJ:MIN $LOOPDEVICE | tr -d ' ') + fi + fi + + # Shortcuts to make the table narrower + local -a gig=(0 1073741824 2147483648 3221225472) + local devicemax="1:5 rbps=10485760 wbps=31457280 riops=2000 wiops=4000" + local mm=memory/memory + + # Format: + # --<option> = <value> | rootless? | check: cgroups v1 | check: cgroups v2 + # + # Requires very wide window to read. Sorry. + # + # FIXMEs: + # cpu-rt-period (cgv1 only, on cpu/cpu.rt_period_us) works on RHEL8 but not on Ubuntu + # cpu-rt-runtime (cgv1 only, on cpu/cpu.rt_runtime_us) fails: error setting cgroup config for procHooks ... + tests=" +cpu-shares = 512 | - | cpu/cpu.shares = 512 | cpu.weight = 20 +cpus = 5 | - | cpu/cpu.cfs_quota_us = 500000 | cpu.max = 500000 100000 +cpuset-cpus = 0 | - | cpuset/cpuset.cpus = 0 | cpuset.cpus = 0 +cpuset-mems = 0 | - | cpuset/cpuset.mems = 0 | cpuset.mems = 0 + +memory = 1G | 2 | $mm.limit_in_bytes = ${gig[1]} | memory.max = ${gig[1]} +memory-swap = 3G | 2 | $mm.memsw.limit_in_bytes = ${gig[3]} | memory.swap.max = ${gig[2]} +memory-reservation = 400M | 2 | $mm.soft_limit_in_bytes = 419430400 | memory.low = 419430400 + +blkio-weight = 321 | - | - | io.bfq.weight = default 321 $lomajmin 98 +blkio-weight-device = $LOOPDEVICE:98 | - | - | io.bfq.weight = default 321 $lomajmin 98 + +device-read-bps = /dev/zero:10mb | - | - | io.max = $devicemax +device-read-iops = /dev/zero:2000 | - | - | io.max = $devicemax +device-write-bps = /dev/zero:30mb | - | - | io.max = $devicemax +device-write-iops = /dev/zero:4000 | - | - | io.max = $devicemax +" + + # Run a container + run_podman run ${pass_loop_device} -d $IMAGE sleep infinity + cid="$output" + + # Pass 1: read the table above, gather up the options applicable + # to this test environment (root/rootless, cgroups v1/v2) + local -a opts + local -A check + while read opt works_rootless cgv1 cgv2; do + if is_rootless; then + local skipping="skipping --$opt : does not work rootless" + if [[ $works_rootless = '-' ]]; then + echo "[ $skipping ]" + continue + fi + if [[ ! $works_rootless =~ $cgv ]]; then + echo "[ $skipping on cgroups v$cgv ]" + continue + fi + fi + + tuple=$cgv1 + if is_cgroupsv2; then + tuple=$cgv2 + fi + if [[ $tuple = '-' ]]; then + echo "[ skipping --$opt : N/A on cgroups v$cgv ]" + continue + fi + + # OK: setting is applicable. Preserve it. (First removing whitespace) + opt=${opt// /} + opts+=("--$opt") + check["--$opt"]=$tuple + done < <(parse_table "$tests") + + # Now do the update in one fell swoop + run_podman update "${opts[@]}" $cid + + # ...and check one by one + for opt in "${opts[@]}"; do + read path op expect <<<"${check[$opt]}" + run_podman exec $cid cat /sys/fs/cgroup/$path + + # Magic echo of unquoted-output converts newlines to spaces; + # important for otherwise multiline blkio file. + updated="$(echo $output)" + assert "$updated" $op "$expect" "$opt ($path)" + done + + # Clean up + run_podman rm -f -t0 $cid + if [[ -n "$LOOPDEVICE" ]]; then + losetup -d $LOOPDEVICE + LOOPDEVICE= + fi +} + +# vim: filetype=sh diff --git a/test/system/320-system-df.bats b/test/system/320-system-df.bats index 217357b37..35e121c62 100644 --- a/test/system/320-system-df.bats +++ b/test/system/320-system-df.bats @@ -27,7 +27,7 @@ function teardown() { run_podman system df --format '{{ .Type }}:{{ .Total }}:{{ .Active }}' is "${lines[0]}" "Images:1:1" "system df : Images line" is "${lines[1]}" "Containers:2:1" "system df : Containers line" - is "${lines[2]}" "Local Volumes:2:1" "system df : Volumes line" + is "${lines[2]}" "Local Volumes:2:2" "system df : Volumes line" # Try -v. (Grrr. No way to specify individual formats) # diff --git a/test/system/330-corrupt-images.bats b/test/system/330-corrupt-images.bats index 7f2b81835..2f0fd753c 100644 --- a/test/system/330-corrupt-images.bats +++ b/test/system/330-corrupt-images.bats @@ -74,7 +74,7 @@ function _corrupt_image_test() { # Corruptify, and confirm that 'podman images' throws an error rm -v ${PODMAN_CORRUPT_TEST_WORKDIR}/root/*-images/$id/${rm_path} run_podman 125 images - is "$output" "Error: error retrieving label for image \"$id\": you may need to remove the image to resolve the error.*" + is "$output" "Error: retrieving label for image \"$id\": you may need to remove the image to resolve the error.*" # Run the requested command. Confirm it succeeds, with suitable warnings run_podman $* diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 0d6be2d60..d70c95973 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -119,7 +119,7 @@ EOF # Some of the above may not exist on our host. Find only the ones that do. local -a subset=() - for mp in ${mps[@]}; do + for mp in "${mps[@]}"; do if [ -e $mp ]; then subset+=($mp) fi @@ -128,7 +128,7 @@ EOF # Run 'stat' on all the files, plus /dev/null. Get path, file type, # number of links, major, and minor (see below for why). Do it all # in one go, to avoid multiple podman-runs - run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]} + run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null "${subset[@]}" assert $status -le 1 "stat exit status: expected 0 or 1" local devnull= diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats index 082482c7a..cc86f282a 100644 --- a/test/system/410-selinux.bats +++ b/test/system/410-selinux.bats @@ -212,7 +212,7 @@ function check_label() { # https://github.com/opencontainers/selinux/pull/148/commits/a5dc47f74c56922d58ead05d1fdcc5f7f52d5f4e # from failed to set /proc/self/attr/keycreate on procfs # to write /proc/self/attr/keycreate: invalid argument - runc) expect="OCI runtime error: .*: \(failed to set|write\) /proc/self/attr/keycreate" ;; + runc) expect="OCI runtime error: .*: \(failed to set\|write\) /proc/self/attr/keycreate.*" ;; *) skip "Unknown runtime '$runtime'";; esac diff --git a/test/system/420-cgroups.bats b/test/system/420-cgroups.bats index 025a20012..3269f666c 100644 --- a/test/system/420-cgroups.bats +++ b/test/system/420-cgroups.bats @@ -19,6 +19,8 @@ load helpers esac run_podman --cgroup-manager=$other run --name myc $IMAGE true + assert "$output" = "" "run true, with cgroup-manager=$other, is silent" + run_podman container inspect --format '{{.HostConfig.CgroupManager}}' myc is "$output" "$other" "podman preserved .HostConfig.CgroupManager" @@ -29,7 +31,8 @@ load helpers # Restart the container, without --cgroup-manager option (ie use default) # Prior to #7970, this would fail with an OCI runtime error - run_podman start myc + run_podman start -a myc + assert "$output" = "" "restarted container emits no output" run_podman rm myc } diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index b9a173c2a..862bc285c 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -61,9 +61,9 @@ load helpers is "$output" "$random_2" "curl 127.0.0.1:/index2.txt" # Verify http contents: wget from a second container - run_podman run --rm --net=host $IMAGE wget -qO - $SERVER/index.txt + run_podman run --rm --net=host --http-proxy=false $IMAGE wget -qO - $SERVER/index.txt is "$output" "$random_1" "podman wget /index.txt" - run_podman run --rm --net=host $IMAGE wget -qO - $SERVER/index2.txt + run_podman run --rm --net=host --http-proxy=false $IMAGE wget -qO - $SERVER/index2.txt is "$output" "$random_2" "podman wget /index2.txt" # Tests #4889 - two-argument form of "podman ports" was broken @@ -767,4 +767,14 @@ EOF is "$output" "" "Should print no output" } +@test "podman network rm --dns-option " { + dns_opt=dns$(random_string) + run_podman run --rm --dns-opt=${dns_opt} $IMAGE cat /etc/resolv.conf + is "$output" ".*options ${dns_opt}" "--dns-opt was added" + + dns_opt=dns$(random_string) + run_podman run --rm --dns-option=${dns_opt} $IMAGE cat /etc/resolv.conf + is "$output" ".*options ${dns_opt}" "--dns-option was added" +} + # vim: filetype=sh diff --git a/test/system/520-checkpoint.bats b/test/system/520-checkpoint.bats index 7c8fc143a..73fa5d4c4 100644 --- a/test/system/520-checkpoint.bats +++ b/test/system/520-checkpoint.bats @@ -101,6 +101,25 @@ function teardown() { run_podman rm -t 0 -f $cid } +@test "podman checkpoint/restore print IDs or raw input" { + # checkpoint/restore -a must print the IDs + run_podman run -d $IMAGE top + ctrID="$output" + run_podman container checkpoint -a + is "$output" "$ctrID" + run_podman container restore -a + is "$output" "$ctrID" + + # checkpoint/restore $input must print $input + cname=$(random_string) + run_podman run -d --name $cname $IMAGE top + run_podman container checkpoint $cname + is "$output" $cname + run_podman container restore $cname + is "$output" $cname + + run_podman rm -t 0 -f $ctrID $cname +} @test "podman checkpoint --export, with volumes" { skip_if_remote "Test uses --root/--runroot, which are N/A over remote" diff --git a/test/system/610-format.bats b/test/system/610-format.bats new file mode 100644 index 000000000..8f74634d1 --- /dev/null +++ b/test/system/610-format.bats @@ -0,0 +1,184 @@ +#!/usr/bin/env bats -*- bats -*- +# +# PR #15673: For all commands that accept --format '{{.GoTemplate}}', +# invoke with --format '{{"\n"}}' and make sure they don't choke. +# + +load helpers + +function teardown() { + # In case test fails: standard teardown does not wipe machines or secrets + run_podman '?' machine rm -f mymachine + run_podman '?' secret rm mysecret + + basic_teardown +} + +# Most commands can't just be run with --format; they need an argument or +# option. This table defines what those are. +# +# FIXME: once you've finished fixing them all, remove the SKIPs (just +# remove the entire lines, except for pod-inspect, just remove the SKIP +# but leave "mypod") +extra_args_table=" +history | $IMAGE +image history | $IMAGE +image inspect | $IMAGE +container inspect | mycontainer + +volume inspect | -a +secret inspect | mysecret +network inspect | podman +ps | -a + +image search | $IMAGE +search | $IMAGE + +pod inspect | mypod + +events | --stream=false --events-backend=file +" + +# podman machine is finicky. Assume we can't run it, but see below for more. +can_run_podman_machine= + +# podman stats, too +can_run_stats= + +# Main test loop. Recursively runs 'podman [subcommand] help', looks for: +# > '[command]', which indicates, recurse; or +# > '--format', in which case we +# > check autocompletion, look for Go templates, in which case we +# > run the command with --format '{{"\n"}}' and make sure it passes +function check_subcommand() { + for cmd in $(_podman_commands "$@"); do + # Special case: 'podman machine' can only be run under ideal conditions + if [[ "$cmd" = "machine" ]] && [[ -z "$can_run_podman_machine" ]]; then + continue + fi + if [[ "$cmd" = "stats" ]] && [[ -z "$can_run_stats" ]]; then + continue + fi + + # Human-readable podman command string, with multiple spaces collapsed + command_string="podman $* $cmd" + command_string=${command_string// / } # 'podman x' -> 'podman x' + + # Run --help, decide if this is a subcommand with subcommands + run_podman "$@" $cmd --help + local full_help="$output" + + # The line immediately after 'Usage:' gives us a 1-line synopsis + usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1) + assert "$usage" != "" "podman $cmd: no Usage message found" + + # Strip off the leading command string; we no longer need it + usage=$(sed -e "s/^ $command_string \?//" <<<"$usage") + + # If usage ends in '[command]', recurse into subcommands + if expr "$usage" : '\[command\]' >/dev/null; then + # (except for 'podman help', which is a special case) + if [[ $cmd != "help" ]]; then + check_subcommand "$@" $cmd + fi + continue + fi + + # Not a subcommand-subcommand. Look for --format option + if [[ ! "$output" =~ "--format" ]]; then + continue + fi + + # Have --format. Make sure it's a Go-template option, not like --push + run_podman __completeNoDesc "$@" "$cmd" --format '{{.' + if [[ ! "$output" =~ \{\{\.[A-Z] ]]; then + continue + fi + + # Got one. + dprint "$command_string has --format" + + # Whatever is needed to make a runnable command + local extra=${extra_args[$command_string]} + if [[ -n "$extra" ]]; then + # Cross off our list + unset extra_args["$command_string"] + fi + + # This is what does the work. We run with '?' so we can offer + # better error messages than just "exited with error status". + run_podman '?' "$@" "$cmd" $extra --format '{{"\n"}}' + + # Output must always be empty. + # + # - If you see "unterminated quoted string" here, there's a + # regression, and you need to fix --format (see PR #15673) + # + # - If you see any other error, it probably means that someone + # added a new podman subcommand that supports --format but + # needs some sort of option or argument to actually run. + # See 'extra_args_table' at the top of this script. + # + assert "$output" = "" "$command_string --format '{{\"\n\"}}'" + + # *Now* check exit status. This should never, ever, ever trigger! + # If it does, it means the podman command failed without an err msg! + assert "$status" = "0" \ + "$command_string --format '{{\"\n\"}}' failed with no output!" + done +} + +# Test entry point +@test "check Go template formatting" { + skip_if_remote + + # Setup: some commands need a container, pod, secret, ... + run_podman run -d --name mycontainer $IMAGE top + run_podman pod create mypod + run_podman secret create mysecret /etc/hosts + + # ...or machine. But podman machine is ultra-finicky, it fails as root + # or if qemu is missing. Instead of checking for all the possible ways + # to skip it, just try running init. If it works, we can test it. + run_podman '?' machine init --image-path=/dev/null mymachine + if [[ $status -eq 0 ]]; then + can_run_podman_machine=true + extra_args_table+=" +machine inspect | mymachine +" + fi + + # Similarly, 'stats' cannot run rootless under cgroups v1 + if ! is_rootless || is_cgroupsv2; then + can_run_stats=true + extra_args_table+=" +container stats | --no-stream +pod stats | --no-stream +stats | --no-stream +" + fi + + # Convert the table at top to an associative array, keyed on subcommand + declare -A extra_args + while read subcommand extra; do + extra_args["podman $subcommand"]=$extra + done < <(parse_table "$extra_args_table") + + # Run the test + check_subcommand + + # Clean up + run_podman pod rm mypod + run_podman rmi $(pause_image) + run_podman rm -f -t0 mycontainer + run_podman secret rm mysecret + run_podman '?' machine rm -f mymachine + + # Make sure there are no leftover commands in our table - this would + # indicate a typo in the table, or a flaw in our logic such that + # we're not actually recursing. + local leftovers="${!extra_args[@]}" + assert "$leftovers" = "" "Did not find (or test) subcommands:" +} + +# vim: filetype=sh diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 72602a9e6..bad9544ff 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -182,8 +182,11 @@ EOF run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "none" "network mode none is set for the container" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman kube down - < $PODMAN_TMPDIR/test.yaml + run_podman 125 inspect test_pod-test + is "$output" ".*Error: inspecting object: no such object: \"test_pod-test\"" + run_podman pod rm -a + run_podman rm -a } @test "podman play with user from image" { @@ -325,7 +328,6 @@ spec: - name: TERM value: xterm - name: container - value: podman image: quay.io/libpod/userimage name: test @@ -353,6 +355,34 @@ status: {} run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" test_pod-test is "$output" "$default_driver" "play kube uses default log driver" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman kube down $PODMAN_TMPDIR/test.yaml + run_podman 125 inspect test_pod-test + is "$output" ".*Error: inspecting object: no such object: \"test_pod-test\"" + run_podman pod rm -a + run_podman rm -a +} + +@test "podman kube play - URL" { + TESTDIR=$PODMAN_TMPDIR/testdir + mkdir -p $TESTDIR + echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + + HOST_PORT=$(random_free_port) + SERVER=http://127.0.0.1:$HOST_PORT + + run_podman run -d --name myyaml -p "$HOST_PORT:80" \ + -v $PODMAN_TMPDIR/test.yaml:/var/www/testpod.yaml:Z \ + -w /var/www \ + $IMAGE /bin/busybox-extras httpd -f -p 80 + + run_podman kube play $SERVER/testpod.yaml + run_podman inspect test_pod-test --format "{{.State.Running}}" + is "$output" "true" + run_podman kube down $SERVER/testpod.yaml + run_podman 125 inspect test_pod-test + is "$output" ".*Error: inspecting object: no such object: \"test_pod-test\"" + + run_podman pod rm -a -f + run_podman rm -a -f + run_podman rm -f -t0 myyaml } diff --git a/test/system/710-kube.bats b/test/system/710-kube.bats new file mode 100644 index 000000000..97a640e3f --- /dev/null +++ b/test/system/710-kube.bats @@ -0,0 +1,160 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Test podman kube generate +# + +load helpers + +# standard capability drop list +capabilities='{"drop":["CAP_MKNOD","CAP_NET_RAW","CAP_AUDIT_WRITE"]}' + +# Warning that is emitted once on containers, multiple times on pods +kubernetes_63='Truncation Annotation: .* Kubernetes only allows 63 characters' + +# filter: convert yaml to json, because bash+yaml=madness +function yaml2json() { + egrep -v "$kubernetes_63" | python3 -c 'import yaml +import json +import sys +json.dump(yaml.safe_load(sys.stdin), sys.stdout)' +} + +############################################################################### +# BEGIN tests + +@test "podman kube generate - usage message" { + run_podman kube generate --help + is "$output" ".*podman.* kube generate \[options\] {CONTAINER...|POD...|VOLUME...}" + run_podman generate kube --help + is "$output" ".*podman.* generate kube \[options\] {CONTAINER...|POD...|VOLUME...}" +} + +@test "podman kube generate - container" { + cname=c$(random_string 15) + run_podman container create --name $cname $IMAGE top + run_podman kube generate $cname + + # Convert yaml to json, and dump to stdout (to help in case of errors) + json=$(yaml2json <<<"$output") + jq . <<<"$json" + + # What we expect to see. This is by necessity an incomplete list. + # For instance, it does not include org.opencontainers.image.base.* + # because sometimes we get that, sometimes we don't. No clue why. + # + # And, unfortunately, if new fields are added to the YAML, we won't + # test those unless a developer remembers to add them here. + # + # Reasons for doing it this way, instead of straight-comparing yaml: + # 1) the arbitrariness of the org.opencontainers.image.base annotations + # 2) YAML order is nondeterministic, so on a pod with two containers + # (as in the pod test below) we cannot rely on cname1/cname2. + expect=" +apiVersion | = | v1 +kind | = | Pod + +metadata.annotations.\"io.kubernetes.cri-o.TTY/$cname\" | = | false +metadata.annotations.\"io.podman.annotations.autoremove/$cname\" | = | FALSE +metadata.annotations.\"io.podman.annotations.init/$cname\" | = | FALSE +metadata.annotations.\"io.podman.annotations.privileged/$cname\" | = | FALSE +metadata.annotations.\"io.podman.annotations.publish-all/$cname\" | = | FALSE + +metadata.creationTimestamp | =~ | [0-9T:-]\\+Z +metadata.labels.app | = | ${cname}-pod +metadata.name | = | ${cname}-pod + +spec.containers[0].command | = | [\"top\"] +spec.containers[0].image | = | $IMAGE +spec.containers[0].name | = | $cname + +spec.containers[0].securityContext.capabilities | = | $capabilities + +status | = | null +" + + # Parse and check all those + while read key op expect; do + actual=$(jq -r -c ".$key" <<<"$json") + assert "$actual" $op "$expect" ".$key" + done < <(parse_table "$expect") + + run_podman rm $cname +} + +@test "podman kube generate - pod" { + local pname=p$(random_string 15) + local cname1=c1$(random_string 15) + local cname2=c2$(random_string 15) + + run_podman pod create --name $pname --publish 9999:8888 + + # Needs at least one container. Error is slightly different between + # regular and remote podman: + # regular: Error: pod ... only has... + # remote: Error: generating YAML: pod ... only has... + run_podman 125 kube generate $pname + assert "$output" =~ "Error: .* only has an infra container" + + run_podman container create --name $cname1 --pod $pname $IMAGE top + run_podman container create --name $cname2 --pod $pname $IMAGE bottom + run_podman kube generate $pname + + json=$(yaml2json <<<"$output") + jq . <<<"$json" + + # See container test above for description of this table + expect=" +apiVersion | = | v1 +kind | = | Pod + +metadata.annotations.\"io.kubernetes.cri-o.ContainerType/$cname1\" | = | container +metadata.annotations.\"io.kubernetes.cri-o.ContainerType/$cname2\" | = | container +metadata.annotations.\"io.kubernetes.cri-o.SandboxID/$cname1\" | =~ | [0-9a-f]\\{56\\} +metadata.annotations.\"io.kubernetes.cri-o.SandboxID/$cname2\" | =~ | [0-9a-f]\\{56\\} +metadata.annotations.\"io.kubernetes.cri-o.TTY/$cname1\" | = | false +metadata.annotations.\"io.kubernetes.cri-o.TTY/$cname2\" | = | false +metadata.annotations.\"io.podman.annotations.autoremove/$cname1\" | = | FALSE +metadata.annotations.\"io.podman.annotations.autoremove/$cname2\" | = | FALSE +metadata.annotations.\"io.podman.annotations.init/$cname1\" | = | FALSE +metadata.annotations.\"io.podman.annotations.init/$cname2\" | = | FALSE +metadata.annotations.\"io.podman.annotations.privileged/$cname1\" | = | FALSE +metadata.annotations.\"io.podman.annotations.privileged/$cname2\" | = | FALSE +metadata.annotations.\"io.podman.annotations.publish-all/$cname1\" | = | FALSE +metadata.annotations.\"io.podman.annotations.publish-all/$cname2\" | = | FALSE + +metadata.creationTimestamp | =~ | [0-9T:-]\\+Z +metadata.labels.app | = | ${pname} +metadata.name | = | ${pname} + +spec.hostname | = | $pname +spec.restartPolicy | = | Never + +spec.containers[0].command | = | [\"top\"] +spec.containers[0].image | = | $IMAGE +spec.containers[0].name | = | $cname1 +spec.containers[0].ports[0].containerPort | = | 8888 +spec.containers[0].ports[0].hostPort | = | 9999 +spec.containers[0].resources | = | {} + +spec.containers[1].command | = | [\"bottom\"] +spec.containers[1].image | = | $IMAGE +spec.containers[1].name | = | $cname2 +spec.containers[1].ports | = | null +spec.containers[1].resources | = | {} + +spec.containers[0].securityContext.capabilities | = | $capabilities + +status | = | {} +" + + while read key op expect; do + actual=$(jq -r -c ".$key" <<<"$json") + assert "$actual" $op "$expect" ".$key" + done < <(parse_table "$expect") + + run_podman rm $cname1 $cname2 + run_podman pod rm $pname + run_podman rmi $(pause_image) +} + +# vim: filetype=sh diff --git a/test/system/900-ssh.bats b/test/system/900-ssh.bats new file mode 100644 index 000000000..4f1682d48 --- /dev/null +++ b/test/system/900-ssh.bats @@ -0,0 +1,61 @@ +#!/usr/bin/env bats +# +# Simplest set of podman tests. If any of these fail, we have serious problems. +# + +load helpers + +# Override standard setup! We don't yet trust podman-images or podman-rm +function setup() { + if ! is_remote; then + skip "only applicable on podman-remote" + fi + + basic_setup +} + +function teardown() { + if ! is_remote; then + return + fi + + # In case test function failed to clean up + if [[ -n $_SERVICE_PID ]]; then + run kill $_SERVICE_PID + fi + + # see test/system/272-system-connection.bats for why this is needed + mount \ + | grep $PODMAN_TMPDIR \ + | awk '{print $3}' \ + | xargs -l1 --no-run-if-empty umount + + run_podman system connection rm --all + + basic_teardown +} + +function _run_podman_remote() { + PODMAN=${PODMAN%%--url*} run_podman "$@" +} + +@test "podman --ssh test" { + skip_if_no_ssh "cannot run these tests without an ssh binary" + # Start server + _SERVICE_PORT=$(random_free_port 63000-64999) + + ${PODMAN%%-remote*} --root ${PODMAN_TMPDIR}/root \ + --runroot ${PODMAN_TMPDIR}/runroot \ + system service -t 99 tcp://localhost:$_SERVICE_PORT & + _SERVICE_PID=$! + wait_for_port localhost $_SERVICE_PORT + + notme=${PODMAN_ROOTLESS_USER} + + uid=$(id -u $notme) + + run_podman 125 --ssh=native system connection add testing ssh://$notme@localhost:22/run/user/$uid/podman/podman.sock + is "$output" "Error: exit status 255" + + # need to figure out how to podman remote test with the new ssh +} diff --git a/test/system/TODO.md b/test/system/TODO.md index e47292f26..55e7601d1 100644 --- a/test/system/TODO.md +++ b/test/system/TODO.md @@ -1,4 +1,4 @@ - + # Overview diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 19bc6547c..4bc1ba78c 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -36,20 +36,6 @@ fi # That way individual tests can override with their own setup/teardown, # while retaining the ability to include these if they so desire. -# Some CI systems set this to runc, overriding the default crun. -if [[ -n $OCI_RUNTIME ]]; then - if [[ -z $CONTAINERS_CONF ]]; then - # FIXME: BATS provides no mechanism for end-of-run cleanup[1]; how - # can we avoid leaving this file behind when we finish? - # [1] https://github.com/bats-core/bats-core/issues/39 - export CONTAINERS_CONF=$(mktemp --tmpdir=${BATS_TMPDIR:-/tmp} podman-bats-XXXXXXX.containers.conf) - cat >$CONTAINERS_CONF <<EOF -[engine] -runtime="$OCI_RUNTIME" -EOF - fi -fi - # Setup helper: establish a test environment with exactly the images needed function basic_setup() { # Clean up all containers @@ -191,6 +177,15 @@ function run_podman() { # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" + + # FIXME FIXME FIXME: instrumenting to track down #15488. Please + # remove once that's fixed. We include the args because, remember, + # bats only shows output on error; it's possible that the first + # instance of the metacopy warning happens in a test that doesn't + # check output, hence doesn't fail. + if [[ "$output" =~ Ignoring.global.metacopy.option ]]; then + echo "# YO! metacopy warning triggered by: podman $*" >&3 + fi fi if [ "$status" -ne 0 ]; then echo -n "[ rc=$status "; @@ -342,11 +337,32 @@ function wait_for_port() { die "Timed out waiting for $host:$port" } +################### +# wait_for_file # Returns once file is available on host +################### +function wait_for_file() { + local file=$1 # The path to the file + local _timeout=${2:-5} # Optional; default 5 seconds + + # Wait + while [ $_timeout -gt 0 ]; do + test -e $file && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + + die "Timed out waiting for $file" +} + # END podman helpers ############################################################################### # BEGIN miscellaneous tools # Shortcuts for common needs: +function no_ssh() { + [ "$(man ssh)" -ne 0 ] +} + function is_ubuntu() { grep -qiw ubuntu /etc/os-release } @@ -470,6 +486,17 @@ function _add_label_if_missing() { } ###################### +# skip_if_no_ssh # ...with an optional message +###################### +function skip_if_no_ssh() { + if no_ssh; then + local msg=$(_add_label_if_missing "$1" "ssh") + skip "${msg:-not applicable with no ssh binary}" + fi +} + + +###################### # skip_if_rootless # ...with an optional message ###################### function skip_if_rootless() { @@ -876,5 +903,59 @@ function _podman_commands() { awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' <<<"$output" | grep . } +############################### +# _build_health_check_image # Builds a container image with a configured health check +############################### +# +# The health check will fail once the /uh-oh file exists. +# +# First argument is the desired name of the image +# Second argument, if present and non-null, forces removal of the /uh-oh file once the check failed; this way the container can be restarted +# + +function _build_health_check_image { + local imagename="$1" + local cleanfile="" + + if [[ ! -z "$2" ]]; then + cleanfile="rm -f /uh-oh" + fi + # Create an image with a healthcheck script; said script will + # pass until the file /uh-oh gets created (by us, via exec) + cat >${PODMAN_TMPDIR}/healthcheck <<EOF +#!/bin/sh + +if test -e /uh-oh; then + echo "Uh-oh on stdout!" + echo "Uh-oh on stderr!" >&2 + ${cleanfile} + exit 1 +else + echo "Life is Good on stdout" + echo "Life is Good on stderr" >&2 + exit 0 +fi +EOF + + cat >${PODMAN_TMPDIR}/entrypoint <<EOF +#!/bin/sh + +trap 'echo Received SIGTERM, finishing; exit' SIGTERM; echo WAITING; while :; do sleep 0.1; done +EOF + + cat >${PODMAN_TMPDIR}/Containerfile <<EOF +FROM $IMAGE + +COPY healthcheck /healthcheck +COPY entrypoint /entrypoint + +RUN chmod 755 /healthcheck /entrypoint + +CMD ["/entrypoint"] +EOF + + run_podman build -t $imagename ${PODMAN_TMPDIR} +} + # END miscellaneous tools ############################################################################### diff --git a/test/system/helpers.systemd.bash b/test/system/helpers.systemd.bash index d9abc087d..afbab6e08 100644 --- a/test/system/helpers.systemd.bash +++ b/test/system/helpers.systemd.bash @@ -32,3 +32,17 @@ journalctl() { systemd-run() { command systemd-run $_DASHUSER "$@"; } + +install_kube_template() { + # If running from a podman source directory, build and use the source + # version of the play-kube-@ unit file + unit_name="podman-kube@.service" + unit_file="contrib/systemd/system/${unit_name}" + if [[ -e ${unit_file}.in ]]; then + echo "# [Building & using $unit_name from source]" >&3 + # Force regenerating unit file (existing one may have /usr/bin path) + rm -f $unit_file + BINDIR=$(dirname $PODMAN) make $unit_file + cp $unit_file $UNIT_DIR/$unit_name + fi +} diff --git a/test/testvol/main.go b/test/testvol/main.go index dd4ba642d..ab26e2df0 100644 --- a/test/testvol/main.go +++ b/test/testvol/main.go @@ -80,7 +80,7 @@ func startServer(socketPath string) error { if config.path == "" { path, err := ioutil.TempDir("", "test_volume_plugin") if err != nil { - return fmt.Errorf("error getting directory for plugin: %w", err) + return fmt.Errorf("getting directory for plugin: %w", err) } config.path = path } else { @@ -98,7 +98,7 @@ func startServer(socketPath string) error { server := volume.NewHandler(handle) if err := server.ServeUnix(socketPath, 0); err != nil { - return fmt.Errorf("error starting server: %w", err) + return fmt.Errorf("starting server: %w", err) } return nil } @@ -161,7 +161,7 @@ func (d *DirDriver) Create(opts *volume.CreateRequest) error { volPath := filepath.Join(d.volumesPath, opts.Name) if err := os.Mkdir(volPath, 0755); err != nil { - return fmt.Errorf("error making volume directory: %w", err) + return fmt.Errorf("making volume directory: %w", err) } newVol.path = volPath @@ -240,7 +240,7 @@ func (d *DirDriver) Remove(req *volume.RemoveRequest) error { delete(d.volumes, req.Name) if err := os.RemoveAll(vol.path); err != nil { - return fmt.Errorf("error removing mountpoint of volume %s: %w", req.Name, err) + return fmt.Errorf("removing mountpoint of volume %s: %w", req.Name, err) } logrus.Debugf("Removed volume %s", req.Name) diff --git a/test/testvol/util.go b/test/testvol/util.go index b50bb3afb..b4961e097 100644 --- a/test/testvol/util.go +++ b/test/testvol/util.go @@ -25,5 +25,5 @@ func getPluginName(pathOrName string) string { func getPlugin(sockNameOrPath string) (*plugin.VolumePlugin, error) { path := getSocketPath(sockNameOrPath) name := getPluginName(sockNameOrPath) - return plugin.GetVolumePlugin(name, path, 0) + return plugin.GetVolumePlugin(name, path, nil, nil) } diff --git a/test/upgrade/test-upgrade.bats b/test/upgrade/test-upgrade.bats index 5efe05d49..dca97e324 100644 --- a/test/upgrade/test-upgrade.bats +++ b/test/upgrade/test-upgrade.bats @@ -345,10 +345,10 @@ failed | exited | 17 @test "rm a stopped container" { run_podman rm myfailedcontainer - is "$output" "[0-9a-f]\\{64\\}" "podman rm myfailedcontainer" + is "$output" "myfailedcontainer" "podman rm myfailedcontainer" run_podman rm mydonecontainer - is "$output" "[0-9a-f]\\{64\\}" "podman rm mydonecontainer" + is "$output" "mydonecontainer" "podman rm mydonecontainer" } diff --git a/test/utils/utils.go b/test/utils/utils.go index 9c2a63c81..19b287ae1 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -225,7 +225,7 @@ func (p *PodmanTest) WaitContainerReady(id string, expStr string, timeout int, s return false } - if strings.Contains(s.OutputToString(), expStr) { + if strings.Contains(s.OutputToString(), expStr) || strings.Contains(s.ErrorToString(), expStr) { return true } time.Sleep(time.Duration(step) * time.Second) |