diff options
Diffstat (limited to 'test')
40 files changed, 1535 insertions, 112 deletions
diff --git a/test/README.md b/test/README.md index d7710cc95..769bdbfd7 100644 --- a/test/README.md +++ b/test/README.md @@ -84,7 +84,7 @@ file itself. Consider the following actual test: It("podman inspect bogus pod", func() { session := podmanTest.Podman([]string{"pod", "inspect", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) ``` diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 64aafa013..564c7bed5 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -18,7 +18,7 @@ t HEAD libpod/_ping 200 for i in /version version; do t GET $i 200 \ .Components[0].Name="Podman Engine" \ - .Components[0].Details.APIVersion~3[0-9.-]\\+ \ + .Components[0].Details.APIVersion~4[0-9.-]\\+ \ .Components[0].Details.MinAPIVersion=3.1.0 \ .Components[0].Details.Os=linux \ .ApiVersion=1.40 \ diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index c5b2f5ec1..610d3e36d 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -65,6 +65,16 @@ t GET libpod/containers/json?last=1 200 \ cid=$(jq -r '.[0].Id' <<<"$output") +if root; then + t GET libpod/containers/stats?containers='[$cid]' 200 +else + if have_cgroupsv2; then + t GET libpod/containers/stats?containers='[$cid]' 200 + else + t GET libpod/containers/stats?containers='[$cid]' 409 + fi +fi + t DELETE libpod/containers/$cid 204 # Issue #6799: it should be possible to start a container, even w/o args. diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index b639e05f9..fd1542293 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -174,6 +174,8 @@ t POST libpod/volumes/create \ # with date way back in the past, volume should not be deleted (compat api) t POST volumes/prune?filters='{"until":["500000"]}' 200 t GET libpod/volumes/json?filters='{"label":["testuntilcompat"]}' 200 length=1 +t GET libpod/volumes/json?filters='{"until":["500000"]}' 200 length=0 +t GET libpod/volumes/json?filters='{"until":["5000000000"]}' 200 length=1 # with date far in the future, volume should be deleted (compat api) t POST volumes/prune?filters='{"until":["5000000000"]}' 200 diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index f252bd401..dbad6824f 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -33,19 +33,49 @@ class ContainerTestCase(APITestCase): self.assertId(r.content) _ = parse(r.json()["Created"]) + r = requests.post( self.podman_url + "/v1.40/containers/create?name=topcontainer", - json={"Cmd": ["top"], "Image": "alpine:latest"}, + json={"Cmd": ["top"], + "Image": "alpine:latest", + "Healthcheck": { + "Test": ["CMD", "pidof", "top"], + "Interval": 5000000000, + "Timeout": 2000000000, + "Retries": 3, + "StartPeriod": 5000000000 + } + }, ) self.assertEqual(r.status_code, 201, r.text) payload = r.json() container_id = payload["Id"] self.assertIsNotNone(container_id) - r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/json") + r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json") + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + self.assertIsNone(out["State"].get("Health")) + self.assertListEqual(["CMD", "pidof", "top"], out["Config"]["Healthcheck"]["Test"]) + self.assertEqual(5000000000, out["Config"]["Healthcheck"]["Interval"]) + self.assertEqual(2000000000, out["Config"]["Healthcheck"]["Timeout"]) + self.assertEqual(3, out["Config"]["Healthcheck"]["Retries"]) + self.assertEqual(5000000000, out["Config"]["Healthcheck"]["StartPeriod"]) + + r = requests.get(self.uri(f"/containers/{container_id}/json")) self.assertEqual(r.status_code, 200, r.text) self.assertId(r.content) out = r.json() + hc = out["Config"]["Healthcheck"]["Test"] + self.assertListEqual(["CMD", "pidof", "top"], hc) + + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json") + self.assertEqual(r.status_code, 200, r.text) + out = r.json() state = out["State"]["Health"] self.assertIsInstance(state, dict) diff --git a/test/buildah-bud/apply-podman-deltas b/test/buildah-bud/apply-podman-deltas index 41c84257f..18b3d56f9 100755 --- a/test/buildah-bud/apply-podman-deltas +++ b/test/buildah-bud/apply-podman-deltas @@ -56,22 +56,31 @@ function errmsg() { done } -# skip: used to add a 'skip' to one specific test -function skip() { +# _skip: used to add a 'skip' or 'skip_if_remote' to one specific test +function _skip() { + local skip=$1; shift local reason=$1; shift # All further arguments are test names for t in "$@"; do if fgrep -qx "@test \"$t\" {" $BUD; then - $ECHO "@test \"$t\" : skip \"$reason\"" + $ECHO "@test \"$t\" : $skip \"$reason\"" t=${t//\//\\/} - sed -i -e "/^\@test \"$t\" {/ a \ \ skip \"$reason\"" $BUD + sed -i -e "/^\@test \"$t\" {/ a \ \ $skip \"$reason\"" $BUD else - warn "[skip] Did not find test \"$t\" in $BUD" + warn "[$skip] Did not find test \"$t\" in $BUD" fi done } +function skip() { + _skip "skip" "$@" +} + +function skip_if_remote() { + _skip "skip_if_remote" "$@" +} + # END handlers ############################################################################### # BEGIN user-customizable section @@ -79,14 +88,14 @@ function skip() { # These are the hand-maintained exceptions. This is what you want to edit # or update as needed. # -# There are two directives you can use below: +# There are three directives you can use below: # # errmsg "old-message" "new-message" "test name" ["test name"...] # # This replaced "old-message" with "new-message" in @test "test name". # It is used when a podman error message differs from buildah's. # -# skip "reason" "test name" ["test name"...] +# [skip | skip_if_remote] "reason" "test name" ["test name"...] # # This adds a 'skip' statement as the first line of @test "test name". # It is used when a test does not work in podman, either for permanent @@ -126,6 +135,7 @@ errmsg "no such file or directory" \ ############################################################################### # BEGIN tests that don't make sense under podman due to fundamental differences + skip "N/A under podman" \ "bud-flags-order-verification" @@ -147,13 +157,13 @@ skip "Too much effort to spin up a local registry" \ skip "FIXME FIXME FIXME: argument-order incompatible with podman" \ "bud-squash-hardlinks" -skip "FIXME FIXME FIXME we'll figure these out later" \ - "bud-multi-stage-nocache-nocommit" \ - "bud with --cgroup-parent" +skip "FIXME FIXME FIXME: this passes on Ed's laptop, fails in CI??" \ + "bud-multi-stage-nocache-nocommit" -# see https://github.com/containers/podman/pull/10147#issuecomment-832503633 -skip "FIXME FIXME FIXME podman save/load has been fixed (but not yet used in Buildah CI)" \ - "bud with --layers and --no-cache flags" +# This will probably never work: buildah and podman have incompatible defaults +# Documented in https://github.com/containers/podman/issues/10412 +skip "buildah runs with --cgroup-manager=cgroupfs, podman with systemd" \ + "bud with --cgroup-parent" # see https://github.com/containers/podman/pull/10829 skip "FIXME FIXME FIXME - requires updated CI images (#10829)" \ @@ -164,6 +174,31 @@ skip "FIXME FIXME FIXME - requires updated CI images (#10829)" \ ############################################################################### +# BEGIN tests which are skipped because they make no sense under podman-remote + +skip_if_remote "--target does not work with podman-remote" \ + "bud-target" + +skip_if_remote "--runtime not meaningful under podman-remote" \ + "bud with --runtime and --runtime-flag" + +skip_if_remote "secret files not implemented under podman-remote" \ + "bud with containerfile secret" \ + "bud with containerfile secret accessed on second RUN" \ + "bud with containerfile secret options" + +skip_if_remote "volumes don't work with podman-remote" \ + "buildah bud --volume" \ + "buildah-bud-policy" + +# See podman #9890 for discussion +skip_if_remote "--stdin option will not be implemented in podman-remote" \ + "bud test no --stdin" + +############################################################################### +# BEGIN tests which are skipped due to actual podman-remote bugs. + +############################################################################### # Done. exit $RC diff --git a/test/buildah-bud/buildah-tests.diff b/test/buildah-bud/buildah-tests.diff index 1c8592f7f..66d470648 100644 --- a/test/buildah-bud/buildah-tests.diff +++ b/test/buildah-bud/buildah-tests.diff @@ -1,23 +1,75 @@ -From a00508656599b24776982996fdb44d4874338fd4 Mon Sep 17 00:00:00 2001 +From d684753d6f00ee95720d8fb2e09c7ac19b37b01e Mon Sep 17 00:00:00 2001 From: Ed Santiago <santiago@redhat.com> Date: Tue, 9 Feb 2021 17:28:05 -0700 Subject: [PATCH] tweaks for running buildah tests under podman Signed-off-by: Ed Santiago <santiago@redhat.com> --- - tests/helpers.bash | 28 +++++++++++++++++++++++++--- - 1 file changed, 25 insertions(+), 3 deletions(-) + tests/helpers.bash | 71 +++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/tests/helpers.bash b/tests/helpers.bash -index 11deb367..08e73954 100644 +index 11deb367..44c71dad 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash -@@ -164,15 +164,37 @@ function run_buildah() { +@@ -34,6 +34,23 @@ function setup() { + ROOTDIR_OPTS="--root ${TESTDIR}/root --runroot ${TESTDIR}/runroot --storage-driver ${STORAGE_DRIVER}" + BUILDAH_REGISTRY_OPTS="--registries-conf ${TESTSDIR}/registries.conf --registries-conf-dir ${TESTDIR}/registries.d --short-name-alias-conf ${TESTDIR}/cache/shortnames.conf" + PODMAN_REGISTRY_OPTS="--registries-conf ${TESTSDIR}/registries.conf" ++ ++ PODMAN_SERVER_PID= ++ PODMAN_NATIVE="${PODMAN_BINARY} ${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ if [[ -n "$REMOTE" ]]; then ++ PODMAN_NATIVE="${PODMAN_BINARY%%-remote} ${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ # static CONTAINERS_CONF needed for capabilities test. As of 2021-07-01 ++ # no tests in bud.bats override this; if at some point any test does ++ # so, it will probably need to be skip_if_remote()d. ++ env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} $PODMAN_NATIVE system service --timeout=0 & ++ PODMAN_SERVER_PID=$! ++ local timeout=10 ++ while ((timeout > 0)); do ++ test -S /run/podman/podman.sock && return ++ sleep 0.2 ++ done ++ die "podman server never came up" ++ fi + } + + function starthttpd() { +@@ -57,6 +74,12 @@ function stophttpd() { + function teardown() { + stophttpd + ++ if [[ -n "$PODMAN_SERVER_PID" ]]; then ++ kill $PODMAN_SERVER_PID ++ wait $PODMAN_SERVER_PID ++ rm -f /run/podman/podman.sock ++ fi ++ + # Workaround for #1991 - buildah + overlayfs leaks mount points. + # Many tests leave behind /var/tmp/.../root/overlay and sub-mounts; + # let's find those and clean them up, otherwise 'rm -rf' fails. +@@ -129,7 +152,13 @@ function copy() { + } + + function podman() { +- command podman ${PODMAN_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" ++ echo "# ... podman $*" >&3 ++ ${PODMAN_BINARY} ${PODMAN_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" ++} ++ ++function podman-remote() { ++ echo "# ... podman-remote $*" >&3 ++ ${PODMAN_BINARY} ${ROOTDIR_OPTS} "$@" + } + + ################# +@@ -164,15 +193,40 @@ function run_buildah() { --retry) retry=3; shift;; # retry network flakes esac - + + local podman_or_buildah=${BUILDAH_BINARY} -+ local registry_opts=${BUILDAH_REGISTRY_OPTS} ++ local _opts="${ROOTDIR_OPTS} ${BUILDAH_REGISTRY_OPTS}" + if [[ $1 == "bud" || $1 == "build-using-dockerfile" ]]; then + shift + # podman defaults to --layers=true; buildah to --false. @@ -29,7 +81,10 @@ index 11deb367..08e73954 100644 + set "build" "--force-rm=false" "--layers=false" "$@" + fi + podman_or_buildah=${PODMAN_BINARY} -+ registry_opts=${PODMAN_REGISTRY_OPTS} ++ _opts="${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ if [[ -n "$REMOTE" ]]; then ++ _opts= ++ fi + + # podman always exits 125 where buildah exits 1 or 2 + case $expected_rc in @@ -41,17 +96,31 @@ index 11deb367..08e73954 100644 # Remember command args, for possible use in later diagnostic messages - MOST_RECENT_BUILDAH_COMMAND="buildah $*" + MOST_RECENT_BUILDAH_COMMAND="$cmd_basename $*" - + while [ $retry -gt 0 ]; do retry=$(( retry - 1 )) - + # stdout is only emitted upon error; this echo is to help a debugger - echo "\$ $BUILDAH_BINARY $*" - run env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${BUILDAH_BINARY} ${BUILDAH_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" + echo "\$ $cmd_basename $*" -+ run env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${podman_or_buildah} ${registry_opts} ${ROOTDIR_OPTS} "$@" ++ run env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${podman_or_buildah} ${_opts} "$@" # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" --- +@@ -396,3 +450,12 @@ function skip_if_no_docker() { + skip "this test needs actual docker, not podman-docker" + fi + } ++ ++#################### ++# skip_if_remote # (only applicable for podman) ++#################### ++function skip_if_remote() { ++ if [[ -n "$REMOTE" ]]; then ++ skip "${1:-test does not work with podman-remote}" ++ fi ++} +-- 2.31.1 + diff --git a/test/buildah-bud/make-new-buildah-diffs b/test/buildah-bud/make-new-buildah-diffs index 11987e376..3d0a77008 100644 --- a/test/buildah-bud/make-new-buildah-diffs +++ b/test/buildah-bud/make-new-buildah-diffs @@ -56,8 +56,6 @@ if [[ -n "$patch2" ]]; then die "Internal error: I thought I checked for squashed commits, but still see $patch2" fi -# All looks good. Now write that patch into its proper place in the -# podman repo. The sed and tac mess strips trailing whitespace and -# empty lines; we need to do this to pass github CI checks. -sed -e 's/ \+$//' <0001-*.patch |\ - tac | sed -e '/./,$!d' | tac >| ../test/buildah-bud/buildah-tests.diff +# All looks good. We can now copy that patch into its proper place in the +# podman repo. +cp 0001-*.patch ../test/buildah-bud/buildah-tests.diff diff --git a/test/buildah-bud/run-buildah-bud-tests b/test/buildah-bud/run-buildah-bud-tests index a37e90dc4..eb8de5618 100755 --- a/test/buildah-bud/run-buildah-bud-tests +++ b/test/buildah-bud/run-buildah-bud-tests @@ -69,8 +69,7 @@ REMOTE= # If remote, start server & change path if [[ "${PODBIN_NAME:-}" = "remote" ]]; then REMOTE=1 - echo "$ME: remote tests are not working yet" >&2 - exit 1 + PODMAN_BINARY+="-remote" fi function die() { @@ -214,6 +213,7 @@ review the test failure and double-check your changes. (set -x;sudo env TMPDIR=/var/tmp \ PODMAN_BINARY=$PODMAN_BINARY \ + REMOTE=$REMOTE \ BUILDAH_BINARY=$(pwd)/bin/buildah \ COPY_BINARY=$(pwd)/bin/copy \ bats "${bats_filter[@]}" tests/bud.bats) diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index b5bbfcd5c..1c9a8dc6f 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -1,11 +1,13 @@ package integration import ( + "fmt" "net" "os" "os/exec" "strings" + "github.com/containers/podman/v3/pkg/checkpoint/crutils" "github.com/containers/podman/v3/pkg/criu" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" @@ -46,7 +48,7 @@ var _ = Describe("Podman checkpoint", func() { Skip("OCI runtime does not support checkpoint/restore") } - if !criu.CheckForCriu() { + if !criu.CheckForCriu(criu.MinCriuVersion) { Skip("CRIU is missing or too old.") } // Only Fedora 29 and newer has a new enough selinux-policy and @@ -977,4 +979,164 @@ var _ = Describe("Podman checkpoint", func() { // Remove exported checkpoint os.Remove(fileName) }) + + namespaceCombination := []string{ + "cgroup,ipc,net,uts,pid", + "cgroup,ipc,net,uts", + "cgroup,ipc,net", + "cgroup,ipc", + "ipc,net,uts,pid", + "ipc,net,uts", + "ipc,net", + "net,uts,pid", + "net,uts", + "uts,pid", + } + for _, share := range namespaceCombination { + testName := fmt.Sprintf( + "podman checkpoint and restore container out of and into pod (%s)", + share, + ) + It(testName, func() { + if !criu.CheckForCriu(criu.PodCriuVersion) { + Skip("CRIU is missing or too old.") + } + if !crutils.CRRuntimeSupportsPodCheckpointRestore(podmanTest.OCIRuntime) { + Skip("runtime does not support pod restore") + } + // Create a pod + session := podmanTest.Podman([]string{ + "pod", + "create", + "--share", + share, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{ + "run", + "-d", + "--rm", + "--pod", + podID, + ALPINE, + "top", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + cid := session.OutputToString() + + fileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + // Checkpoint the container + result := podmanTest.Podman([]string{ + "container", + "checkpoint", + "-e", + fileName, + cid, + }) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + // Remove the pod and create a new pod + result = podmanTest.Podman([]string{ + "pod", + "rm", + podID, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + // First create a pod with different shared namespaces. + // Restore should fail + + wrongShare := share[:strings.LastIndex(share, ",")] + + session = podmanTest.Podman([]string{ + "pod", + "create", + "--share", + wrongShare, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID = session.OutputToString() + + // Restore container with different port mapping + result = podmanTest.Podman([]string{ + "container", + "restore", + "--pod", + podID, + "-i", + fileName, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("does not share the")) + + // Remove the pod and create a new pod + result = podmanTest.Podman([]string{ + "pod", + "rm", + podID, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + session = podmanTest.Podman([]string{ + "pod", + "create", + "--share", + share, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID = session.OutputToString() + + // Restore container with different port mapping + result = podmanTest.Podman([]string{ + "container", + "restore", + "--pod", + podID, + "-i", + fileName, + }) + result.WaitWithDefaultTimeout() + + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{ + "rm", + "-f", + result.OutputToString(), + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + result = podmanTest.Podman([]string{ + "pod", + "rm", + "-fa", + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + // Remove exported checkpoint + os.Remove(fileName) + }) + } }) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 5a6cf7ffb..2e48e1763 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -811,7 +811,7 @@ func generateNetworkConfig(p *PodmanTestIntegration) (string, string) { func (p *PodmanTestIntegration) removeCNINetwork(name string) { session := p.Podman([]string{"network", "rm", "-f", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeNumerically("<=", 1)) + Expect(session.ExitCode()).To(BeNumerically("<=", 1), "Exit code must be 0 or 1") } func (p *PodmanSessionIntegration) jq(jqCommand string) (string, error) { diff --git a/test/e2e/container_create_volume_test.go b/test/e2e/container_create_volume_test.go new file mode 100644 index 000000000..001698239 --- /dev/null +++ b/test/e2e/container_create_volume_test.go @@ -0,0 +1,127 @@ +package integration + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +func buildDataVolumeImage(pTest *PodmanTestIntegration, image, data, dest string) { + // Create a dummy file for data volume + dummyFile := filepath.Join(pTest.TempDir, data) + err := ioutil.WriteFile(dummyFile, []byte(data), 0644) + Expect(err).To(BeNil()) + + // Create a data volume container image but no CMD binary in it + containerFile := fmt.Sprintf(`FROM scratch +CMD doesnotexist.sh +ADD %s %s/ +VOLUME %s/`, data, dest, dest) + pTest.BuildImage(containerFile, image, "false") +} + +func createContainersConfFile(pTest *PodmanTestIntegration) { + configPath := filepath.Join(pTest.TempDir, "containers.conf") + containersConf := []byte(fmt.Sprintf("[containers]\nprepare_volume_on_create = true\n")) + err := ioutil.WriteFile(configPath, containersConf, os.ModePerm) + Expect(err).To(BeNil()) + + // Set custom containers.conf file + os.Setenv("CONTAINERS_CONF", configPath) + if IsRemote() { + pTest.RestartRemoteService() + } +} + +func checkDataVolumeContainer(pTest *PodmanTestIntegration, image, cont, dest, data string) { + create := pTest.Podman([]string{"create", "--name", cont, image}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + inspect := pTest.InspectContainer(cont) + Expect(len(inspect)).To(Equal(1)) + Expect(len(inspect[0].Mounts)).To(Equal(1)) + Expect(inspect[0].Mounts[0].Destination).To(Equal(dest)) + + mntName, mntSource := inspect[0].Mounts[0].Name, inspect[0].Mounts[0].Source + + volList := pTest.Podman([]string{"volume", "list", "--quiet"}) + volList.WaitWithDefaultTimeout() + Expect(volList).Should(Exit(0)) + Expect(len(volList.OutputToStringArray())).To(Equal(1)) + Expect(volList.OutputToStringArray()[0]).To(Equal(mntName)) + + // Check the mount source directory + files, err := ioutil.ReadDir(mntSource) + Expect(err).To(BeNil()) + + if data == "" { + Expect(len(files)).To(Equal(0)) + } else { + Expect(len(files)).To(Equal(1)) + Expect(files[0].Name()).To(Equal(data)) + } +} + +var _ = Describe("Podman create data volume", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + os.Unsetenv("CONTAINERS_CONF") + }) + + It("podman create with volume data copy turned off", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the default containers.conf and + // check that the volume is not copied from the image. + checkDataVolumeContainer(podmanTest, imgName, "ctr-nocopy", volDest, "") + }) + + It("podman create with volume data copy turned on", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the custom containers.conf and + // check that the volume is copied from the image. + createContainersConfFile(podmanTest) + + checkDataVolumeContainer(podmanTest, imgName, "ctr-copy", volDest, volData) + }) + + It("podman run with volume data copy turned on", func() { + // Create a container with the custom containers.conf and + // check that the container is run successfully + createContainersConfFile(podmanTest) + + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "echo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) +}) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index 28040ecfd..899c84a14 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -174,6 +174,16 @@ var _ = Describe("Podman healthcheck run", func() { Expect(inspect[0].State.Healthcheck.Status).To(Equal("healthy")) }) + It("podman healthcheck unhealthy but valid arguments check", func() { + session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "[\"ls\", \"/foo\"]", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(Exit(1)) + }) + It("podman healthcheck single healthy result changes failed to healthy", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go new file mode 100644 index 000000000..9fd8d7e27 --- /dev/null +++ b/test/e2e/image_scp_test.go @@ -0,0 +1,104 @@ +package integration + +import ( + "io/ioutil" + "os" + + "github.com/containers/common/pkg/config" + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman image scp", func() { + ConfPath := struct { + Value string + IsSet bool + }{} + var ( + tempdir string + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + ConfPath.Value, ConfPath.IsSet = os.LookupEnv("CONTAINERS_CONF") + conf, err := ioutil.TempFile("", "containersconf") + if err != nil { + panic(err) + } + os.Setenv("CONTAINERS_CONF", conf.Name()) + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + os.Remove(os.Getenv("CONTAINERS_CONF")) + if ConfPath.IsSet { + os.Setenv("CONTAINERS_CONF", ConfPath.Value) + } else { + os.Unsetenv("CONTAINERS_CONF") + } + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman image scp quiet flag", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + scp := podmanTest.Podman([]string{"image", "scp", "-q", ALPINE}) + scp.WaitWithDefaultTimeout() + Expect(scp).To(Exit(0)) + }) + + It("podman image scp bogus image", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + scp := podmanTest.Podman([]string{"image", "scp", "FOOBAR"}) + scp.WaitWithDefaultTimeout() + Expect(scp).To(ExitWithError()) + }) + + It("podman image scp with proper connection", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + cmd := []string{"system", "connection", "add", + "--default", + "QA", + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(Equal("QA")) + Expect(cfg.Engine.ServiceDestinations["QA"]).To(Equal( + config.Destination{ + URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + }, + )) + + scp := podmanTest.Podman([]string{"image", "scp", ALPINE, "QA::"}) + scp.Wait(45) + // exit with error because we cannot make an actual ssh connection + // This tests that the input we are given is validated and prepared correctly + // Error: failed to connect: dial tcp: address foo: missing port in address + Expect(scp).To(ExitWithError()) + Expect(scp.ErrorToString()).To(ContainSubstring( + "Error: failed to connect: dial tcp 66.151.147.142:2222: i/o timeout", + )) + + }) + +}) diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go index 6088d991f..7ad1fc1f2 100644 --- a/test/e2e/login_logout_test.go +++ b/test/e2e/login_logout_test.go @@ -97,6 +97,24 @@ var _ = Describe("Podman login and logout", func() { os.RemoveAll(certDirPath) }) + readAuthInfo := func(filePath string) map[string]interface{} { + authBytes, err := ioutil.ReadFile(filePath) + Expect(err).To(BeNil()) + + var authInfo map[string]interface{} + err = json.Unmarshal(authBytes, &authInfo) + Expect(err).To(BeNil()) + fmt.Println(authInfo) + + const authsKey = "auths" + Expect(authInfo).To(HaveKey(authsKey)) + + auths, ok := authInfo[authsKey].(map[string]interface{}) + Expect(ok).To(BeTrue()) + + return auths + } + It("podman login and logout", func() { session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test", server}) session.WaitWithDefaultTimeout() @@ -151,10 +169,7 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - authInfo, _ := ioutil.ReadFile(authFile) - var info map[string]interface{} - json.Unmarshal(authInfo, &info) - fmt.Println(info) + readAuthInfo(authFile) // push should fail with nonexistent authfile session = podmanTest.Podman([]string{"push", "--authfile", "/tmp/nonexistent", ALPINE, testImg}) @@ -284,4 +299,204 @@ var _ = Describe("Podman login and logout", func() { session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) }) + + It("podman login and logout with repository", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepository := server + "/podmantest" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepository)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepository)) + }) + + It("podman login and logout with repository and specified image", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testTarget)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + }) + + It("podman login and logout with repository with fallback", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepos := []string{ + server + "/podmantest", + server, + } + for _, testRepo := range testRepos { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepo, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepos[0])) + Expect(authInfo).To(HaveKey(testRepos[1])) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[0], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[1], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepos[0])) + Expect(authInfo).NotTo(HaveKey(testRepos[1])) + }) + + It("podman login with repository invalid arguments", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + for _, invalidArg := range []string{ + "https://" + server + "/podmantest", + server + "/podmantest/image:latest", + } { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + invalidArg, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitWithError()) + } + }) + + It("podman login and logout with repository push with invalid auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + // only `server` contains the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server)), 0644) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/podmantest/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + }) + + It("podman login and logout with repository pull with wrong auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // only `server + /podmantest` and `server` have the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest/test-alpine": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp0ZXN0" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server, server)), 0644) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{ + "pull", + "--authfile", authFile, + server + "/podmantest/test-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + }) }) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 507fab461..0a973b802 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "strings" + "time" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" @@ -135,6 +136,34 @@ var _ = Describe("Podman logs", func() { Expect(len(results.OutputToStringArray())).To(Equal(3)) }) + It("until duration 10m: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--until", "10m", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(0)) + }) + + It("until time NOW: "+log, func() { + + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + now := time.Now() + now = now.Add(time.Minute * 1) + nowS := now.Format(time.RFC3339) + results := podmanTest.Podman([]string{"logs", "--until", nowS, cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + It("latest and container name should fail: "+log, func() { results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) results.WaitWithDefaultTimeout() diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 2bec88020..fb4a144fa 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -244,10 +244,59 @@ var _ = Describe("Podman network create", func() { Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) + Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) + Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) + Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) + + _, subnet11, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + Expect(err).To(BeNil()) + _, subnet12, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + Expect(err).To(BeNil()) + // Once a container executes a new network, the nic will be created. We should clean those up // best we can defer removeNetworkDevice(bridgePlugin.BrName) + // create a second network to check the auto assigned ipv4 subnet does not overlap + // https://github.com/containers/podman/issues/11032 + netName2 := "dual-" + stringid.GenerateNonCryptoID() + nc = podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:6:3:2:1::/64", "--ipv6", netName2}) + nc.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(netName2) + Expect(nc).Should(Exit(0)) + + // Inspect the network configuration + inspect = podmanTest.Podman([]string{"network", "inspect", netName2}) + inspect.WaitWithDefaultTimeout() + + // JSON the network configuration into something usable + err = json.Unmarshal([]byte(inspect.OutputToString()), &results) + Expect(err).To(BeNil()) + result = results[0] + Expect(result["name"]).To(Equal(netName2)) + + // JSON the bridge info + bridgePlugin, err = genericPluginsToBridge(result["plugins"], "bridge") + Expect(err).To(BeNil()) + Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) + Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) + Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) + Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) + Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) + + _, subnet21, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + Expect(err).To(BeNil()) + _, subnet22, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + Expect(err).To(BeNil()) + + // check that the subnets do not overlap + Expect(subnet11.Contains(subnet21.IP)).To(BeFalse()) + Expect(subnet12.Contains(subnet22.IP)).To(BeFalse()) + try := podmanTest.Podman([]string{"run", "-it", "--rm", "--network", netName, ALPINE, "sh", "-c", "ip addr show eth0 | grep global | awk ' /inet6 / {print $2}'"}) try.WaitWithDefaultTimeout() diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 5e303bf2f..66bfdefe7 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1729,7 +1729,7 @@ var _ = Describe("Podman play kube", func() { }) It("podman play kube with pull policy of missing", func() { - ctr := getCtr(withPullPolicy("missing"), withImage(BB)) + ctr := getCtr(withPullPolicy("Missing"), withImage(BB)) err := generateKubeYaml("pod", getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 63f55fb88..4c6788b9d 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -566,11 +566,11 @@ ENTRYPOINT ["sleep","99999"] ns := "ns:/proc/self/ns/" podCreate := podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podInspect := podmanTest.Podman([]string{"pod", "inspect", podName}) podInspect.WaitWithDefaultTimeout() - Expect(podInspect.ExitCode()).To(Equal(0)) + Expect(podInspect).Should(Exit(0)) podJSON := podInspect.InspectPodToJSON() Expect(podJSON.InfraConfig.PidNS).To(Equal("path")) @@ -579,11 +579,11 @@ ENTRYPOINT ["sleep","99999"] podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) podInspect.WaitWithDefaultTimeout() - Expect(podInspect.ExitCode()).To(Equal(0)) + Expect(podInspect).Should(Exit(0)) podJSON = podInspect.InspectPodToJSON() Expect(podJSON.InfraConfig.PidNS).To(Equal("pod")) @@ -592,11 +592,11 @@ ENTRYPOINT ["sleep","99999"] podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) podInspect.WaitWithDefaultTimeout() - Expect(podInspect.ExitCode()).To(Equal(0)) + Expect(podInspect).Should(Exit(0)) podJSON = podInspect.InspectPodToJSON() Expect(podJSON.InfraConfig.PidNS).To(Equal("host")) @@ -605,11 +605,11 @@ ENTRYPOINT ["sleep","99999"] podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) podInspect.WaitWithDefaultTimeout() - Expect(podInspect.ExitCode()).To(Equal(0)) + Expect(podInspect).Should(Exit(0)) podJSON = podInspect.InspectPodToJSON() Expect(podJSON.InfraConfig.PidNS).To(Equal("private")) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 68ccd0a94..ff70a8cf4 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -16,6 +16,11 @@ LABEL RUN podman --version RUN apk update RUN apk add bash`, ALPINE) +var emptyPruneImage = ` +FROM scratch +ENV test1=test1 +ENV test2=test2` + var _ = Describe("Podman prune", func() { var ( tempdir string @@ -110,8 +115,12 @@ var _ = Describe("Podman prune", func() { Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(numImages)) - // Now build a new image with dangling intermediate images. + // Now build an image and untag it. The (intermediate) images + // should be removed recursively during pruning. podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + session = podmanTest.Podman([]string{"untag", "alpine_bash:latest"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() @@ -136,26 +145,33 @@ var _ = Describe("Podman prune", func() { Expect(len(session.OutputToStringArray())).To(Equal(numImages - numPrunedImages)) }) - It("podman image prune skip cache images", func() { - podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + It("podman image prune - handle empty images", func() { + // As shown in #10832, empty images were not treated correctly + // in Podman. + podmanTest.BuildImage(emptyPruneImage, "empty:scratch", "true") - none := podmanTest.Podman([]string{"images", "-a"}) - none.WaitWithDefaultTimeout() - Expect(none).Should(Exit(0)) - hasNone, _ := none.GrepString("<none>") + session := podmanTest.Podman([]string{"images", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + hasNone, _ := session.GrepString("<none>") Expect(hasNone).To(BeTrue()) - prune := podmanTest.Podman([]string{"image", "prune", "-f"}) - prune.WaitWithDefaultTimeout() - Expect(prune).Should(Exit(0)) + // Nothing will be pruned. + session = podmanTest.Podman([]string{"image", "prune", "-f"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) - after := podmanTest.Podman([]string{"images", "-a"}) - after.WaitWithDefaultTimeout() - Expect(none).Should(Exit(0)) - // Check if all "dangling" images were pruned. - hasNoneAfter, _ := after.GrepString("<none>") - Expect(hasNoneAfter).To(BeFalse()) - Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) + // Now the image will be untagged, and its parent images will + // be removed recursively. + session = podmanTest.Podman([]string{"untag", "empty:scratch"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"image", "prune", "-f"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) }) It("podman image prune dangling images", func() { diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index e27ff27a4..aeb88e481 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -172,6 +172,43 @@ var _ = Describe("Podman ps", func() { Expect(fullCid).To(Equal(result.OutputToStringArray()[0])) }) + It("podman ps --filter network=container:<name>", func() { + ctrAlpha := "alpha" + container := podmanTest.Podman([]string{"run", "-dt", "--name", ctrAlpha, ALPINE, "top"}) + container.WaitWithDefaultTimeout() + Expect(container).Should(Exit(0)) + + ctrBravo := "bravo" + containerBravo := podmanTest.Podman([]string{"run", "-dt", "--network", "container:alpha", "--name", ctrBravo, ALPINE, "top"}) + containerBravo.WaitWithDefaultTimeout() + Expect(containerBravo).Should(Exit(0)) + + result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.Names}}", "--filter", "network=container:alpha"}) + result.WaitWithDefaultTimeout() + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring("bravo")) + }) + + It("podman ps --filter network=container:<id>", func() { + ctrAlpha := "first" + container := podmanTest.Podman([]string{"run", "-dt", "--name", ctrAlpha, ALPINE, "top"}) + container.WaitWithDefaultTimeout() + cid := container.OutputToString() + Expect(container).Should(Exit(0)) + + ctrBravo := "second" + containerBravo := podmanTest.Podman([]string{"run", "-dt", "--network", "container:" + cid, "--name", ctrBravo, ALPINE, "top"}) + containerBravo.WaitWithDefaultTimeout() + Expect(containerBravo).Should(Exit(0)) + + result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.Names}}", "--filter", "network=container:" + cid}) + result.WaitWithDefaultTimeout() + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring("second")) + }) + It("podman ps namespace flag", func() { _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index fd9656f4d..c377f158d 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -44,7 +44,7 @@ var _ = Describe("Podman pull", func() { session = podmanTest.Podman([]string{"pull", "busybox:latest", "docker.io/library/ibetthisdoesnotexistfr:random", "alpine"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) - expectedError := "Error initializing source docker://ibetthisdoesnotexistfr:random" + expectedError := "initializing source docker://ibetthisdoesnotexistfr:random" found, _ := session.ErrorGrepString(expectedError) Expect(found).To(Equal(true)) diff --git a/test/e2e/run_cgroup_parent_test.go b/test/e2e/run_cgroup_parent_test.go index 300c3a8e0..3e261961b 100644 --- a/test/e2e/run_cgroup_parent_test.go +++ b/test/e2e/run_cgroup_parent_test.go @@ -2,6 +2,7 @@ package integration import ( "fmt" + "io/ioutil" "os" "path/filepath" "strings" @@ -80,7 +81,21 @@ var _ = Describe("Podman run with --cgroup-parent", func() { exec.WaitWithDefaultTimeout() Expect(exec).Should(Exit(0)) - cgroup := filepath.Dir(strings.TrimRight(strings.Replace(exec.OutputToString(), "0::", "", -1), "\n")) + containerCgroup := strings.TrimRight(strings.Replace(exec.OutputToString(), "0::", "", -1), "\n") + + content, err := ioutil.ReadFile(filepath.Join("/sys/fs/cgroup", containerCgroup, "cgroup.procs")) + Expect(err).To(BeNil()) + + // Move the container process to a sub cgroup + subCgroupPath := filepath.Join(filepath.Join("/sys/fs/cgroup", containerCgroup, "old-container")) + + err = os.MkdirAll(subCgroupPath, 0755) + Expect(err).To(BeNil()) + + err = ioutil.WriteFile(filepath.Join(subCgroupPath, "cgroup.procs"), content, 0644) + Expect(err).To(BeNil()) + + cgroup := filepath.Dir(containerCgroup) run = podmanTest.Podman([]string{"--cgroup-manager=cgroupfs", "run", "-d", fmt.Sprintf("--cgroup-parent=%s", cgroup), fedoraMinimal, "sleep", "100"}) run.WaitWithDefaultTimeout() diff --git a/test/e2e/run_exit_test.go b/test/e2e/run_exit_test.go index 21f1a8650..e86718577 100644 --- a/test/e2e/run_exit_test.go +++ b/test/e2e/run_exit_test.go @@ -49,11 +49,7 @@ var _ = Describe("Podman run exit", func() { It("podman run exit ExecErrorCodeNotFound", func() { result := podmanTest.Podman([]string{"run", ALPINE, "foobar"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Not(Equal(define.ExecErrorCodeGeneric))) - // TODO This is failing we believe because of a race condition - // Between conmon and podman closing the socket early. - // Test with the following, once the race condition is solved - // Expect(result).Should(Exit(define.ExecErrorCodeNotFound)) + Expect(result).Should(Exit(define.ExecErrorCodeNotFound)) }) It("podman run exit 0", func() { diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 3bfd59b54..3c65c02d1 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1213,14 +1213,14 @@ USER mail`, BB) }) It("podman run with bad healthcheck timeout", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-timeout", "0s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-timeout", "0s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-timeout must be at least 1 second")) }) It("podman run with bad healthcheck start-period", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-start-period", "-1s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-start-period", "-1s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-start-period must be 0 seconds or greater")) diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go index ff3551ad9..0dd1a2b7c 100644 --- a/test/e2e/volume_ls_test.go +++ b/test/e2e/volume_ls_test.go @@ -101,6 +101,22 @@ var _ = Describe("Podman volume ls", func() { Expect(len(session.OutputToStringArray())).To(Equal(0)) }) + It("podman ls volume with --filter until flag", func() { + session := podmanTest.Podman([]string{"volume", "create"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls", "--filter", "until=5000000000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "ls", "--filter", "until=50000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) + }) + It("podman volume ls with --filter dangling", func() { volName1 := "volume1" session := podmanTest.Podman([]string{"volume", "create", volName1}) diff --git a/test/e2e/volume_prune_test.go b/test/e2e/volume_prune_test.go index 68a257110..364ca0ab7 100644 --- a/test/e2e/volume_prune_test.go +++ b/test/e2e/volume_prune_test.go @@ -63,6 +63,37 @@ var _ = Describe("Podman volume prune", func() { podmanTest.Cleanup() }) + It("podman prune volume --filter until", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "until=50"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "until=5000000000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) + + podmanTest.Cleanup() + }) + It("podman prune volume --filter", func() { session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) session.WaitWithDefaultTimeout() diff --git a/test/system/005-info.bats b/test/system/005-info.bats index 4b419841e..50c3ceb30 100644 --- a/test/system/005-info.bats +++ b/test/system/005-info.bats @@ -33,12 +33,16 @@ cgroupVersion: v[12] expr_nvr="[a-z0-9-]\\\+-[a-z0-9.]\\\+-[a-z0-9]\\\+\." expr_path="/[a-z0-9\\\/.-]\\\+\\\$" + # FIXME: if we're ever able to get package versions on Debian, + # add '-[0-9]' to all '*.package' queries below. tests=" host.buildahVersion | [0-9.] host.conmon.path | $expr_path +host.conmon.package | .*conmon.* host.cgroupManager | \\\(systemd\\\|cgroupfs\\\) host.cgroupVersion | v[12] host.ociRuntime.path | $expr_path +host.ociRuntime.package | .*\\\(crun\\\|runc\\\).* store.configFile | $expr_path store.graphDriverName | [a-z0-9]\\\+\\\$ store.graphRoot | $expr_path diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 32fc85c4e..3d9d834b3 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -706,4 +706,21 @@ EOF run_podman rmi nomtab } +@test "podman run --device-cgroup-rule tests" { + skip_if_rootless "cannot add devices in rootless mode" + + run_podman run --device-cgroup-rule="b 7:* rmw" --rm $IMAGE + run_podman run --device-cgroup-rule="c 7:* rmw" --rm $IMAGE + run_podman run --device-cgroup-rule="a 7:1 rmw" --rm $IMAGE + run_podman run --device-cgroup-rule="a 7 rmw" --rm $IMAGE + run_podman 125 run --device-cgroup-rule="b 7:* rmX" --rm $IMAGE + is "$output" "Error: invalid device access in device-access-add: X" + run_podman 125 run --device-cgroup-rule="b 7:2" --rm $IMAGE + is "$output" 'Error: invalid device cgroup rule requires type, major:Minor, and access rules: "b 7:2"' + run_podman 125 run --device-cgroup-rule="x 7:* rmw" --rm $IMAGE + is "$output" "Error: invalid device type in device-access-add:" + run_podman 125 run --device-cgroup-rule="a a:* rmw" --rm $IMAGE + is "$output" "Error: strconv.ParseInt: parsing \"a\": invalid syntax" +} + # vim: filetype=sh diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index ccf83df14..32282c8e1 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -24,6 +24,9 @@ load helpers # test --since with Unix timestamps run_podman logs --since 1000 $cid + # test --until with Unix timestamps + run_podman logs --until 1000 $cid + run_podman rm $cid } @@ -125,4 +128,50 @@ $s_after" _log_test_since journald } +function _log_test_until() { + local driver=$1 + + s_before="before_$(random_string)_${driver}" + s_after="after_$(random_string)_${driver}" + + before=$(date --iso-8601=seconds) + sleep 5 + run_podman run --log-driver=$driver -d --name test $IMAGE sh -c \ + "echo $s_before; trap 'echo $s_after; exit' SIGTERM; while :; do sleep 1; done" + + # sleep a second to make sure the date is after the first echo + sleep 1 + run_podman stop test + # sleep for 20 seconds to get the proper after time + sleep 20 + + run_podman logs test + is "$output" \ + "$s_before +$s_after" + + run_podman logs --until $before test + is "$output" \ + "" + + after=$(date --iso-8601=seconds) + + run_podman logs --until $after test + is "$output" \ + "$s_before +$s_after" + run_podman rm -f test +} + +@test "podman logs - until k8s-file" { + _log_test_until k8s-file +} + +@test "podman logs - until journald" { + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + _log_test_until journald +} + # vim: filetype=sh diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index 2ed791429..d809507a5 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -119,11 +119,31 @@ load helpers # the container's status. run_podman run --name stopme -d $IMAGE sh -c \ - "trap 'echo Received SIGTERM, ignoring' SIGTERM; echo READY; while :; do sleep 1; done" + "trap 'echo Received SIGTERM, ignoring' SIGTERM; echo READY; while :; do sleep 0.2; done" - # Stop the container in the background + wait_for_ready stopme + + local t0=$SECONDS + # Stop the container, but do so in the background so we can inspect + # the container status while it's stopping. Use $PODMAN because we + # don't want the overhead and error checks of run_podman. $PODMAN stop -t 20 stopme & + # Wait for container to acknowledge the signal. We can't use wait_for_output + # because that aborts if .State.Running != true + local timeout=5 + while [[ $timeout -gt 0 ]]; do + run_podman logs stopme + if [[ "$output" =~ "Received SIGTERM, ignoring" ]]; then + break + fi + timeout=$((timeout - 1)) + if [[ $timeout -eq 0 ]]; then + die "Timed out waiting for container to receive SIGERM" + fi + sleep 0.5 + done + # Other commands can acquire the lock run_podman ps -a @@ -131,6 +151,13 @@ load helpers run_podman inspect --format '{{.State.Status}}' stopme is "$output" "stopping" "Status of container should be 'stopping'" + # Time check: make sure we were able to run 'ps' before the container + # exited. If this takes too long, it means ps had to wait for lock. + local delta_t=$(( $SECONDS - t0 )) + if [[ $delta_t -gt 5 ]]; then + die "Operations took too long ($delta_t seconds)" + fi + run_podman kill stopme run_podman wait stopme diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 5778eb46e..39f439e7b 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -22,8 +22,7 @@ load helpers mkdir -p $srcdir/subdir echo "${randomcontent[2]}" > $srcdir/subdir/dotfile. - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer mkdir /srv/subdir + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -41,7 +40,6 @@ load helpers 0 | /tmp | /tmp/hostfile0 | copy to /tmp 1 | /tmp/ | /tmp/hostfile1 | copy to /tmp/ 2 | /tmp/. | /tmp/hostfile2 | copy to /tmp/. -0 | /tmp/hostfile2 | /tmp/hostfile2 | overwrite previous copy 0 | /tmp/anotherbase.txt | /tmp/anotherbase.txt | copy to /tmp, new name 0 | . | /srv/hostfile0 | copy to workdir (rel path), new name 1 | ./ | /srv/hostfile1 | copy to workdir (rel path), new name @@ -175,11 +173,12 @@ load helpers random-1-$(random_string 15) random-2-$(random_string 20) ) - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile" - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/dotfile." - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1" - run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2" + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /tmp/containerfile; + echo ${randomcontent[0]} > /tmp/dotfile.; + echo ${randomcontent[1]} > /srv/containerfile1; + echo ${randomcontent[2]} > /srv/subdir/containerfile2; + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -226,6 +225,98 @@ load helpers } +@test "podman cp file from container to container" { + # Create 3 files with random content in the container. + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + random-2-$(random_string 20) + ) + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /tmp/containerfile; + echo ${randomcontent[0]} > /tmp/dotfile.; + echo ${randomcontent[1]} > /srv/containerfile1; + echo ${randomcontent[2]} > /srv/subdir/containerfile2; + sleep infinity" + + # Commit the image for testing non-running containers + run_podman commit -q cpcontainer + cpimage="$output" + + # format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name> + tests=" +0 | /tmp/containerfile | | /containerfile | / +0 | /tmp/dotfile. | | /dotfile. | / +0 | /tmp/containerfile | / | /containerfile | / +0 | /tmp/containerfile | /. | /containerfile | /. +0 | /tmp/containerfile | /newfile | /newfile | /newfile +1 | containerfile1 | / | /containerfile1 | copy from workdir (rel path) to / +2 | subdir/containerfile2 | / | /containerfile2 | copy from workdir/subdir (rel path) to / +" + + # From RUNNING container + while read id src dest dest_fullname description; do + # dest may be "''" for empty table cells + if [[ $dest == "''" ]];then + unset dest + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + done < <(parse_table "$tests") + run_podman kill cpcontainer + run_podman rm -f cpcontainer + + # From CREATED container + run_podman create --name cpcontainer --workdir=/srv $cpimage + while read id src dest dest_fullname description; do + # dest may be "''" for empty table cells + if [[ $dest == "''" ]];then + unset dest + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + done < <(parse_table "$tests") + run_podman rm -f cpcontainer + + run_podman rmi -f $cpimage +} + + @test "podman cp dir from host to container" { srcdir=$PODMAN_TMPDIR mkdir -p $srcdir/dir/sub @@ -241,8 +332,7 @@ load helpers mkdir -p $srcdir/dir. cp -r $srcdir/dir/* $srcdir/dir. - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer mkdir /srv/subdir + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -309,12 +399,12 @@ load helpers random-0-$(random_string 10) random-1-$(random_string 15) ) - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[0]} > /srv/subdir/containerfile0" - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/subdir/containerfile1" - # "." and "dir/." will copy the contents, so make sure that a dir ending - # with dot is treated correctly. - run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./' + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /srv/subdir/containerfile0; \ + echo ${randomcontent[1]} > /srv/subdir/containerfile1; \ + mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./; \ + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -377,6 +467,110 @@ load helpers } +@test "podman cp dir from container to container" { + # Create 2 files with random content in the container. + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + ) + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /srv/subdir/containerfile0; \ + echo ${randomcontent[1]} > /srv/subdir/containerfile1; \ + mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./; \ + sleep infinity" + + # Commit the image for testing non-running containers + run_podman commit -q cpcontainer + cpimage="$output" + + # format is: <source arg to cp (appended to /srv)> | <dest> | <full dest path> | <test name> + tests=" +/srv | | /srv/subdir | copy /srv +/srv | /newdir | /newdir/subdir | copy /srv to /newdir +/srv/ | | /srv/subdir | copy /srv/ +/srv/. | | /subdir | copy /srv/. +/srv/. | /newdir | /newdir/subdir | copy /srv/. to /newdir +/srv/subdir/. | | | copy /srv/subdir/. +/tmp/subdir. | | /subdir. | copy /tmp/subdir. +" + + # From RUNNING container + while read src dest dest_fullname description; do + if [[ $src == "''" ]];then + unset src + fi + if [[ $dest == "''" ]];then + unset dest + fi + if [[ $dest_fullname == "''" ]];then + unset dest_fullname + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + done < <(parse_table "$tests") + run_podman kill cpcontainer + run_podman rm -f cpcontainer + + # From CREATED container + run_podman create --name cpcontainer --workdir=/srv $cpimage + while read src dest dest_fullname description; do + if [[ $src == "''" ]];then + unset src + fi + if [[ $dest == "''" ]];then + unset dest + fi + if [[ $dest_fullname == "''" ]];then + unset dest_fullname + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + run_podman start $destcontainer + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + run_podman kill $destcontainer + run_podman rm -f $destcontainer + done < <(parse_table "$tests") + + run_podman rm -f cpcontainer + run_podman rmi -f $cpimage +} + + @test "podman cp symlinked directory from container" { destdir=$PODMAN_TMPDIR/cp-weird-symlink mkdir -p $destdir @@ -387,10 +581,10 @@ load helpers random-1-$(random_string 15) ) - run_podman run -d --name cpcontainer $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile0" - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /tmp/containerfile1" - run_podman exec cpcontainer sh -c "mkdir /tmp/sub && cd /tmp/sub && ln -s .. weirdlink" + run_podman run -d --name cpcontainer $IMAGE sh -c "echo ${randomcontent[0]} > /tmp/containerfile0; \ + echo ${randomcontent[1]} > /tmp/containerfile1; \ + mkdir /tmp/sub && cd /tmp/sub && ln -s .. weirdlink; \ + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 7b76c585f..26113e45c 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -749,16 +749,9 @@ RUN echo $random_string EOF run_podman 125 build -t build_test --pull-never $tmpdir - # FIXME: this is just ridiculous. Even after #10030 and #10034, Ubuntu - # remote *STILL* flakes this test! It fails with the correct exit status, - # but the error output is 'Error: stream dropped, unexpected failure' - # Let's just stop checking on podman-remote. As long as it exits 125, - # we're happy. - if ! is_remote; then - is "$output" \ - ".*Error: error creating build container: quay.io/libpod/nosuchimage:nosuchtag: image not known" \ - "--pull-never fails with expected error message" - fi + is "$output" \ + ".*Error: error creating build container: quay.io/libpod/nosuchimage:nosuchtag: image not known" \ + "--pull-never fails with expected error message" } @test "podman build --logfile test" { diff --git a/test/system/090-events.bats b/test/system/090-events.bats index d889bd7f9..22edaeee9 100644 --- a/test/system/090-events.bats +++ b/test/system/090-events.bats @@ -81,6 +81,7 @@ function _events_disjunctive_filters() { @test "events with disjunctive filters - journald" { skip_if_remote "remote does not support --events-backend" + skip_if_journald_unavailable "system does not support journald events" _events_disjunctive_filters --events-backend=journald } diff --git a/test/system/125-import.bats b/test/system/125-import.bats new file mode 100644 index 000000000..c53711618 --- /dev/null +++ b/test/system/125-import.bats @@ -0,0 +1,45 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman import +# + +load helpers + +@test "podman import" { + local archive=$PODMAN_TMPDIR/archive.tar + local random_content=$(random_string 12) + # Generate a random name and tag (must be lower-case) + local random_name=x0$(random_string 12 | tr A-Z a-z) + local random_tag=t0$(random_string 7 | tr A-Z a-z) + local fqin=localhost/$random_name:$random_tag + + run_podman run --name import $IMAGE sh -c "echo ${random_content} > /random.txt" + run_podman export import -o $archive + run_podman rm -f import + + # Simple import + run_podman import -q $archive + iid="$output" + run_podman run -t --rm $iid cat /random.txt + is "$output" "$random_content" "simple import" + run_podman rmi -f $iid + + # Simple import via stdin + run_podman import -q - < <(cat $archive) + iid="$output" + run_podman run -t --rm $iid cat /random.txt + is "$output" "$random_content" "simple import via stdin" + run_podman rmi -f $iid + + # Tagged import + run_podman import -q $archive $fqin + run_podman run -t --rm $fqin cat /random.txt + is "$output" "$random_content" "tagged import" + run_podman rmi -f $fqin + + # Tagged import via stdin + run_podman import -q - $fqin < <(cat $archive) + run_podman run -t --rm $fqin cat /random.txt + is "$output" "$random_content" "tagged import via stdin" + run_podman rmi -f $fqin +} diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index 25eaba45b..6fb40f41e 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -261,7 +261,8 @@ EOF systemctl enable --now podman-auto-update-$cname.timer systemctl list-timers --all - local expect='Finished Podman auto-update testing service' + # While systemd v245 and later uses 'Finished', older versions uses 'Started' for oneshot services + local expect='(Finished|Started) Podman auto-update testing service' local failed_start=failed local count=0 while [ $count -lt 120 ]; do diff --git a/test/system/271-tcp-cors-server.bats b/test/system/271-tcp-cors-server.bats new file mode 100644 index 000000000..cdfa82e82 --- /dev/null +++ b/test/system/271-tcp-cors-server.bats @@ -0,0 +1,44 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Tests podman system service CORS enabled +# + +load helpers + +SERVICE_NAME="podman_test_$(random_string)" + +SERVICE_TCP_HOST="localhost" + +SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service" +SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket" + +@test "podman system service - tcp CORS" { + skip_if_remote "system service tests are meaningless over remote" + PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 )) + run_podman system service --cors="*" tcp:$SERVICE_TCP_HOST:$PORT -t 20 & + podman_pid="$!" + sleep 5s + run curl -s --max-time 10 -vvv $SERVICE_TCP_HOST:$PORT/_ping 2>&1 + is "$output" ".*< Access-Control-Allow-Origin: \*.*" "access-control-allow-origin verifies CORS is set" + kill $podman_pid + wait $podman_pid || true +} + +@test "podman system service - tcp without CORS" { + skip_if_remote "system service tests are meaningless over remote" + PORT=$(( ((RANDOM<<15)|RANDOM) % 63001 + 2000 )) + run_podman system service tcp:$SERVICE_TCP_HOST:$PORT -t 20 & + podman_pid="$!" + sleep 5s + (curl -s --max-time 10 -vvv $SERVICE_TCP_HOST:$PORT/_ping 2>&1 | grep -Eq "Access-Control-Allow-Origin:") && false || true + kill $podman_pid + wait $podman_pid || true +} + +@test "podman system service - CORS enabled in logs" { + skip_if_remote "system service tests are meaningless over remote" + run_podman system service --log-level="debug" --cors="*" -t 1 + is "$output" ".*CORS Headers were set to \*.*" "debug log confirms CORS headers set" +} + +# vim: filetype=sh diff --git a/test/system/410-selinux.bats b/test/system/410-selinux.bats index 4ef9c8b30..5ee0e0715 100644 --- a/test/system/410-selinux.bats +++ b/test/system/410-selinux.bats @@ -50,6 +50,18 @@ function check_label() { check_label "--systemd=always" "container_init_t" } +@test "podman selinux: init container with --security-opt type" { + check_label "--systemd=always --security-opt=label=type:spc_t" "spc_t" +} + +@test "podman selinux: init container with --security-opt level&type" { + check_label "--systemd=always --security-opt=label=level:s0:c1,c2 --security-opt=label=type:spc_t" "spc_t" "s0:c1,c2" +} + +@test "podman selinux: init container with --security-opt level" { + check_label "--systemd=always --security-opt=label=level:s0:c1,c2" "container_init_t" "s0:c1,c2" +} + @test "podman selinux: pid=host" { # FIXME this test fails when run rootless with runc: # Error: container_linux.go:367: starting container process caused: process_linux.go:495: container init caused: readonly path /proc/asound: operation not permitted: OCI permission denied diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 419d325b0..6ffee7eaf 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -139,7 +139,7 @@ load helpers $IMAGE nc -l -n -v -p $myport cid="$output" - wait_for_port 127.0.0.1 $myport + wait_for_output "listening on .*:$myport .*" $cid # emit random string, and check it teststring=$(random_string 30) @@ -390,4 +390,89 @@ load helpers run_podman network rm -f $netname } +# Test for https://github.com/containers/podman/issues/10052 +@test "podman network connect/disconnect with port forwarding" { + random_1=$(random_string 30) + HOST_PORT=12345 + SERVER=http://127.0.0.1:$HOST_PORT + + # Create a test file with random content + INDEX1=$PODMAN_TMPDIR/hello.txt + echo $random_1 > $INDEX1 + + local netname=testnet-$(random_string 10) + run_podman network create $netname + is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + + local netname2=testnet2-$(random_string 10) + run_podman network create $netname2 + is "$output" ".*/cni/net.d/$netname2.conflist" "output of 'network create'" + + # First, run a container in background to ensure that the rootless cni ns + # is not destroyed after network disconnect. + run_podman run -d --network $netname $IMAGE top + background_cid=$output + + # Run a httpd container on first network with exposed port + run_podman run -d -p "$HOST_PORT:80" \ + --network $netname \ + -v $INDEX1:/var/www/index.txt:Z \ + -w /var/www \ + $IMAGE /bin/busybox-extras httpd -f -p 80 + cid=$output + + # Verify http contents: curl from localhost + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt" + + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" + ip="$output" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" + mac="$output" + + run_podman network disconnect $netname $cid + + # check that we cannot curl (timeout after 3 sec) + run curl --max-time 3 -s $SERVER/index.txt + if [ "$status" -eq 0 ]; then + die "curl did not fail, it should have timed out or failed with non zero exit code" + fi + + run_podman network connect $netname $cid + + # curl should work again + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should work again" + + # check that we have a new ip and mac + # if the ip is still the same this whole test turns into a nop + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" + if [[ "$output" == "$ip" ]]; then + die "IP address did not change after podman network disconnect/connect" + fi + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" + if [[ "$output" == "$mac" ]]; then + die "MAC address did not change after podman network disconnect/connect" + fi + + # connect a second network + run_podman network connect $netname2 $cid + + # curl should work + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should work" + + # disconnect the first network + run_podman network disconnect $netname $cid + + # curl should still work + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should still work" + + # cleanup + run_podman stop -t 0 $cid $background_cid + run_podman rm -f $cid $background_cid + run_podman network rm -f $netname $netname2 +} + # vim: filetype=sh diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 02fd7252c..bd9471ace 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -288,7 +288,7 @@ function wait_for_port() { # Wait while [ $_timeout -gt 0 ]; do - { exec 3<> /dev/tcp/$host/$port; } &>/dev/null && return + { exec 5<> /dev/tcp/$host/$port; } &>/dev/null && return sleep 1 _timeout=$(( $_timeout - 1 )) done |