diff options
Diffstat (limited to 'test')
86 files changed, 1470 insertions, 241 deletions
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index c105a9278..7b500bf57 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -12,6 +12,8 @@ iid=$(jq -r '.[0].Id' <<<"$output") t GET libpod/images/$iid/exists 204 t GET libpod/images/$PODMAN_TEST_IMAGE_NAME/exists 204 +t GET libpod/images/${iid}abcdef/exists 404 \ + .cause="failed to find image ${iid}abcdef" # FIXME: compare to actual podman info t GET libpod/images/json 200 \ diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 5c35edf2b..decdc4754 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -5,8 +5,10 @@ # WORKDIR=/data ENV_WORKDIR_IMG=quay.io/libpod/testimage:20200929 +MultiTagName=localhost/test/testformultitag:tag podman pull $IMAGE &>/dev/null +podman tag $IMAGE $MultiTagName podman pull $ENV_WORKDIR_IMG &>/dev/null # Unimplemented #t POST libpod/containers/create '' 201 'sdf' @@ -201,6 +203,15 @@ t GET 'containers/json?limit=0&all=1' 200 \ .[0].Id~[0-9a-f]\\{64\\} \ .[1].Id~[0-9a-f]\\{64\\} +t GET containers/json?limit=2 200 length=2 + +# Filter with two ids should return both container +t GET "containers/json?filters=%7B%22id%22%3A%5B%22${cid}%22%2C%22${cid_top}%22%5D%7D&all=1" 200 length=2 +# Filter with two ids and status running should return only 1 container +t GET "containers/json?filters=%7B%22id%22%3A%5B%22${cid}%22%2C%22${cid_top}%22%5D%2C%22status%22%3A%5B%22running%22%5D%7D&all=1" 200 \ + length=1 \ + .[0].Id=${cid_top} + t POST containers/${cid_top}/stop "" 204 t DELETE containers/$cid 204 @@ -216,4 +227,13 @@ t GET containers/$cid/json 200 \ t DELETE containers/$cid 204 +# when the image had multi tags, the container's Image should be correct +# Fixes https://github.com/containers/podman/issues/8547 +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} +t DELETE containers/$cid 204 +t DELETE images/${MultiTagName}?force=true 200 # vim: filetype=sh diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index aa167a97a..33f4ea37f 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -20,6 +20,18 @@ t POST libpod/volumes/create \ .Labels.testlabel=testonly \ .Options.type=tmpfs \ .Options.o=nodev,noexec +t POST libpod/volumes/create \ + '"Name":"foo3","Label":{"testlabel":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo3 \ + .Labels.testlabel="" \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec +t POST libpod/volumes/create \ + '"Name":"foo4","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo4 \ + .Labels.testlabel1=testonly \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec # Negative test # We have created a volume named "foo1" @@ -37,15 +49,21 @@ t GET libpod/volumes/json 200 \ t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22foo1%22%5D%7D 200 length=1 .[0].Name=foo1 # -G --data-urlencode 'filters={"name":["foo1","foo2"]}' t GET libpod/volumes/json?filters=%7B%22name%22%3A%20%5B%22foo1%22%2C%20%22foo2%22%5D%7D 200 length=2 .[0].Name=foo1 .[1].Name=foo2 -# -G --data-urlencode 'filters={"name":["notexist"]}' -t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22notexists%22%5D%7D 200 length=0 +# -G --data-urlencode 'filters={"name":["nonexistent"]}' +t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22nonexistent%22%5D%7D 200 length=0 +# -G --data-urlencode 'filters={"label":["testlabel"]}' +t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D 200 length=2 +# -G --data-urlencode 'filters={"label":["testlabel=testonly"]}' +t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel=testonly%22%5D%7D 200 length=1 +# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}' +t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D 200 length=1 ## inspect volume t GET libpod/volumes/foo1/json 200 \ .Name=foo1 \ .Mountpoint=$volumepath/foo1/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* -t GET libpod/volumes/notexist/json 404 \ +t GET libpod/volumes/nonexistent/json 404 \ .cause="no such volume" \ .message~.* \ .response=404 @@ -60,6 +78,18 @@ t DELETE libpod/volumes/foo1 404 \ .message~.* \ .response=404 +## Prune volumes with label matching 'testlabel1=testonly' +# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}' +t POST libpod/volumes/prune?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D "" 200 +# -G --data-urlencode 'filters={"label":["testlabel1=testonly"]}' +t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D 200 length=0 + +## Prune volumes with label matching 'testlabel' +# -G --data-urlencode 'filters={"label":["testlabel"]}' +t POST libpod/volumes/prune?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D "" 200 +# -G --data-urlencode 'filters={"label":["testlabel"]}' +t GET libpod/volumes/json?filters=%7B%22label%22:%5B%22testlabel%22%5D%7D 200 length=0 + ## Prune volumes t POST libpod/volumes/prune "" 200 #After prune volumes, there should be no volume existing diff --git a/test/apiv2/35-networks.at b/test/apiv2/35-networks.at index 0ce56ee3c..5327bd076 100644 --- a/test/apiv2/35-networks.at +++ b/test/apiv2/35-networks.at @@ -50,7 +50,13 @@ t GET networks?filters=%7B%22dangling%22%3A%5B%221%22%5D%7D 500 \ # network inspect docker t GET networks/a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 200 \ .Name=network1 \ -.Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 +.Id=a7662f44d65029fd4635c91feea3d720a57cef52e2a9fcc7772b69072cc1ccd1 \ +.Scope=local + +# network create docker +t POST networks/create '"Name":"net3","IPAM":{"Config":[]}' 201 +# network delete docker +t DELETE networks/net3 204 # clean the network t DELETE libpod/networks/network1 200 \ diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at new file mode 100644 index 000000000..44cd05a13 --- /dev/null +++ b/test/apiv2/45-system.at @@ -0,0 +1,67 @@ +# -*- sh -*- +# +# system related tests +# + +## ensure system is clean +t POST 'libpod/system/prune?volumes=true&all=true' params='' 200 + +## podman system df +t GET system/df 200 '{"LayersSize":0,"Images":[],"Containers":[],"Volumes":[],"BuildCache":[],"BuilderSize":0}' +t GET libpod/system/df 200 '{"Images":[],"Containers":[],"Volumes":[]}' + +# Create volume. We expect df to report this volume next invocation of system/df +t GET libpod/info 200 +volumepath=$(jq -r ".store.volumePath" <<<"$output") +t POST libpod/volumes/create name=foo1 201 \ + .Name=foo1 \ + .Driver=local \ + .Mountpoint=$volumepath/foo1/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels={} \ + .Options=null + +t GET system/df 200 '.Volumes[0].Name=foo1' + +t GET libpod/system/df 200 '.Volumes[0].VolumeName=foo1' + +# Create two more volumes to test pruneing +t POST libpod/volumes/create \ + '"Name":"foo2","Label":{"testlabel1":""},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo2 \ + .Driver=local \ + .Mountpoint=$volumepath/foo2/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1="" \ + .Options.o=nodev,noexec + +t POST libpod/volumes/create \ + '"Name":"foo3","Label":{"testlabel1":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ + .Name=foo3 \ + .Driver=local \ + .Mountpoint=$volumepath/foo3/_data \ + .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ + .Labels.testlabel1=testonly \ + .Options.o=nodev,noexec + +t GET system/df 200 '.Volumes | length=3' +t GET libpod/system/df 200 '.Volumes | length=3' + +# Prune volumes + +# -G --data-urlencode 'volumes=true&filters={"label":["testlabel1=idontmatch"]}' +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=idontmatch%22%5D%7D' params='' 200 + +# nothing should have been pruned +t GET system/df 200 '.Volumes | length=3' +t GET libpod/system/df 200 '.Volumes | length=3' + +# -G --data-urlencode 'volumes=true&filters={"label":["testlabel1=testonly"]}' +# only foo3 should be pruned because of filter +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1=testonly%22%5D%7D' params='' 200 .VolumePruneReports[0].Id=foo3 +# only foo2 should be pruned because of filter +t POST 'libpod/system/prune?volumes=true&filters=%7B%22label%22:%5B%22testlabel1%22%5D%7D' params='' 200 .VolumePruneReports[0].Id=foo2 +# foo1, the last remaining volume should be pruned without any filters applied +t POST 'libpod/system/prune?volumes=true' params='' 200 .VolumePruneReports[0].Id=foo1 + +# TODO add other system prune tests for pods / images diff --git a/test/compose/README.md b/test/compose/README.md new file mode 100644 index 000000000..863decf2c --- /dev/null +++ b/test/compose/README.md @@ -0,0 +1,47 @@ +Tests for docker-compose +======================== + +This directory contains tests for docker-compose under podman. + +Each subdirectory must contain one docker-compose.yml file along with +all necessary infrastructure for it (e.g. Containerfile, any files +to be copied into the container, and so on. + +The `test-compose` script will, for each test subdirectory: + +* set up a fresh podman root under an empty working directory; +* run a podman server rooted therein; +* cd to the test subdirectory, and run `docker-compose up -d`; +* source `tests.sh`; +* run `docker-compose down`. + +As a special case, `setup.sh` and `teardown.sh` in the test directory +will contain commands to be executed prior to `docker-compose up` and +after `docker-compose down` respectively. + +tests.sh will probably contain commands of the form + + test_port 12345 = 'hello there' + +Where 12345 is the port to curl to; '=' checks equality, '~' uses `expr` +to check substrings; and 'hello there' is a string to look for in +the curl results. + +Usage: + + $ sudo test/compose/test-compose [pattern] + +By default, all subdirs will be run. If given a pattern, only those +subdirectories matching 'pattern' will be run. + +If `$COMPOSE_WAIT` is set, `test-compose` will pause before running +`docker-compose down`. This can be helpful for you to debug failing tests: + + $ env COMPOSE_WAIT=1 sudo --preserve-env=COMPOSE_WAIT test/compose/test-compose + +Then, in another window, + + # ls -lt /var/tmp/ + # X=/var/tmp/test-compose.tmp.XXXXXX <--- most recent results of above + # podman --root $X/root --runroot $X/runroot ps -a + # podman --root $X/root --runroot $X/runroot logs -l diff --git a/test/compose/env_and_volume/README.md b/test/compose/env_and_volume/README.md new file mode 100644 index 000000000..e7d74976b --- /dev/null +++ b/test/compose/env_and_volume/README.md @@ -0,0 +1,12 @@ +environment variable and volume +=============== + +This test creates two containers both of which are running flask. The first container has +an environment variable called PODMAN_MSG. That container pipes the contents of PODMAN_MSG +to a file on a shared volume between the containers. The second container then reads the +file are returns the PODMAN_MSG value via flask (http). + +Validation +------------ +* curl http://localhost:5000 and verify message +* curl http://localhost:5001 and verify message diff --git a/test/compose/env_and_volume/docker-compose.yml b/test/compose/env_and_volume/docker-compose.yml new file mode 100644 index 000000000..df906e170 --- /dev/null +++ b/test/compose/env_and_volume/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' +services: + writer: + environment: + - PODMAN_MSG=podman_rulez + build: write + ports: + - '5000:5000' + volumes: + - data:/data + reader: + build: read + ports: + - '5001:5000' + volumes: + - data:/data +volumes: + data: diff --git a/test/compose/env_and_volume/read/Dockerfile b/test/compose/env_and_volume/read/Dockerfile new file mode 100644 index 000000000..8d5c45401 --- /dev/null +++ b/test/compose/env_and_volume/read/Dockerfile @@ -0,0 +1,5 @@ +FROM quay.io/libpod/podman_python +WORKDIR /app +COPY . /app +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/test/compose/env_and_volume/read/app.py b/test/compose/env_and_volume/read/app.py new file mode 100644 index 000000000..71fbbb26a --- /dev/null +++ b/test/compose/env_and_volume/read/app.py @@ -0,0 +1,10 @@ +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + f = open("/data/message", "r") + return f.read() + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/test/compose/env_and_volume/tests.sh b/test/compose/env_and_volume/tests.sh new file mode 100644 index 000000000..a4c8bed30 --- /dev/null +++ b/test/compose/env_and_volume/tests.sh @@ -0,0 +1,4 @@ +# -*- bash -*- + +test_port 5000 = "done" +test_port 5001 = "podman_rulez" diff --git a/test/compose/env_and_volume/write/Dockerfile b/test/compose/env_and_volume/write/Dockerfile new file mode 100644 index 000000000..8d5c45401 --- /dev/null +++ b/test/compose/env_and_volume/write/Dockerfile @@ -0,0 +1,5 @@ +FROM quay.io/libpod/podman_python +WORKDIR /app +COPY . /app +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/test/compose/env_and_volume/write/app.py b/test/compose/env_and_volume/write/app.py new file mode 100644 index 000000000..b6ad6fe63 --- /dev/null +++ b/test/compose/env_and_volume/write/app.py @@ -0,0 +1,13 @@ +from flask import Flask +import os +app = Flask(__name__) + +@app.route('/') +def hello(): + f = open("/data/message", "w") + f.write(os.getenv("PODMAN_MSG")) + f.close() + return "done" + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/test/compose/images/README.md b/test/compose/images/README.md new file mode 100644 index 000000000..f25fbdc24 --- /dev/null +++ b/test/compose/images/README.md @@ -0,0 +1,5 @@ +images +====== + +Use these directories for images that are needed for the compose testing. These +images should be then pushed to `quay.io/libpod` for consumption. diff --git a/test/compose/images/podman-python/Containerfile b/test/compose/images/podman-python/Containerfile new file mode 100644 index 000000000..47f90afaa --- /dev/null +++ b/test/compose/images/podman-python/Containerfile @@ -0,0 +1,3 @@ +FROM alpine +WORKDIR /app +RUN apk update && apk add py3-pip && pip3 install flask && rm -fr /var/cache/apk/* diff --git a/test/compose/mount_and_label/README.md b/test/compose/mount_and_label/README.md new file mode 100644 index 000000000..623b38cac --- /dev/null +++ b/test/compose/mount_and_label/README.md @@ -0,0 +1,9 @@ +mount and label +=============== + +This test creates a container with a mount (not volume) and also adds a label to the container. + +Validation +------------ +* curl http://localhost:5000 and verify message +* inspect the container to make the label exists on it diff --git a/test/compose/mount_and_label/docker-compose.yml b/test/compose/mount_and_label/docker-compose.yml new file mode 100644 index 000000000..112d7e134 --- /dev/null +++ b/test/compose/mount_and_label/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + web: + build: frontend + ports: + - '5000:5000' + volumes: + - /tmp/data:/data:ro + labels: + - "io.podman=the_best" diff --git a/test/compose/mount_and_label/frontend/Dockerfile b/test/compose/mount_and_label/frontend/Dockerfile new file mode 100644 index 000000000..8d5c45401 --- /dev/null +++ b/test/compose/mount_and_label/frontend/Dockerfile @@ -0,0 +1,5 @@ +FROM quay.io/libpod/podman_python +WORKDIR /app +COPY . /app +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/test/compose/mount_and_label/frontend/app.py b/test/compose/mount_and_label/frontend/app.py new file mode 100644 index 000000000..bd2794d94 --- /dev/null +++ b/test/compose/mount_and_label/frontend/app.py @@ -0,0 +1,10 @@ +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + f = open("/data/message") + return f.read() + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/test/compose/mount_and_label/setup.sh b/test/compose/mount_and_label/setup.sh new file mode 100644 index 000000000..8633d65d5 --- /dev/null +++ b/test/compose/mount_and_label/setup.sh @@ -0,0 +1,2 @@ +mkdir -p /tmp/data +echo "Podman rulez!" > /tmp/data/message diff --git a/test/compose/mount_and_label/teardown.sh b/test/compose/mount_and_label/teardown.sh new file mode 100644 index 000000000..57867c28a --- /dev/null +++ b/test/compose/mount_and_label/teardown.sh @@ -0,0 +1 @@ +rm /tmp/data/message diff --git a/test/compose/mount_and_label/tests.sh b/test/compose/mount_and_label/tests.sh new file mode 100644 index 000000000..07ff089b5 --- /dev/null +++ b/test/compose/mount_and_label/tests.sh @@ -0,0 +1,4 @@ +# -*- bash -*- + +test_port 5000 = "Podman rulez!" +podman container inspect -l --format '{{.Config.Labels}}' | grep "the_best" diff --git a/test/compose/port_map_diff_port/README.md b/test/compose/port_map_diff_port/README.md new file mode 100644 index 000000000..13ece72ad --- /dev/null +++ b/test/compose/port_map_diff_port/README.md @@ -0,0 +1,9 @@ +port map on different port +=============== + +This test creates a container that runs flask on different ports for the container +and the host + +Validation +------------ +* curl http://localhost:5001 and verify message diff --git a/test/compose/port_map_diff_port/docker-compose.yml b/test/compose/port_map_diff_port/docker-compose.yml new file mode 100644 index 000000000..3003c52f4 --- /dev/null +++ b/test/compose/port_map_diff_port/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + web: + build: frontend + ports: + - '5001:5000' diff --git a/test/compose/port_map_diff_port/frontend/Dockerfile b/test/compose/port_map_diff_port/frontend/Dockerfile new file mode 100644 index 000000000..8d5c45401 --- /dev/null +++ b/test/compose/port_map_diff_port/frontend/Dockerfile @@ -0,0 +1,5 @@ +FROM quay.io/libpod/podman_python +WORKDIR /app +COPY . /app +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/test/compose/port_map_diff_port/frontend/app.py b/test/compose/port_map_diff_port/frontend/app.py new file mode 100644 index 000000000..895556a89 --- /dev/null +++ b/test/compose/port_map_diff_port/frontend/app.py @@ -0,0 +1,9 @@ +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + return "Podman rulez!" + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/test/compose/port_map_diff_port/tests.sh b/test/compose/port_map_diff_port/tests.sh new file mode 100644 index 000000000..5a468aadc --- /dev/null +++ b/test/compose/port_map_diff_port/tests.sh @@ -0,0 +1,3 @@ +# -*- bash -*- + +test_port 5001 = "Podman rulez!" diff --git a/test/compose/setup.sh.example b/test/compose/setup.sh.example new file mode 100644 index 000000000..9004b1e76 --- /dev/null +++ b/test/compose/setup.sh.example @@ -0,0 +1,3 @@ +# -*- bash -*- + +export ENV_PASSTHRU=$(random_string 20) diff --git a/test/compose/simple_port_map/README.md b/test/compose/simple_port_map/README.md new file mode 100644 index 000000000..f28d71c3e --- /dev/null +++ b/test/compose/simple_port_map/README.md @@ -0,0 +1,9 @@ +simple port map to host +=============== + +This test creates a container that runs flask on and maps to the same +host port + +Validation +------------ +* curl http://localhost:5000 and verify message diff --git a/test/compose/simple_port_map/docker-compose.yml b/test/compose/simple_port_map/docker-compose.yml new file mode 100644 index 000000000..e7eab1047 --- /dev/null +++ b/test/compose/simple_port_map/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3' +services: + web: + build: frontend + ports: + - '5000:5000' diff --git a/test/compose/simple_port_map/frontend/Dockerfile b/test/compose/simple_port_map/frontend/Dockerfile new file mode 100644 index 000000000..2595828ff --- /dev/null +++ b/test/compose/simple_port_map/frontend/Dockerfile @@ -0,0 +1,6 @@ +FROM alpine +WORKDIR /app +RUN apk update && apk add py3-pip && pip3 install flask +COPY . /app +ENTRYPOINT ["python3"] +CMD ["app.py"] diff --git a/test/compose/simple_port_map/frontend/app.py b/test/compose/simple_port_map/frontend/app.py new file mode 100644 index 000000000..e4f84068c --- /dev/null +++ b/test/compose/simple_port_map/frontend/app.py @@ -0,0 +1,10 @@ +from flask import Flask +import os +app = Flask(__name__) + +@app.route('/') +def hello(): + return "Podman rulez!" + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/test/compose/simple_port_map/tests.sh b/test/compose/simple_port_map/tests.sh new file mode 100644 index 000000000..ccb2b6a3d --- /dev/null +++ b/test/compose/simple_port_map/tests.sh @@ -0,0 +1,3 @@ +# -*- bash -*- + +test_port 5000 = "Podman rulez!" diff --git a/test/compose/teardown.sh.example b/test/compose/teardown.sh.example new file mode 100644 index 000000000..3f8153fa0 --- /dev/null +++ b/test/compose/teardown.sh.example @@ -0,0 +1,4 @@ +# -*- bash -*- + +# FIXME: this is completely unnecessary; it's just an example of a teardown +unset ENV_PASSTHRU diff --git a/test/compose/test-compose b/test/compose/test-compose new file mode 100755 index 000000000..9558fbf58 --- /dev/null +++ b/test/compose/test-compose @@ -0,0 +1,345 @@ +#!/usr/bin/env bash +# +# Usage: test-compose [testname] +# +ME=$(basename $0) + +############################################################################### +# BEGIN stuff you can but probably shouldn't customize + +# Directory where this script and all subtests live +TEST_ROOTDIR=$(realpath $(dirname $0)) + +# Podman executable +PODMAN_BIN=$(realpath $TEST_ROOTDIR/../../bin)/podman + +# Local path to docker socket (we will add the unix:/ prefix when we need it) +DOCKER_SOCK=/var/run/docker.sock + +# END stuff you can but probably shouldn't customize +############################################################################### +# BEGIN setup + +export TMPDIR=${TMPDIR:-/var/tmp} +WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX) + +# Log of all HTTP requests and responses; always make '.log' point to latest +LOGBASE=${TMPDIR}/$ME.log +LOG=${LOGBASE}.$(date +'%Y%m%dT%H%M%S') +ln -sf $LOG $LOGBASE + +# Keep track of test count and failures in files, not variables, because +# variables don't carry back up from subshells. +testcounter_file=$WORKDIR/.testcounter +failures_file=$WORKDIR/.failures + +echo 0 >$testcounter_file +echo 0 >$failures_file + +# END setup +############################################################################### +# BEGIN infrastructure code - the helper functions used in tests themselves + +######### +# die # Exit error with a message to stderr +######### +function die() { + echo "$ME: $*" >&2 + exit 1 +} + +######## +# is # Simple comparison +######## +function is() { + local actual=$1 + local expect=$2 + local testname=$3 + + if [[ $actual = $expect ]]; 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 +########## +function like() { + local actual=$1 + local expect=$2 + local testname=$3 + + # "is" (equality) is a subset of "like", but one that expr fails on if + # the expected result has shell-special characters like '['. Treat it + # as a special case. + + if [[ "$actual" = "$expect" ]]; then + _show_ok 1 "$testname=$expect" + return + fi + + if expr "$actual" : ".*$expect" &>/dev/null; then + # On success, include expected value; this helps readers understand + _show_ok 1 "$testname ('$actual') ~ $expect" + return + fi + _show_ok 0 "$testname" "~ $expect" "$actual" +} + +############## +# _show_ok # Helper for is() and like(): displays 'ok' or 'not ok' +############## +function _show_ok() { + local ok=$1 + local testname=$2 + + # If output is a tty, colorize pass/fail + local red= + local green= + local reset= + local bold= + if [ -t 1 ]; then + red='\e[31m' + green='\e[32m' + reset='\e[0m' + bold='\e[1m' + fi + + _bump $testcounter_file + count=$(<$testcounter_file) + + # "skip" is a special case of "ok". Assume that our caller has included + # the magical '# skip - reason" comment string. + if [[ $ok == "skip" ]]; then + # colon-plus: replace green with yellow, but only if green is non-null + green="${green:+\e[33m}" + ok=1 + fi + if [ $ok -eq 1 ]; then + echo -e "${green}ok $count $testname${reset}" + echo "ok $count $testname" >>$LOG + return + fi + + # Failed + local expect=$3 + local actual=$4 + printf "${red}not ok $count $testname${reset}\n" + printf "${red}# expected: %s${reset}\n" "$expect" + printf "${red}# actual: ${bold}%s${reset}\n" "$actual" + + echo "not ok $count $testname" >>$LOG + echo " expected: $expect" >>$LOG + + _bump $failures_file +} + +########### +# _bump # Increment a counter in a file +########### +function _bump() { + local file=$1 + + count=$(<$file) + echo $(( $count + 1 )) >| $file +} + +############### +# test_port # Run curl against a port, check results against expectation +############### +function test_port() { + local port="$1" # e.g. 5000 + local op="$2" # '=' or '~' + local expect="$3" # what to expect from curl output + + local actual=$(curl --retry 5 --retry-connrefused -s http://127.0.0.1:$port/) + local curl_rc=$? + if [ $curl_rc -ne 0 ]; then + _show_ok 0 "$testname - curl failed with status $curl_rc" +### docker-compose down >>$logfile 2>&1 +### exit 1 + fi + + case "$op" in + '=') is "$actual" "$expect" "$testname : port $port" ;; + '~') like "$actual" "$expect" "$testname : port $port" ;; + *) die "Invalid operator '$op'" ;; + esac +} + + +################### +# start_service # Run the socket listener +################### +service_pid= +function start_service() { + test -x $PODMAN_BIN || die "Not found: $PODMAN_BIN" + + # FIXME: use ${testname} subdir but we can't: 50-char limit in runroot + rm -rf $WORKDIR/{root,runroot,cni} + mkdir --mode 0755 $WORKDIR/{root,runroot,cni} + chcon --reference=/var/lib/containers $WORKDIR/root + cp /etc/cni/net.d/*podman*conflist $WORKDIR/cni/ + + $PODMAN_BIN \ + --root $WORKDIR/root \ + --runroot $WORKDIR/runroot \ + --cgroup-manager=systemd \ + --cni-config-dir $WORKDIR/cni \ + system service \ + --time 0 unix:/$DOCKER_SOCK \ + &> $WORKDIR/server.log & + service_pid=$! + + # Wait (FIXME: how do we test the socket?) + local _timeout=5 + while [ $_timeout -gt 0 ]; do + # FIXME: should we actually try a read or write? + test -S $DOCKER_SOCK && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + cat $WORKDIR/server.log + die "Timed out waiting for service" +} + +############ +# podman # Needed by some test scripts to invoke the actual podman binary +############ +function podman() { + echo "\$ podman $*" >>$WORKDIR/output.log + $PODMAN_BIN \ + --root $WORKDIR/root \ + --runroot $WORKDIR/runroot \ + "$@" >>$WORKDIR/output.log 2>&1 +} + +################### +# random_string # Returns a pseudorandom human-readable string +################### +function random_string() { + # Numeric argument, if present, is desired length of string + local length=${1:-10} + + head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length +} + +# END infrastructure code +############################################################################### +# BEGIN sanity checks + +for tool in curl docker-compose; do + type $tool &>/dev/null || die "$ME: Required tool '$tool' not found" +done + +# END sanity checks +############################################################################### +# BEGIN entry handler (subtest invoker) + +# Identify the tests to run. If called with args, use those as globs. +tests_to_run=() +if [ -n "$*" ]; then + shopt -s nullglob + for i; do + match=(${TEST_ROOTDIR}/*${i}*/docker-compose.yml) + if [ ${#match} -eq 0 ]; then + die "No match for $TEST_ROOTDIR/*$i*.curl" + fi + tests_to_run+=("${match[@]}") + done + shopt -u nullglob +else + tests_to_run=(${TEST_ROOTDIR}/*/docker-compose.yml) +fi + +# Too hard to precompute the number of tests; just spit it out at the end. +n_tests=0 +for t in ${tests_to_run[@]}; do + testdir="$(dirname $t)" + testname="$(basename $testdir)" + + if [ -e $test_dir/SKIP ]; then + local reason="$(<$test_dir/SKIP)" + if [ -n "$reason" ]; then + reason=" - $reason" + fi + _show_ok skip "$testname # skip$reason" + continue + fi + + start_service + + logfile=$WORKDIR/$testname.log + ( + cd $testdir || die "Cannot cd $testdir" + + # setup file may be used for creating temporary directories/files. + # We source it so that envariables defined in it will get back to us. + if [ -e setup.sh ]; then + . setup.sh + fi + if [ -e teardown.sh ]; then + trap '. teardown.sh' 0 + fi + + docker-compose up -d &> $logfile + docker_compose_rc=$? + if [[ $docker_compose_rc -ne 0 ]]; then + _show_ok 0 "$testname - up" "[ok]" "status=$docker_compose_rc" + sed -e 's/^/# /' <$logfile + docker-compose down >>$logfile 2>&1 # No status check here + exit 1 + fi + _show_ok 1 "$testname - up" + + # Run tests. This is likely to be a series of 'test_port' checks + # but may also include podman commands to inspect labels, state + if [ -e tests.sh ]; then + . tests.sh + fi + # FIXME: if any tests fail, try 'podman logs' on container? + + if [ -n "$COMPOSE_WAIT" ]; then + echo -n "Pausing due to \$COMPOSE_WAIT. Press ENTER to continue: " + read keepgoing + fi + + # Done. Clean up. + docker-compose down &> $logfile + rc=$? + if [[ $rc -eq 0 ]]; then + _show_ok 1 "$testname - down" + else + _show_ok 0 "$testname - down" "[ok]" "rc=$rc" + # FIXME: show error + fi + ) + + kill $service_pid + wait $service_pid + + # FIXME: otherwise we get EBUSY + umount $WORKDIR/root/overlay &>/dev/null + + # FIXME: run 'podman ps'? +# rm -rf $WORKDIR/${testname} +done + +# END entry handler +############################################################################### + +# Clean up + +test_count=$(<$testcounter_file) +failure_count=$(<$failures_file) + +#if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then +# rm -rf $WORKDIR +#fi + +echo "1..${test_count}" + +exit $failure_count diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index d7bbdc633..a076ada6b 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -62,7 +62,7 @@ type PodmanTestIntegration struct { var LockTmpDir string -// PodmanSessionIntegration sturct for command line session +// PodmanSessionIntegration struct for command line session type PodmanSessionIntegration struct { *PodmanSession } @@ -121,7 +121,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { } } - // Pull cirros but dont put it into the cache + // Pull cirros but don't put it into the cache pullImages := []string{cirros, fedoraToolbox} pullImages = append(pullImages, CACHE_IMAGES...) for _, image := range pullImages { diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 28672cfc6..719ac9fac 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -82,6 +82,7 @@ var _ = Describe("Podman run", func() { }) It("podman Capabilities in containers.conf", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cap := podmanTest.Podman([]string{"run", ALPINE, "grep", "CapEff", "/proc/self/status"}) cap.WaitWithDefaultTimeout() Expect(cap.ExitCode()).To(Equal(0)) @@ -121,6 +122,7 @@ var _ = Describe("Podman run", func() { }) verifyNSHandling := func(nspath, option string) { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") os.Setenv("CONTAINERS_CONF", "config/containers-ns.conf") if IsRemote() { podmanTest.RestartRemoteService() diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index 33908b60e..f81323847 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -24,7 +24,6 @@ var _ = Describe("Podman cp", func() { ) BeforeEach(func() { - SkipIfRemote("FIXME: Podman-remote cp needs to work") tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -61,7 +60,7 @@ var _ = Describe("Podman cp", func() { // Copy TO the container. - // Cannot copy to a non-existent path (note the trailing "/"). + // Cannot copy to a nonexistent path (note the trailing "/"). session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo/"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index aaf089d97..a4931ff2d 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -233,7 +233,7 @@ var _ = Describe("Podman create", func() { Expect(err).To(BeNil()) defer os.RemoveAll(tmpDir) - podName := "rudoplh" + podName := "rudolph" ctrName := "prancer" podIDFile := tmpDir + "pod-id-file" @@ -337,8 +337,8 @@ var _ = Describe("Podman create", func() { Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) }) - It("podman create --authfile with nonexist authfile", func() { - session := podmanTest.Podman([]string{"create", "--authfile", "/tmp/nonexist", "--name=foo", ALPINE}) + It("podman create --authfile with nonexistent authfile", func() { + session := podmanTest.Podman([]string{"create", "--authfile", "/tmp/nonexistent", "--name=foo", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).To(Not(Equal(0))) }) @@ -655,7 +655,7 @@ var _ = Describe("Podman create", func() { session = podmanTest.Podman([]string{"create", "--platform=linux/arm64", "--override-os", "windows", ALPINE}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) - expectedError = "--platform option can not be specified with --overide-arch or --override-os" + expectedError = "--platform option can not be specified with --override-arch or --override-os" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"create", "-q", "--platform=linux/arm64", ALPINE}) diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index b37bd584e..0c7a1bd66 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -5,9 +5,11 @@ import ( "fmt" "os" "strings" + "sync" "time" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -115,10 +117,7 @@ var _ = Describe("Podman events", func() { SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) - test := podmanTest.Podman([]string{"events", "--help"}) - test.WaitWithDefaultTimeout() - fmt.Println(test.OutputToStringArray()) - result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1h"}) + result := podmanTest.Podman([]string{"events", "--stream=false", "--until", "1h"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(BeZero()) }) @@ -154,4 +153,40 @@ var _ = Describe("Podman events", func() { Expect(eventsMap).To(HaveKey("Status")) }) + + It("podman events --until future", func() { + name1 := stringid.GenerateNonCryptoID() + name2 := stringid.GenerateNonCryptoID() + name3 := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"create", "--name", name1, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer GinkgoRecover() + defer wg.Done() + + // wait 2 seconds to be sure events is running + time.Sleep(time.Second * 2) + session = podmanTest.Podman([]string{"create", "--name", name2, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"create", "--name", name3, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }() + + // unix timestamp in 10 seconds + until := time.Now().Add(time.Second * 10).Unix() + result := podmanTest.Podman([]string{"events", "--since", "30s", "--until", fmt.Sprint(until)}) + result.Wait(11) + Expect(result.ExitCode()).To(BeZero()) + Expect(result.OutputToString()).To(ContainSubstring(name1)) + Expect(result.OutputToString()).To(ContainSubstring(name2)) + Expect(result.OutputToString()).To(ContainSubstring(name3)) + + wg.Wait() + }) }) diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index f61f52589..18737105e 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -119,6 +119,21 @@ var _ = Describe("Podman exec", func() { Expect(session.ExitCode()).To(Equal(100)) }) + It("podman exec --privileged", func() { + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) + }) + It("podman exec terminal doesn't hang", func() { setup := podmanTest.Podman([]string{"run", "-dti", "--name", "test1", fedoraMinimal, "sleep", "+Inf"}) setup.WaitWithDefaultTimeout() diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go index 7ff5d4207..480bfe5fc 100644 --- a/test/e2e/exists_test.go +++ b/test/e2e/exists_test.go @@ -38,7 +38,6 @@ var _ = Describe("Podman image|container exists", func() { Expect(session).Should(Exit(0)) }) It("podman image exists in local storage by short name", func() { - Skip("FIXME-8165: shortnames don't seem to work with quay (#8176)") session := podmanTest.Podman([]string{"image", "exists", "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 0950a9321..239817e6c 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -471,6 +471,7 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate kube multiple pods should fail", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", ALPINE, "top"}) pod1.WaitWithDefaultTimeout() Expect(pod1.ExitCode()).To(Equal(0)) diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 765844265..3f059300b 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -74,6 +74,9 @@ var _ = Describe("Podman generate systemd", func() { found, _ := session.GrepString(" stop -t 1234 ") Expect(found).To(BeTrue()) + + found, _ = session.GrepString("TimeoutStopSec=1294") + Expect(found).To(BeTrue()) }) It("podman generate systemd", func() { diff --git a/test/e2e/image_sign_test.go b/test/e2e/image_sign_test.go index c9041eaba..57739419c 100644 --- a/test/e2e/image_sign_test.go +++ b/test/e2e/image_sign_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" "os/exec" "path/filepath" @@ -58,4 +59,19 @@ var _ = Describe("Podman image sign", func() { _, err = os.Stat(filepath.Join(sigDir, "library")) Expect(err).To(BeNil()) }) + + It("podman sign --all multi-arch image", func() { + cmd := exec.Command("gpg", "--import", "sign/secret-key.asc") + err := cmd.Run() + Expect(err).To(BeNil()) + sigDir := filepath.Join(podmanTest.TempDir, "test-sign-multi") + err = os.MkdirAll(sigDir, os.ModePerm) + Expect(err).To(BeNil()) + session := podmanTest.Podman([]string{"image", "sign", "--all", "--directory", sigDir, "--sign-by", "foo@bar.com", "docker://library/alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + fInfos, err := ioutil.ReadDir(filepath.Join(sigDir, "library")) + Expect(err).To(BeNil()) + Expect(len(fInfos) > 1).To(BeTrue()) + }) }) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 281b2c313..2dab4858e 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -146,17 +146,17 @@ var _ = Describe("Podman images", func() { Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(8)) - retapline := podmanTest.Podman([]string{"images", "-f", "reference=a*pine"}) - retapline.WaitWithDefaultTimeout() - Expect(retapline).Should(Exit(0)) - Expect(len(retapline.OutputToStringArray())).To(Equal(6)) - Expect(retapline.LineInOutputContains("alpine")).To(BeTrue()) - - retapline = podmanTest.Podman([]string{"images", "-f", "reference=alpine"}) - retapline.WaitWithDefaultTimeout() - Expect(retapline).Should(Exit(0)) - Expect(len(retapline.OutputToStringArray())).To(Equal(6)) - Expect(retapline.LineInOutputContains("alpine")).To(BeTrue()) + retalpine := podmanTest.Podman([]string{"images", "-f", "reference=a*pine"}) + retalpine.WaitWithDefaultTimeout() + Expect(retalpine).Should(Exit(0)) + Expect(len(retalpine.OutputToStringArray())).To(Equal(6)) + Expect(retalpine.LineInOutputContains("alpine")).To(BeTrue()) + + retalpine = podmanTest.Podman([]string{"images", "-f", "reference=alpine"}) + retalpine.WaitWithDefaultTimeout() + Expect(retalpine).Should(Exit(0)) + Expect(len(retalpine.OutputToStringArray())).To(Equal(6)) + Expect(retalpine.LineInOutputContains("alpine")).To(BeTrue()) retnone := podmanTest.Podman([]string{"images", "-q", "-f", "reference=bogus"}) retnone.WaitWithDefaultTimeout() diff --git a/test/e2e/kill_test.go b/test/e2e/kill_test.go index 8a4828583..8b31cae72 100644 --- a/test/e2e/kill_test.go +++ b/test/e2e/kill_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" . "github.com/containers/podman/v2/test/utils" @@ -112,4 +113,58 @@ var _ = Describe("Podman kill", func() { Expect(result.ExitCode()).To(Equal(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) + + It("podman kill --cidfile", func() { + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "cid" + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToStringArray()[0] + + kill := podmanTest.Podman([]string{"kill", "--cidfile", tmpFile}) + kill.WaitWithDefaultTimeout() + Expect(kill.ExitCode()).To(BeZero()) + + wait := podmanTest.Podman([]string{"wait", "--condition", "exited", cid}) + wait.WaitWithDefaultTimeout() + Expect(wait.ExitCode()).To(BeZero()) + }) + + It("podman kill multiple --cidfile", func() { + tmpDir1, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile1 := tmpDir1 + "cid" + defer os.RemoveAll(tmpDir1) + + tmpDir2, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile2 := tmpDir2 + "cid" + defer os.RemoveAll(tmpDir2) + + session := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile1, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToStringArray()[0] + + session2 := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile2, ALPINE, "top"}) + session2.WaitWithDefaultTimeout() + Expect(session2.ExitCode()).To(Equal(0)) + cid2 := session2.OutputToStringArray()[0] + + kill := podmanTest.Podman([]string{"kill", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) + kill.WaitWithDefaultTimeout() + Expect(kill.ExitCode()).To(BeZero()) + + wait := podmanTest.Podman([]string{"wait", "--condition", "exited", cid1}) + wait.WaitWithDefaultTimeout() + Expect(wait.ExitCode()).To(BeZero()) + wait = podmanTest.Podman([]string{"wait", "--condition", "exited", cid2}) + wait.WaitWithDefaultTimeout() + Expect(wait.ExitCode()).To(BeZero()) + }) + }) diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go index 5de77f158..a2e88ecd2 100644 --- a/test/e2e/login_logout_test.go +++ b/test/e2e/login_logout_test.go @@ -66,7 +66,7 @@ var _ = Describe("Podman login and logout", func() { registriesConfWithSearch = []byte(fmt.Sprintf("[registries.search]\nregistries = ['%s']", server)) - testImg = strings.Join([]string{server, "test-apline"}, "/") + testImg = strings.Join([]string{server, "test-alpine"}, "/") certDirPath = filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", server) os.MkdirAll(certDirPath, os.ModePerm) @@ -155,8 +155,8 @@ var _ = Describe("Podman login and logout", func() { json.Unmarshal(authInfo, &info) fmt.Println(info) - // push should fail with nonexist authfile - session = podmanTest.Podman([]string{"push", "--authfile", "/tmp/nonexist", ALPINE, testImg}) + // push should fail with nonexistent authfile + session = podmanTest.Podman([]string{"push", "--authfile", "/tmp/nonexistent", ALPINE, testImg}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) @@ -168,8 +168,8 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - // logout should fail with nonexist authfile - session = podmanTest.Podman([]string{"logout", "--authfile", "/tmp/nonexist", server}) + // logout should fail with nonexistent authfile + session = podmanTest.Podman([]string{"logout", "--authfile", "/tmp/nonexistent", server}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index a749a86ff..b370aeec1 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -332,6 +332,11 @@ var _ = Describe("Podman logs", func() { wait.WaitWithDefaultTimeout() Expect(wait).To(Exit(0)) + inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("10kB")) + results := podmanTest.Podman([]string{"logs", cid}) results.WaitWithDefaultTimeout() Expect(results).To(Exit(0)) @@ -355,4 +360,21 @@ var _ = Describe("Podman logs", func() { Expect(outlines[0]).To(Equal("1\r")) Expect(outlines[1]).To(Equal("2\r")) }) + + It("podman logs test stdout and stderr", func() { + cname := "log-test" + logc := podmanTest.Podman([]string{"run", "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + wait := podmanTest.Podman([]string{"wait", cname}) + wait.WaitWithDefaultTimeout() + Expect(wait).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", cname}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(results.OutputToString()).To(Equal("stdout")) + Expect(results.ErrorToString()).To(Equal("stderr")) + }) }) diff --git a/test/e2e/mount_rootless_test.go b/test/e2e/mount_rootless_test.go index 1e4152709..c410a2507 100644 --- a/test/e2e/mount_rootless_test.go +++ b/test/e2e/mount_rootless_test.go @@ -16,7 +16,7 @@ var _ = Describe("Podman mount", func() { ) BeforeEach(func() { - SkipIfNotRootless("This function is not enabled for rootfull podman") + SkipIfNotRootless("This function is not enabled for rootful podman") SkipIfRemote("Podman mount not supported for remote connections") tempdir, err = CreateTempDirInTempDir() if err != nil { diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index 7cdad9bf2..dd94bd7ca 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -33,7 +33,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad network name in disconnect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") dis := podmanTest.Podman([]string{"network", "disconnect", "foobar", "test"}) dis.WaitWithDefaultTimeout() Expect(dis.ExitCode()).ToNot(BeZero()) @@ -41,7 +41,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad container name in network disconnect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -55,7 +55,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network disconnect", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -80,7 +80,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad network name in connect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") dis := podmanTest.Podman([]string{"network", "connect", "foobar", "test"}) dis.WaitWithDefaultTimeout() Expect(dis.ExitCode()).ToNot(BeZero()) @@ -88,7 +88,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("bad container name in network connect should result in error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -102,7 +102,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman connect on a container that already is connected to the network should error", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -120,7 +120,7 @@ var _ = Describe("Podman network connect and disconnect", func() { It("podman network connect", func() { SkipIfRemote("This requires a pending PR to be merged before it will work") - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -152,7 +152,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network connect when not running", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() @@ -181,7 +181,7 @@ var _ = Describe("Podman network connect and disconnect", func() { }) It("podman network disconnect when not running", func() { - SkipIfRootless("network connect and disconnect are only rootfull") + SkipIfRootless("network connect and disconnect are only rootful") netName1 := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 21b3074fc..7e9a18ab2 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -106,6 +106,7 @@ var _ = Describe("Podman network create", func() { Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("0.0.0.0/0")) Expect(bridgePlugin.IsGW).To(BeTrue()) Expect(bridgePlugin.IPMasq).To(BeTrue()) + Expect(bridgePlugin.IPAM.Ranges[0][0].Gateway).ToNot(BeEmpty()) Expect(portMapPlugin.Capabilities["portMappings"]).To(BeTrue()) }) @@ -153,6 +154,8 @@ var _ = Describe("Podman network create", func() { // JSON the bridge info bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge") Expect(err).To(BeNil()) + // check that gateway is added to config + Expect(bridgePlugin.IPAM.Ranges[0][0].Gateway).To(Equal("10.11.12.1")) // Once a container executes a new network, the nic will be created. We should clean those up // best we can diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 5ecfdd6b5..f009e333e 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -16,7 +16,7 @@ import ( ) var unknownKindYaml = ` -apiVerson: v1 +apiVersion: v1 kind: UnknownKind metadata: labels: @@ -62,6 +62,7 @@ metadata: spec: restartPolicy: {{ .RestartPolicy }} hostname: {{ .Hostname }} + hostNetwork: {{ .HostNetwork }} hostAliases: {{ range .HostAliases }} - hostnames: @@ -220,6 +221,7 @@ spec: spec: restartPolicy: {{ .RestartPolicy }} hostname: {{ .Hostname }} + hostNetwork: {{ .HostNetwork }} containers: {{ with .Ctrs }} {{ range . }} @@ -376,6 +378,7 @@ type Pod struct { Name string RestartPolicy string Hostname string + HostNetwork bool HostAliases []HostAlias Ctrs []*Ctr Volumes []*Volume @@ -396,6 +399,7 @@ func getPod(options ...podOption) *Pod { Name: defaultPodName, RestartPolicy: "Never", Hostname: "", + HostNetwork: false, HostAliases: nil, Ctrs: make([]*Ctr, 0), Volumes: make([]*Volume, 0), @@ -464,6 +468,12 @@ func withVolume(v *Volume) podOption { } } +func withHostNetwork() podOption { + return func(pod *Pod) { + pod.HostNetwork = true + } +} + // Deployment describes the options a kube yaml can be configured at deployment level type Deployment struct { Name string @@ -793,11 +803,11 @@ var _ = Describe("Podman play kube", func() { }) - It("podman play kube fail with nonexist authfile", func() { + It("podman play kube fail with nonexistent authfile", func() { err := generateKubeYaml("pod", getPod(), kubeYaml) Expect(err).To(BeNil()) - kube := podmanTest.Podman([]string{"play", "kube", "--authfile", "/tmp/nonexist", kubeYaml}) + kube := podmanTest.Podman([]string{"play", "kube", "--authfile", "/tmp/nonexistent", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Not(Equal(0))) @@ -820,8 +830,28 @@ var _ = Describe("Podman play kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) }) + // If you do not supply command or args for a Container, the defaults defined in the Docker image are used. + It("podman play kube test correct args and cmd when not specified", func() { + pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil)))) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + + // this image's ENTRYPOINT is `/entrypoint.sh` and it's COMMAND is `/etc/docker/registry/config.yml` + Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh /etc/docker/registry/config.yml]`)) + }) + + // If you supply a command but no args for a Container, only the supplied command is used. + // The default EntryPoint and the default Cmd defined in the Docker image are ignored. It("podman play kube test correct command with only set command in yaml file", func() { - pod := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg(nil)))) + pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo", "hello"}), withArg(nil)))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -837,8 +867,29 @@ var _ = Describe("Podman play kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) + // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied. It("podman play kube test correct command with only set args in yaml file", func() { - pod := getPod(withCtr(getCtr(withImage(redis), withCmd(nil), withArg([]string{"echo", "hello"})))) + pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg([]string{"echo", "hello"})))) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + // this image's ENTRYPOINT is `/entrypoint.sh` + // so result should be `/entrypoint.sh + withArg(...)` + Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh echo hello]`)) + }) + + // If you supply a command and args, + // the default Entrypoint and the default Cmd defined in the Docker image are ignored. + // Your command is run with your args. + It("podman play kube test correct command with both set args and cmd in yaml file", func() { + pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo"}), withArg([]string{"hello"})))) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) @@ -849,9 +900,7 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - // this image's ENTRYPOINT is called `docker-entrypoint.sh` - // so result should be `docker-entrypoint.sh + withArg(...)` - Expect(inspect.OutputToString()).To(ContainSubstring(`[docker-entrypoint.sh echo hello]`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) It("podman play kube test correct output", func() { @@ -1072,7 +1121,7 @@ var _ = Describe("Podman play kube", func() { logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) - Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted")) + Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted")) }) It("podman play kube seccomp pod level", func() { @@ -1099,7 +1148,7 @@ var _ = Describe("Podman play kube", func() { logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) - Expect(logs.OutputToString()).To(ContainSubstring("Operation not permitted")) + Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted")) }) It("podman play kube with pull policy of never should be 125", func() { @@ -1240,7 +1289,7 @@ spec: inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - // yaml's command shuold override the image's Entrypoint + // yaml's command should override the image's Entrypoint correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) }) @@ -1285,7 +1334,7 @@ spec: Expect(inspect.OutputToString()).To(Equal("5000/tcp -> 127.0.0.100:5000")) }) - It("podman play kube test with non-existent empty HostPath type volume", func() { + It("podman play kube test with nonexistent empty HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") pod := getPod(withVolume(getHostPathVolume(`""`, hostPathLocation))) @@ -1312,7 +1361,7 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) }) - It("podman play kube test with non-existent File HostPath type volume", func() { + It("podman play kube test with nonexistent File HostPath type volume", func() { hostPathLocation := filepath.Join(tempdir, "file") pod := getPod(withVolume(getHostPathVolume("File", hostPathLocation))) @@ -1548,4 +1597,23 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) Expect(inspect.ExitCode()).To(Equal(0)) Expect(inspect.OutputToString()).To(Equal("false")) }) + + It("podman play kube test with HostNetwork", func() { + if !strings.Contains(podmanTest.OCIRuntime, "crun") { + Skip("Test only works on crun") + } + + pod := getPod(withHostNetwork()) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", pod.Name, "--format", "{{ .InfraConfig.HostNetwork }}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(Equal("true")) + }) }) diff --git a/test/e2e/pod_infra_container_test.go b/test/e2e/pod_infra_container_test.go index 7ec36b2f8..452a3de21 100644 --- a/test/e2e/pod_infra_container_test.go +++ b/test/e2e/pod_infra_container_test.go @@ -225,6 +225,7 @@ var _ = Describe("Podman pod create", func() { }) It("podman pod container can override pod pid NS", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"pod", "create", "--share", "pid"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go index f968f73a6..710147893 100644 --- a/test/e2e/pod_kill_test.go +++ b/test/e2e/pod_kill_test.go @@ -127,6 +127,7 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill all", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") _, ec, podid := podmanTest.CreatePod("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index ea8d10e78..225da785c 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -157,6 +157,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps --ctr-names", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") _, ec, podid := podmanTest.CreatePod("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 41fc59267..a034ec2d1 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -17,8 +17,9 @@ var _ = Describe("Podman pod stats", func() { ) BeforeEach(func() { - if os.Geteuid() != 0 { - SkipIfCgroupV2("--cgroup-manager=cgroupfs which doesn't work in rootless mode") + SkipIfRootless("Tests fail with both CGv1/2 + required --cgroup-manager=cgroupfs") + if isContainerized() { + SkipIfCgroupV1("All tests fail Error: unable to load cgroup at ...: cgroup deleted") } tempdir, err = CreateTempDirInTempDir() @@ -176,7 +177,8 @@ var _ = Describe("Podman pod stats", func() { It("podman stats on net=host post", func() { // --net=host not supported for rootless pods at present - SkipIfRootlessCgroupsV1("Pause stats not supported in cgroups v1") + // problem with sysctls being passed to containers of the pod. + SkipIfCgroupV1("Bug: Error: sysctl net.ipv4.ping_group_range is not allowed in the hosts network namespace: OCI runtime error") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) podCreate.WaitWithDefaultTimeout() diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index c02ed5a50..3bc1012df 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -349,4 +349,64 @@ var _ = Describe("Podman prune", func() { // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(len(CACHE_IMAGES))) }) + + It("podman system prune --volumes --filter", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(7)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=label1=value1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(6)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1=slv1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(5)) + + session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(3)) + + podmanTest.Cleanup() + }) }) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 05571157c..0c5d817ba 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -673,4 +673,55 @@ var _ = Describe("Podman ps", func() { Expect(session.LineInOutputContains("test3")).To(BeTrue()) Expect(session.LineInOutputContains("test4")).To(BeTrue()) }) + It("podman ps filter pod", func() { + pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"}) + pod1.WaitWithDefaultTimeout() + Expect(pod1.ExitCode()).To(BeZero()) + con1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", ALPINE, "top"}) + con1.WaitWithDefaultTimeout() + Expect(con1.ExitCode()).To(BeZero()) + + pod2 := podmanTest.Podman([]string{"pod", "create", "--name", "pod2"}) + pod2.WaitWithDefaultTimeout() + Expect(pod2.ExitCode()).To(BeZero()) + con2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod2", ALPINE, "top"}) + con2.WaitWithDefaultTimeout() + Expect(con2.ExitCode()).To(BeZero()) + + // bogus pod name or id should not result in error + session := podmanTest.Podman([]string{"ps", "--filter", "pod=1234"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + // filter by pod name + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by full pod id + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by partial pod id + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()[0:12]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + + // filter by multiple pods is inclusive + session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1", "--filter", "pod=pod2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(len(session.OutputToStringArray())).To(Equal(4)) + Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) + Expect(StringInSlice(pod2.OutputToString(), session.OutputToStringArray())) + + }) + }) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index 446e2bd38..7099a2904 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -393,8 +393,8 @@ var _ = Describe("Podman pull", func() { Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 4)) }) - It("podman pull from docker with nonexist --authfile", func() { - session := podmanTest.Podman([]string{"pull", "--authfile", "/tmp/nonexist", ALPINE}) + It("podman pull from docker with nonexistent --authfile", func() { + session := podmanTest.Podman([]string{"pull", "--authfile", "/tmp/nonexistent", ALPINE}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) }) @@ -506,7 +506,7 @@ var _ = Describe("Podman pull", func() { session = podmanTest.Podman([]string{"pull", "--platform=linux/arm64", "--override-os", "windows", ALPINE}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) - expectedError = "--platform option can not be specified with --overide-arch or --override-os" + expectedError = "--platform option can not be specified with --override-arch or --override-os" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", ALPINE}) diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index c8d77b7c6..1f40e4928 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -113,7 +113,7 @@ var _ = Describe("Podman rmi", func() { }) It("podman rmi image that is a parent of another image", func() { - Skip("I need help with this one. i dont understand what is going on") + Skip("I need help with this one. i don't understand what is going on") podmanTest.AddImageToRWStore(cirros) session := podmanTest.Podman([]string{"run", "--name", "c_test", cirros, "true"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go index 5242e04d2..51657cb1e 100644 --- a/test/e2e/run_ns_test.go +++ b/test/e2e/run_ns_test.go @@ -35,6 +35,7 @@ var _ = Describe("Podman run ns", func() { }) It("podman run pidns test", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"run", fedoraMinimal, "bash", "-c", "echo $$"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -105,6 +106,7 @@ var _ = Describe("Podman run ns", func() { }) It("podman run --ipc=host --pid=host", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cmd := exec.Command("ls", "-l", "/proc/self/ns/pid") res, err := cmd.Output() Expect(err).To(BeNil()) diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go index ab11128ba..48f9ea76e 100644 --- a/test/e2e/run_privileged_test.go +++ b/test/e2e/run_privileged_test.go @@ -16,22 +16,22 @@ import ( // know about at compile time. That is: the kernel may have more caps // available than we are aware of, leading to host=FFF... and ctr=3FF... // because the latter is all we request. Accept that. -func containerCapMatchesHost(ctr_cap string, host_cap string) { +func containerCapMatchesHost(ctrCap string, hostCap string) { if isRootless() { return } - ctr_cap_n, err := strconv.ParseUint(ctr_cap, 16, 64) - Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctr_cap) + ctrCap_n, err := strconv.ParseUint(ctrCap, 16, 64) + Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctrCap) - host_cap_n, err := strconv.ParseUint(host_cap, 16, 64) - Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", host_cap) + hostCap_n, err := strconv.ParseUint(hostCap, 16, 64) + Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", hostCap) // host caps can never be zero (except rootless). // and host caps must always be a superset (inclusive) of container - Expect(host_cap_n).To(BeNumerically(">", 0), "host cap %q should be nonzero", host_cap) - Expect(host_cap_n).To(BeNumerically(">=", ctr_cap_n), "host cap %q should never be less than container cap %q", host_cap, ctr_cap) - host_cap_masked := host_cap_n & (1<<len(capability.List()) - 1) - Expect(ctr_cap_n).To(Equal(host_cap_masked), "container cap %q is not a subset of host cap %q", ctr_cap, host_cap) + Expect(hostCap_n).To(BeNumerically(">", 0), "host cap %q should be nonzero", hostCap) + Expect(hostCap_n).To(BeNumerically(">=", ctrCap_n), "host cap %q should never be less than container cap %q", hostCap, ctrCap) + hostCap_masked := hostCap_n & (1<<len(capability.List()) - 1) + Expect(ctrCap_n).To(Equal(hostCap_masked), "container cap %q is not a subset of host cap %q", ctrCap, hostCap) } var _ = Describe("Podman privileged container tests", func() { @@ -68,26 +68,38 @@ var _ = Describe("Podman privileged container tests", func() { }) It("podman privileged CapEff", func() { - host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(host_cap.ExitCode()).To(Equal(0)) + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) session := podmanTest.Podman([]string{"run", "--privileged", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString()) + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) It("podman cap-add CapEff", func() { // Get caps of current process - host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(host_cap.ExitCode()).To(Equal(0)) + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) session := podmanTest.Podman([]string{"run", "--cap-add", "all", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString()) + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) + }) + + It("podman cap-add CapEff with --user", func() { + // Get caps of current process + hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) + Expect(hostCap.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--user=bin", "--cap-add", "all", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) It("podman cap-drop CapEff", func() { @@ -98,6 +110,15 @@ var _ = Describe("Podman privileged container tests", func() { Expect("0000000000000000").To(Equal(capEff[1])) }) + It("podman privileged should disable seccomp by default", func() { + hostSeccomp := SystemExec("grep", []string{"-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) + Expect(hostSeccomp.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "grep", "-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman non-privileged should have very few devices", func() { session := podmanTest.Podman([]string{"run", "-t", "busybox", "ls", "-l", "/dev"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go index 3294f6d3b..2e9d38e2d 100644 --- a/test/e2e/run_selinux_test.go +++ b/test/e2e/run_selinux_test.go @@ -274,6 +274,7 @@ var _ = Describe("Podman run", func() { }) It("podman test --pid=host", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"run", "--pid=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index dbdd6a072..4888a676b 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -493,7 +493,9 @@ USER bin` Skip("Kernel does not support blkio.weight") } } - + if podmanTest.Host.Distribution == "ubuntu" { + Skip("Ubuntu <= 20.10 lacks BFQ scheduler") + } if CGROUPSV2 { // convert linearly from [10-1000] to [1-10000] session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.bfq.weight"}) @@ -1228,8 +1230,8 @@ USER mail` Expect(session).To(ExitWithError()) }) - It("podman run should fail with nonexist authfile", func() { - session := podmanTest.Podman([]string{"run", "--authfile", "/tmp/nonexist", ALPINE, "ls"}) + It("podman run should fail with nonexistent authfile", func() { + session := podmanTest.Podman([]string{"run", "--authfile", "/tmp/nonexistent", ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) }) diff --git a/test/e2e/runlabel_test.go b/test/e2e/runlabel_test.go index 7c0b8bc9b..10e16ea23 100644 --- a/test/e2e/runlabel_test.go +++ b/test/e2e/runlabel_test.go @@ -114,12 +114,12 @@ var _ = Describe("podman container runlabel", func() { Expect(result.ExitCode()).To(Equal(0)) }) - It("runlabel should fail with nonexist authfile", func() { + It("runlabel should fail with nonexistent authfile", func() { image := "podman-runlabel-test:podman" podmanTest.BuildImage(PodmanDockerfile, image, "false") - // runlabel should fail with nonexist authfile - result := podmanTest.Podman([]string{"container", "runlabel", "--authfile", "/tmp/nonexist", "RUN", image}) + // runlabel should fail with nonexistent authfile + result := podmanTest.Podman([]string{"container", "runlabel", "--authfile", "/tmp/nonexistent", "RUN", image}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Not(Equal(0))) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 5c3c69fd4..f809c5afe 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -430,9 +430,9 @@ registries = ['{{.Host}}:{{.Port}}']` resetRegistriesConfigEnv() }) - // search should fail with nonexist authfile - It("podman search fail with nonexist --authfile", func() { - search := podmanTest.Podman([]string{"search", "--authfile", "/tmp/nonexist", ALPINE}) + // search should fail with nonexistent authfile + It("podman search fail with nonexistent --authfile", func() { + search := podmanTest.Podman([]string{"search", "--authfile", "/tmp/nonexistent", ALPINE}) search.WaitWithDefaultTimeout() Expect(search.ExitCode()).To(Not(Equal(0))) }) diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 942e00123..a6f22e007 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -49,6 +49,29 @@ var _ = Describe("Podman start", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman start --rm removed on failure", func() { + session := podmanTest.Podman([]string{"create", "--name=test", "--rm", ALPINE, "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"start", "test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + session = podmanTest.Podman([]string{"container", "exists", "test"}) + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("podman start --rm --attach removed on failure", func() { + session := podmanTest.Podman([]string{"create", "--rm", ALPINE, "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + session = podmanTest.Podman([]string{"start", "--attach", cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + session = podmanTest.Podman([]string{"container", "exists", cid}) + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + It("podman container start single container by id", func() { session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index ab117a2a0..53aa230e9 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -140,7 +140,7 @@ var _ = Describe("Podman stats", func() { // Regression test for #8265 It("podman stats with custom memory limits", func() { - // Run thre containers. One with a memory limit. Make sure + // Run three containers. One with a memory limit. Make sure // that the limits are different and the limited one has a // lower limit. ctrNoLimit0 := "no-limit-0" diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 48294943b..49ab3b8ed 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -13,10 +13,10 @@ import ( var _ = Describe("Podman systemd", func() { var ( - tempdir string - err error - podmanTest *PodmanTestIntegration - systemd_unit_file string + tempdir string + err error + podmanTest *PodmanTestIntegration + systemdUnitFile string ) BeforeEach(func() { @@ -27,7 +27,7 @@ var _ = Describe("Podman systemd", func() { podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() podmanTest.SeedImages() - systemd_unit_file = `[Unit] + systemdUnitFile = `[Unit] Description=redis container [Service] Restart=always @@ -50,7 +50,7 @@ WantedBy=multi-user.target SkipIfRootless("rootless can not write to /etc") SkipIfContainerized("test does not have systemd as pid 1") - sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemd_unit_file), 0644) + sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemdUnitFile), 0644) Expect(sys_file).To(BeNil()) defer func() { stop := SystemExec("bash", []string{"-c", "systemctl stop redis"}) @@ -131,6 +131,21 @@ WantedBy=multi-user.target Expect(conData[0].Config.SystemdMode).To(BeTrue()) }) + It("podman create container with --uidmap and conmon PidFile accessible", func() { + ctrName := "testCtrUidMap" + run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", ctrName}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + pidFile := strings.TrimSuffix(session.OutputToString(), "\n") + _, err := ioutil.ReadFile(pidFile) + Expect(err).To(BeNil()) + }) + It("podman create container with systemd=always triggers systemd mode", func() { ctrName := "testCtr" run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE}) diff --git a/test/e2e/toolbox_test.go b/test/e2e/toolbox_test.go index 7393b13cb..6de775983 100644 --- a/test/e2e/toolbox_test.go +++ b/test/e2e/toolbox_test.go @@ -121,6 +121,7 @@ var _ = Describe("Toolbox-specific testing", func() { if podmanTest.RemoteTest { Skip("Shm size check does not work with a remote client") } + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") var session *PodmanSessionIntegration var cmd *exec.Cmd var hostShmSize, containerShmSize int @@ -239,7 +240,7 @@ var _ = Describe("Toolbox-specific testing", func() { session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) + Expect(session.ErrorToString()).To(ContainSubstring(expectedOutput)) }) It("podman create --userns=keep-id + podman exec - adding group with groupadd", func() { diff --git a/test/e2e/tree_test.go b/test/e2e/tree_test.go index 2a7feaacb..9bdc3af9d 100644 --- a/test/e2e/tree_test.go +++ b/test/e2e/tree_test.go @@ -35,7 +35,7 @@ var _ = Describe("Podman image tree", func() { It("podman image tree", func() { SkipIfRemote("Does not work on remote client") - Skip("dont understand why this fails") + Skip("don't understand why this fails") podmanTest.AddImageToRWStore(cirros) dockerfile := `FROM quay.io/libpod/cirros:latest RUN mkdir hello diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go index 8c44e57e4..544532ee0 100644 --- a/test/e2e/volume_create_test.go +++ b/test/e2e/volume_create_test.go @@ -82,5 +82,13 @@ var _ = Describe("Podman volume create", func() { inspectGID.WaitWithDefaultTimeout() Expect(inspectGID.ExitCode()).To(Equal(0)) Expect(inspectGID.OutputToString()).To(Equal(gid)) + + // options should containt `uid=3000,gid=4000:3000:4000` + optionFormat := `{{ .Options.o }}:{{ .Options.UID }}:{{ .Options.GID }}` + optionStrFormatExpect := fmt.Sprintf(`uid=%s,gid=%s:%s:%s`, uid, gid, uid, gid) + inspectOpts := podmanTest.Podman([]string{"volume", "inspect", "--format", optionFormat, volName}) + inspectOpts.WaitWithDefaultTimeout() + Expect(inspectOpts.ExitCode()).To(Equal(0)) + Expect(inspectOpts.OutputToString()).To(Equal(optionStrFormatExpect)) }) }) diff --git a/test/e2e/volume_prune_test.go b/test/e2e/volume_prune_test.go index c8521ebe7..a910c47a7 100644 --- a/test/e2e/volume_prune_test.go +++ b/test/e2e/volume_prune_test.go @@ -62,6 +62,66 @@ var _ = Describe("Podman volume prune", func() { podmanTest.Cleanup() }) + It("podman prune volume --filter", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(7)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=label1=value1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(6)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1=slv1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(5)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(3)) + + podmanTest.Cleanup() + }) + It("podman system prune --volume", func() { session := podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() diff --git a/test/framework/framework.go b/test/framework/framework.go index 52401faf8..57c6bda2a 100644 --- a/test/framework/framework.go +++ b/test/framework/framework.go @@ -7,7 +7,7 @@ import ( "github.com/onsi/gomega" ) -// TestFramework is used to support commonnly used test features +// TestFramework is used to support commonly used test features type TestFramework struct { setup func(*TestFramework) error teardown func(*TestFramework) error diff --git a/test/python/docker/test_containers.py b/test/python/docker/test_containers.py index 20d8417c3..5a9f761a6 100644 --- a/test/python/docker/test_containers.py +++ b/test/python/docker/test_containers.py @@ -143,7 +143,7 @@ class TestContainers(unittest.TestCase): top = self.client.containers.get(TestContainers.topContainerId) top.stop() - # Pause exited container should trow error + # Pause exited container should throw error with self.assertRaises(errors.APIError) as error: top.pause() self.assertEqual(error.exception.response.status_code, 500) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 3ee141f5f..29dc95dc3 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -401,7 +401,7 @@ json-file | f is "$output" "$driver" "podman inspect: driver" # If LogPath is non-null, check that it exists and has a valid log - run_podman inspect --format '{{.LogPath}}' myctr + run_podman inspect --format '{{.HostConfig.LogConfig.Path}}' myctr if [[ $do_check != '-' ]]; then is "$output" "/.*" "LogPath (driver=$driver)" if ! test -e "$output"; then @@ -415,13 +415,18 @@ json-file | f fi if [[ $driver != 'none' ]]; then - run_podman logs myctr - is "$output" "$msg" "check that podman logs works as expected" + if [[ $driver = 'journald' ]] && journald_unavailable; then + # Cannot perform check + : + else + run_podman logs myctr + is "$output" "$msg" "podman logs, with driver '$driver'" + fi else run_podman 125 logs myctr if ! is_remote; then is "$output" ".*this container is using the 'none' log driver, cannot read logs.*" \ - "podman logs does not work with none log driver" + "podman logs, with driver 'none', should fail with error" fi fi run_podman rm myctr @@ -437,14 +442,7 @@ json-file | f skip_if_remote "We cannot read journalctl over remote." # We can't use journald on RHEL as rootless, either: rhbz#1895105 - if is_rootless; then - run journalctl -n 1 - if [[ $status -ne 0 ]]; then - if [[ $output =~ permission ]]; then - skip "Cannot use rootless journald on this system" - fi - fi - fi + skip_if_journald_unavailable msg=$(random_string 20) pidfile="${PODMAN_TMPDIR}/$(random_string 20)" @@ -550,27 +548,33 @@ json-file | f } @test "Verify /run/.containerenv exist" { - run_podman run --rm $IMAGE ls -1 /run/.containerenv - is "$output" "/run/.containerenv" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $engine' - is "$output" ".*podman.*" "failed to identify engine" - - run_podman run --privileged --name "testcontainerenv" --rm $IMAGE sh -c '. /run/.containerenv; echo $name' - is "$output" ".*testcontainerenv.*" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $image' - is "$output" ".*$IMAGE.*" "failed to idenitfy image" - - run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $rootless' - # FIXME: on some CI systems, 'run --privileged' emits a spurious - # warning line about dup devices. Ignore it. - remove_same_dev_warning - if is_rootless; then - is "$output" "1" - else - is "$output" "0" - fi + # Nonprivileged container: file exists, but must be empty + run_podman run --rm $IMAGE stat -c '%s' /run/.containerenv + is "$output" "0" "file size of /run/.containerenv, nonprivileged" + + # Prep work: get ID of image; make a cont. name; determine if we're rootless + run_podman inspect --format '{{.ID}}' $IMAGE + local iid="$output" + + random_cname=c$(random_string 15 | tr A-Z a-z) + local rootless=0 + if is_rootless; then + rootless=1 + fi + + run_podman run --privileged --rm --name $random_cname $IMAGE \ + sh -c '. /run/.containerenv; echo $engine; echo $name; echo $image; echo $id; echo $imageid; echo $rootless' + + # FIXME: on some CI systems, 'run --privileged' emits a spurious + # warning line about dup devices. Ignore it. + remove_same_dev_warning + + is "${lines[0]}" "podman-.*" 'containerenv : $engine' + is "${lines[1]}" "$random_cname" 'containerenv : $name' + is "${lines[2]}" "$IMAGE" 'containerenv : $image' + is "${lines[3]}" "[0-9a-f]\{64\}" 'containerenv : $id' + is "${lines[4]}" "$iid" 'containerenv : $imageid' + is "${lines[5]}" "$rootless" 'containerenv : $rootless' } @test "podman run with --net=host and --port prints warning" { diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index a081a7ce1..bac153b8e 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -55,14 +55,7 @@ ${cid[0]} d" "Sequential output from logs" @test "podman logs over journald" { # We can't use journald on RHEL as rootless: rhbz#1895105 - if is_rootless; then - run journalctl -n 1 - if [[ $status -ne 0 ]]; then - if [[ $output =~ permission ]]; then - skip "Cannot use rootless journald on this system" - fi - fi - fi + skip_if_journald_unavailable msg=$(random_string 20) diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 1ed2779b2..0447122b1 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -82,4 +82,43 @@ load helpers run_podman rm -a } +@test "podman ps -a --storage" { + skip_if_remote "ps --storage does not work over remote" + + # Setup: ensure that we have no hidden storage containers + run_podman ps --storage -a + is "${#lines[@]}" "1" "setup check: no storage containers at start of test" + + # Force a buildah timeout; this leaves a buildah container behind + PODMAN_TIMEOUT=5 run_podman 124 build -t thiswillneverexist - <<EOF +FROM $IMAGE +RUN sleep 30 +EOF + + run_podman ps -a + is "${#lines[@]}" "1" "podman ps -a does not see buildah container" + + run_podman ps --storage -a + is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + is "${lines[1]}" \ + "[0-9a-f]\{12\} \+$IMAGE *buildah .* seconds ago .* storage .* ${PODMAN_TEST_IMAGE_NAME}-working-container" \ + "podman ps --storage" + + cid="${lines[1]:0:12}" + + # 'rm -a' should be a NOP + run_podman rm -a + run_podman ps --storage -a + is "${#lines[@]}" "2" "podman ps -a --storage sees buildah container" + + # This is what deletes the container + # FIXME: why doesn't "podman rm --storage $cid" do anything? + run_podman rm -f "$cid" + + run_podman ps --storage -a + is "${#lines[@]}" "1" "storage container has been removed" +} + + + # vim: filetype=sh diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 43bdf217d..d3cf1c274 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -8,8 +8,6 @@ load helpers @test "podman cp file from host to container" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr mkdir -p $srcdir local -a randomcontent=( @@ -57,60 +55,16 @@ load helpers is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \ "copy nonexistent host path" - # Container path does not exist. Notice that the error message shows how - # the specified container is resolved. + # Container (parent) path does not exist. run_podman 125 cp $srcdir/hostfile0 cpcontainer:/IdoNotExist/ - is "$output" 'Error: "/IdoNotExist/" could not be found on container.*(resolved to .*/IdoNotExist.*' \ + is "$output" 'Error: "/IdoNotExist/" could not be found on container cpcontainer: No such file or directory' \ "copy into nonexistent path in container" run_podman rm -f cpcontainer } -@test "podman cp --extract=true tar archive to container" { - skip_if_remote "podman-remote does not yet handle cp" - - # Create tempfile with random name and content - dirname=cp-test-extract - srcdir=$PODMAN_TMPDIR/$dirname - mkdir -p $srcdir - rand_filename=$(random_string 20) - rand_content=$(random_string 50) - echo $rand_content > $srcdir/$rand_filename - chmod 644 $srcdir/$rand_filename - - # Now tar it up! - tar_file=$PODMAN_TMPDIR/archive.tar.gz - tar -C $PODMAN_TMPDIR -zvcf $tar_file $dirname - - run_podman run -d --name cpcontainer $IMAGE sleep infinity - - # First just copy without extracting the archive. - run_podman cp $tar_file cpcontainer:/tmp - # Now remove the archive which will also test if it exists and is a file. - # To save expensive exec'ing, create a file for the next tests. - run_podman exec cpcontainer sh -c "rm /tmp/archive.tar.gz; touch /tmp/file.txt" - - # Now copy with extracting the archive. NOTE that Podman should - # auto-decompress the file if needed. - run_podman cp --extract=true $tar_file cpcontainer:/tmp - run_podman exec cpcontainer cat /tmp/$dirname/$rand_filename - is "$output" "$rand_content" - - # Test extract on non archive. - run_podman cp --extract=true $srcdir/$rand_filename cpcontainer:/foo.txt - - # Cannot extract an archive to a file! - run_podman 125 cp --extract=true $tar_file cpcontainer:/tmp/file.txt - is "$output" 'Error: cannot extract archive .* to file "/tmp/file.txt"' - - run_podman rm -f cpcontainer -} - - @test "podman cp file from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host mkdir -p $srcdir @@ -153,8 +107,6 @@ load helpers @test "podman cp dir from host to container" { - skip_if_remote "podman-remote does not yet handle cp" - dirname=dir-test srcdir=$PODMAN_TMPDIR/$dirname mkdir -p $srcdir @@ -195,8 +147,6 @@ load helpers @test "podman cp dir from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/dir-test mkdir -p $srcdir @@ -230,8 +180,6 @@ load helpers @test "podman cp file from host to container volume" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-volume mkdir -p $srcdir echo "This file should be in volume2" > $srcdir/hostfile @@ -268,8 +216,6 @@ load helpers @test "podman cp file from host to container mount" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-mount-src mountdir=$PODMAN_TMPDIR/cp-test-mount mkdir -p $srcdir $mountdir @@ -296,8 +242,6 @@ load helpers # perform wildcard expansion in the container. We should get both # files copied into the host. @test "podman cp * - wildcard copy multiple files from container to host" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -321,8 +265,6 @@ load helpers # Create a file on the host; make a symlink in the container pointing # into host-only space. Try to podman-cp that symlink. It should fail. @test "podman cp - will not recognize symlink pointing into host space" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -350,8 +292,6 @@ load helpers # in the container pointing to 'file*' (file star). Try to podman-cp # this invalid double symlink. It must fail. @test "podman cp - will not expand globs in host space (#3829)" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -372,8 +312,6 @@ load helpers # Another symlink into host space, this one named '*' (star). cp should fail. @test "podman cp - will not expand wildcard" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -394,8 +332,6 @@ load helpers # THIS IS EXTREMELY WEIRD. Podman expands symlinks in weird ways. @test "podman cp into container: weird symlink expansion" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-in dstdir=$PODMAN_TMPDIR/cp-test-out mkdir -p $srcdir $dstdir @@ -427,7 +363,7 @@ load helpers is "$output" "" "output from podman cp 1" run_podman 125 cp --pause=false $srcdir/$rand_filename2 cpcontainer:/tmp/d2/x/ - is "$output" 'Error: "/tmp/d2/x/" could not be found on container.*' "cp will not create nonexistent destination directory" + is "$output" 'Error: "/tmp/d2/x/" could not be found on container cpcontainer: No such file or directory' "cp will not create nonexistent destination directory" run_podman cp --pause=false $srcdir/$rand_filename3 cpcontainer:/tmp/d3/x is "$output" "" "output from podman cp 3" @@ -454,8 +390,6 @@ load helpers # rhbz1741718 : file copied into container:/var/lib/foo appears as /foo # (docker only, never seems to have affected podman. Make sure it never does). @test "podman cp into a subdirectory matching GraphRoot" { - skip_if_remote "podman-remote does not yet handle cp" - # Create tempfile with random name and content srcdir=$PODMAN_TMPDIR/cp-test-in mkdir -p $srcdir @@ -491,8 +425,6 @@ load helpers @test "podman cp from stdin to container" { - skip_if_remote "podman-remote does not yet handle cp" - # Create tempfile with random name and content srcdir=$PODMAN_TMPDIR/cp-test-stdin mkdir -p $srcdir @@ -525,24 +457,22 @@ load helpers # Input stream must be a (compressed) tar archive. run_podman 125 cp - cpcontainer:/tmp < $srcdir/$rand_filename - is "$output" "Error:.*: error reading tar stream.*" "input stream must be a (compressed) tar archive" + is "$output" "Error: source must be a (compressed) tar archive when copying from stdin" # Destination must be a directory (on an existing file). run_podman exec cpcontainer touch /tmp/file.txt run_podman 125 cp /dev/stdin cpcontainer:/tmp/file.txt < $tar_file - is "$output" 'Error: destination must be a directory or stream when copying from a stream' + is "$output" 'Error: destination must be a directory when copying from stdin' # Destination must be a directory (on an absent path). run_podman 125 cp /dev/stdin cpcontainer:/tmp/IdoNotExist < $tar_file - is "$output" 'Error: destination must be a directory or stream when copying from a stream' + is "$output" 'Error: destination must be a directory when copying from stdin' run_podman rm -f cpcontainer } @test "podman cp from container to stdout" { - skip_if_remote "podman-remote does not yet handle cp" - srcdir=$PODMAN_TMPDIR/cp-test-stdout mkdir -p $srcdir rand_content=$(random_string 50) @@ -569,7 +499,7 @@ load helpers tar xvf $srcdir/stdout.tar -C $srcdir run cat $srcdir/file.txt is "$output" "$rand_content" - run 1 ls $srcfir/empty.txt + run 1 ls $srcdir/empty.txt rm -f $srcdir/* # Copy directory. @@ -579,9 +509,9 @@ load helpers fi tar xvf $srcdir/stdout.tar -C $srcdir - run cat $srcdir/file.txt + run cat $srcdir/tmp/file.txt is "$output" "$rand_content" - run cat $srcdir/empty.txt + run cat $srcdir/tmp/empty.txt is "$output" "" run_podman rm -f cpcontainer diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 8e9a2d613..0e83a184b 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -345,7 +345,7 @@ EOF # all commands after 'podman build' would silently be ignored. # In the test below, prior to #8092, the 'sed' would not get # any input, and we would never see $random3 in the output. - # And, we use 'sed' to massage $random3 juuuuust on the remote + # And, we use 'sed' to massage $random3 just on the remote # chance that podman itself could pass stdin through. results=$(echo $random3 | ( echo $random1 @@ -424,6 +424,23 @@ EOF run_podman rmi -a --force } +@test "podman build --logfile test" { + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + tmpbuilddir=$tmpdir/build + mkdir -p $tmpbuilddir + dockerfile=$tmpbuilddir/Dockerfile + cat >$dockerfile <<EOF +FROM $IMAGE +EOF + + run_podman build -t build_test --format=docker --logfile=$tmpdir/logfile $tmpbuilddir + run cat $tmpdir/logfile + is "$output" ".*STEP 2: COMMIT" "COMMIT seen in log" + + run_podman rmi -f build_test +} + function teardown() { # A timeout or other error in 'build' can leave behind stale images # that podman can't even see and which will cascade into subsequent diff --git a/test/system/260-sdnotify.bats b/test/system/260-sdnotify.bats index c99ba4fa6..a5fa0f4e6 100644 --- a/test/system/260-sdnotify.bats +++ b/test/system/260-sdnotify.bats @@ -100,8 +100,17 @@ function _assert_mainpid_is_conmon() { run_podman logs sdnotify_conmon_c is "$output" "READY" "\$NOTIFY_SOCKET in container" + # The 'echo's help us debug failed runs run cat $_SOCAT_LOG - is "${lines[-1]}" "READY=1" "final output from sdnotify" + echo "socat log:" + echo "$output" + + # ARGH! 'READY=1' should always be the last output line. But sometimes, + # for reasons unknown, we get an extra MAINPID=xxx after READY=1 (#8718). + # Who knows if this is a systemd bug, or conmon, or what. I don't + # even know where to begin asking. So, to eliminate the test flakes, + # we look for READY=1 _anywhere_ in the output, not just the last line. + is "$output" ".*READY=1.*" "sdnotify sent READY=1" _assert_mainpid_is_conmon "${lines[0]}" diff --git a/test/system/600-completion.bats b/test/system/600-completion.bats index 1e43cdc41..8cac2c9aa 100644 --- a/test/system/600-completion.bats +++ b/test/system/600-completion.bats @@ -8,6 +8,17 @@ load helpers +# Returns true if we are able to podman-pause +function _can_pause() { + # Even though we're just trying completion, not an actual unpause, + # podman barfs with: + # Error: unpause is not supported for cgroupv1 rootless containers + if is_rootless && is_cgroupsv1; then + return 1 + fi + return 0 +} + function check_shell_completion() { local count=0 @@ -61,8 +72,9 @@ function check_shell_completion() { if ! is_remote; then run_completion "$@" $cmd "--" # If this fails there is most likely a problem with the cobra library - is "${lines[0]}" "--.*" "Found flag in suggestions" - [ ${#lines[@]} -gt 2 ] || die "No flag suggestions" + is "${lines[0]}" "--.*" \ + "$* $cmd: flag(s) listed in suggestions" + [ ${#lines[@]} -gt 2 ] || die "$* $cmd: No flag suggestions" _check_completion_end NoFileComp fi # continue the outer for args loop @@ -70,8 +82,14 @@ function check_shell_completion() { ;; *CONTAINER*) + # podman unpause fails early on rootless cgroupsv1 + if [[ $cmd = "unpause" ]] && ! _can_pause; then + continue 2 + fi + run_completion "$@" $cmd "${extra_args[@]}" "" - is "$output" ".*-$random_container_name${nl}" "Found expected container in suggestions" + is "$output" ".*-$random_container_name${nl}" \ + "$* $cmd: actual container listed in suggestions" match=true # resume @@ -79,7 +97,8 @@ function check_shell_completion() { *POD*) run_completion "$@" $cmd "${extra_args[@]}" "" - is "$output" ".*-$random_pod_name${nl}" "Found expected pod in suggestions" + is "$output" ".*-$random_pod_name${nl}" \ + "$* $cmd: actual pod listed in suggestions" _check_completion_end NoFileComp match=true @@ -88,16 +107,20 @@ function check_shell_completion() { *IMAGE*) run_completion "$@" $cmd "${extra_args[@]}" "" - is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" "Found expected image in suggestions" + is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" \ + "$* $cmd: actual image listed in suggestions" # check that we complete the image with and without tag after at least one char is typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_name:0:1}" - is "$output" ".*$random_image_name:$random_image_tag${nl}" "Found expected image with tag in suggestions" - is "$output" ".*$random_image_name${nl}" "Found expected image without tag in suggestions" + is "$output" ".*$random_image_name:$random_image_tag${nl}" \ + "$* $cmd: image name:tag included in suggestions" + is "$output" ".*$random_image_name${nl}" \ + "$* $cmd: image name(w/o tag) included in suggestions" # check that we complete the image id after at least two chars are typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_id:0:2}" - is "$output" ".*$random_image_id${nl}" "Found expected image id in suggestions" + is "$output" ".*$random_image_id${nl}" \ + "$* $cmd: image id included in suggestions when two leading characters present in command line" match=true # resume @@ -105,7 +128,8 @@ function check_shell_completion() { *NETWORK*) run_completion "$@" $cmd "${extra_args[@]}" "" - is "$output" ".*$random_network_name${nl}" "Found network in suggestions" + is "$output" ".*$random_network_name${nl}" \ + "$* $cmd: actual network listed in suggestions" _check_completion_end NoFileComp match=true @@ -114,7 +138,8 @@ function check_shell_completion() { *VOLUME*) run_completion "$@" $cmd "${extra_args[@]}" "" - is "$output" ".*$random_volume_name${nl}" "Found volume in suggestions" + is "$output" ".*$random_volume_name${nl}" \ + "$* $cmd: actual volume listed in suggestions" _check_completion_end NoFileComp match=true @@ -126,14 +151,14 @@ function check_shell_completion() { ### FIXME how can we get the configured registries? _check_completion_end NoFileComp ### FIXME this fails if no registries are configured - [[ ${#lines[@]} -gt 2 ]] || die "No registries found in suggestions" + [[ ${#lines[@]} -gt 2 ]] || die "$* $cmd: No REGISTRIES found in suggestions" match=true # resume ;;& *PATH* | *CONTEXT* | *KUBEFILE* | *COMMAND* | *ARG...* | *URI*) - # default shell completion should be done for everthing which accepts a path + # default shell completion should be done for everything which accepts a path run_completion "$@" $cmd "${extra_args[@]}" "" # cp is a special case it returns ShellCompDirectiveNoSpace @@ -141,7 +166,7 @@ function check_shell_completion() { _check_completion_end NoSpace else _check_completion_end Default - [[ ${#lines[@]} -eq 2 ]] || die "Suggestions are in the output" + [[ ${#lines[@]} -eq 2 ]] || die "$* $cmd: Suggestions are in the output" fi ;; @@ -172,7 +197,7 @@ function check_shell_completion() { run_completion "$@" $cmd "${extra_args[@]}" "" _check_completion_end NoFileComp if [ ${#lines[@]} -gt 2 ]; then - # checking for line count is not enough since we may inlcude additional debug output + # checking for line count is not enough since we may include additional debug output # lines starting with [Debug] are allowed i=0 length=$(( ${#lines[@]} - 2 )) @@ -212,7 +237,9 @@ function _check_completion_end() { run_podman create --name created-$random_container_name $IMAGE run_podman run --name running-$random_container_name -d $IMAGE top run_podman run --name pause-$random_container_name -d $IMAGE top - run_podman pause pause-$random_container_name + if _can_pause; then + run_podman pause pause-$random_container_name + fi run_podman run --name exited-$random_container_name -d $IMAGE echo exited # create pods for each state diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 6a7c6cc42..0572c6866 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -14,7 +14,7 @@ PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODM IMAGE=$PODMAN_TEST_IMAGE_FQN # Default timeout for a podman command. -PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-60} +PODMAN_TIMEOUT=${PODMAN_TIMEOUT:-120} # Prompt to display when logging podman commands; distinguish root/rootless _LOG_PROMPT='$' @@ -168,8 +168,11 @@ function run_podman() { if [ "$status" -eq 124 ]; then if expr "$output" : ".*timeout: sending" >/dev/null; then - echo "*** TIMED OUT ***" - false + # It's possible for a subtest to _want_ a timeout + if [[ "$expected_rc" != "124" ]]; then + echo "*** TIMED OUT ***" + false + fi fi fi @@ -259,6 +262,31 @@ function is_cgroupsv2() { test "$cgroup_type" = "cgroup2fs" } +# rhbz#1895105: rootless journald is unavailable except to users in +# certain magic groups; which our testuser account does not belong to +# (intentional: that is the RHEL default, so that's the setup we test). +function journald_unavailable() { + if ! is_rootless; then + # root must always have access to journal + return 1 + fi + + run journalctl -n 1 + if [[ $status -eq 0 ]]; then + return 1 + fi + + if [[ $output =~ permission ]]; then + return 0 + fi + + # This should never happen; if it does, it's likely that a subsequent + # test will fail. This output may help track that down. + echo "WEIRD: 'journalctl -n 1' failed with a non-permission error:" + echo "$output" + return 1 +} + ########################### # _add_label_if_missing # make sure skip messages include rootless/remote ########################### @@ -315,6 +343,15 @@ function skip_if_cgroupsv1() { fi } +################################## +# skip_if_journald_unavailable # rhbz#1895105: rootless journald permissions +################################## +function skip_if_journald_unavailable { + if journald_unavailable; then + skip "Cannot use rootless journald on this system" + fi +} + ######### # die # Abort with helpful message ######### diff --git a/test/utils/common_function_test.go b/test/utils/common_function_test.go index 0bbc31d5b..2f9e6f2c7 100644 --- a/test/utils/common_function_test.go +++ b/test/utils/common_function_test.go @@ -64,7 +64,7 @@ var _ = Describe("Common functions test", func() { Expect(host.Version).To(Equal(strings.Trim(ver, "\""))) } }, - Entry("Configure file is not exist.", "/tmp/notexist", "", "", true), + Entry("Configure file is not exist.", "/tmp/nonexistent", "", "", true), Entry("Item value with and without \"", "/tmp/os-release.test", "fedora", "\"28\"", false), Entry("Item empty with and without \"", "/tmp/os-release.test", "", "\"\"", false), ) @@ -142,7 +142,7 @@ var _ = Describe("Common functions test", func() { Expect(Containerized()).To(Equal(expect)) }, Entry("Set container in env", "", true, false, true), - Entry("Can not read from file", "/tmp/notexist", false, false, false), + Entry("Can not read from file", "/tmp/nonexistent", false, false, false), Entry("Docker in cgroup file", "/tmp/cgroup.test", false, true, true), Entry("Docker not in cgroup file", "/tmp/cgroup.test", false, true, false), ) diff --git a/test/utils/utils.go b/test/utils/utils.go index 027e96427..f21584537 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -432,7 +432,7 @@ func IsKernelNewerThan(version string) (bool, error) { } -// IsCommandAvaible check if command exist +// IsCommandAvailable check if command exist func IsCommandAvailable(command string) bool { check := exec.Command("bash", "-c", strings.Join([]string{"command -v", command}, " ")) err := check.Run() |