diff options
Diffstat (limited to 'test')
30 files changed, 1470 insertions, 232 deletions
diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index 33f4ea37f..b38810039 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -12,7 +12,7 @@ t POST libpod/volumes/create name=foo1 201 \ .Mountpoint=$volumepath/foo1/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ - .Options=null + .Options={} t POST libpod/volumes/create '' 201 t POST libpod/volumes/create \ '"Name":"foo2","Label":{"testlabel":"testonly"},"Options":{"type":"tmpfs","o":"nodev,noexec"}}' 201 \ diff --git a/test/apiv2/45-system.at b/test/apiv2/45-system.at index 7d14fd4b3..985d86e56 100644 --- a/test/apiv2/45-system.at +++ b/test/apiv2/45-system.at @@ -19,7 +19,7 @@ t POST libpod/volumes/create name=foo1 201 \ .Mountpoint=$volumepath/foo1/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ - .Options=null + .Options={} t GET system/df 200 '.Volumes[0].Name=foo1' @@ -58,10 +58,10 @@ 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 .VolumePruneReport[0].Id=foo3 +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 .VolumePruneReport[0].Id=foo2 +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 .VolumePruneReport[0].Id=foo1 +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/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 2f9e62149..cc66dd5af 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -1,13 +1,15 @@ import json +import os import random +import shutil import string import subprocess +import sys +import time import unittest from multiprocessing import Process import requests -import sys -import time from dateutil.parser import parse from test.apiv2.rest_api import Podman @@ -449,7 +451,7 @@ class TestApi(unittest.TestCase): self.assertEqual(inspect.status_code, 404, inspect.content) prune = requests.post(PODMAN_URL + "/v1.40/networks/prune") - self.assertEqual(prune.status_code, 405, prune.content) + self.assertEqual(prune.status_code, 404, prune.content) def test_volumes_compat(self): name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10)) @@ -499,8 +501,18 @@ class TestApi(unittest.TestCase): rm = requests.delete(PODMAN_URL + f"/v1.40/volumes/{name}") self.assertEqual(rm.status_code, 204, rm.content) + # recreate volume with data and then prune it + r = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.content) + create = json.loads(r.content) + with open(os.path.join(create["Mountpoint"], "test_prune"), "w") as file: + file.writelines(["This is a test\n", "This is a good test\n"]) + prune = requests.post(PODMAN_URL + "/v1.40/volumes/prune") self.assertEqual(prune.status_code, 200, prune.content) + payload = json.loads(prune.content) + self.assertIn(name, payload["VolumesDeleted"]) + self.assertGreater(payload["SpaceReclaimed"], 0) def test_auth_compat(self): r = requests.post( @@ -530,6 +542,50 @@ class TestApi(unittest.TestCase): self.assertIn("Volumes", obj) self.assertIn("BuildCache", obj) + def test_prune_compat(self): + name = "Ctnr_" + "".join(random.choice(string.ascii_letters) for i in range(10)) + + r = requests.post( + PODMAN_URL + f"/v1.40/containers/create?name={name}", + json={ + "Cmd": ["cp", "/etc/motd", "/motd.size_test"], + "Image": "alpine:latest", + "NetworkDisabled": True, + }, + ) + self.assertEqual(r.status_code, 201, r.text) + create = json.loads(r.text) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/wait") + self.assertEqual(r.status_code, 200, r.text) + wait = json.loads(r.text) + self.assertEqual(wait["StatusCode"], 0, wait["Error"]["Message"]) + + prune = requests.post(PODMAN_URL + "/v1.40/containers/prune") + self.assertEqual(prune.status_code, 200, prune.status_code) + prune_payload = json.loads(prune.text) + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + self.assertIn(create["Id"], prune_payload["ContainersDeleted"]) + + # Delete any orphaned containers + r = requests.get(PODMAN_URL + "/v1.40/containers/json?all=true") + self.assertEqual(r.status_code, 200, r.text) + for ctnr in json.loads(r.text): + requests.delete(PODMAN_URL + f"/v1.40/containers/{ctnr['Id']}?force=true") + + prune = requests.post(PODMAN_URL + "/v1.40/images/prune") + self.assertEqual(prune.status_code, 200, prune.text) + prune_payload = json.loads(prune.text) + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + + # FIXME need method to determine which image is going to be "pruned" to fix test + # TODO should handler be recursive when deleting images? + # self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) + self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) + if __name__ == "__main__": unittest.main() diff --git a/test/e2e/build/basicalpine/Containerfile.volume b/test/e2e/build/basicalpine/Containerfile.volume new file mode 100644 index 000000000..6a4fc8242 --- /dev/null +++ b/test/e2e/build/basicalpine/Containerfile.volume @@ -0,0 +1,2 @@ +FROM alpine +VOLUME "/volume0" diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 21f98d3d0..71b4c0089 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -72,9 +72,9 @@ var _ = Describe("Podman build", func() { st, err := os.Stat(logfile) Expect(err).To(BeNil()) - Expect(st.Size()).To(Not(Equal(0))) + Expect(st.Size()).To(Not(Equal(int64(0)))) - session = podmanTest.Podman([]string{"rmi", "alpine"}) + session = podmanTest.Podman([]string{"rmi", "test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 75310b961..3270ce685 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -4,6 +4,7 @@ import ( "net" "os" "os/exec" + "strings" "github.com/containers/podman/v2/pkg/criu" . "github.com/containers/podman/v2/test/utils" @@ -622,7 +623,7 @@ var _ = Describe("Podman checkpoint", func() { result := podmanTest.Podman([]string{"container", "checkpoint", "-l"}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) - Expect(result.ErrorToString()).To(ContainSubstring("Cannot checkpoint containers that have been started with '--rm'")) + Expect(result.ErrorToString()).To(ContainSubstring("cannot checkpoint containers that have been started with '--rm'")) // Checkpointing with --export should still work fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -652,4 +653,173 @@ var _ = Describe("Podman checkpoint", func() { // Remove exported checkpoint os.Remove(fileName) }) + + It("podman checkpoint a container with volumes", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/basicalpine/Containerfile.volume", "-t", "test-cr-volume", + }) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Start the container + localRunString := getRunString([]string{ + "--rm", + "-v", "/volume1", + "-v", "my-test-vol:/volume2", + "test-cr-volume", + "top", + }) + session = podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + + cid := session.OutputToString() + + // Add file in volume0 + result := podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume0/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + // Add file in volume1 + result = podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume1/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + // Add file in volume2 + result = podmanTest.Podman([]string{ + "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume2/test.output", + }) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + // Checkpoint the container + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container should fail because named volume still exists + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result).To(ExitWithError()) + Expect(result.ErrorToString()).To(ContainSubstring( + "volume with name my-test-vol already exists. Use --ignore-volumes to not restore content of volumes", + )) + + // Remove named volume + session = podmanTest.Podman([]string{"volume", "rm", "my-test-vol"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Restoring container + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Validate volume0 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume0/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Validate volume1 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume1/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Validate volume2 content + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume2/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Remove exported checkpoint + os.Remove(checkpointFileName) + }) + + It("podman checkpoint container with --pre-checkpoint", func() { + if !strings.Contains(podmanTest.OCIRuntime, "runc") { + Skip("Test only works on runc 1.0-rc3 or higher.") + } + localRunString := getRunString([]string{ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "checkpoint", "-P", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + + result = podmanTest.Podman([]string{"container", "restore", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + }) + + It("podman checkpoint container with --pre-checkpoint and export (migration)", func() { + if !strings.Contains(podmanTest.OCIRuntime, "runc") { + Skip("Test only works on runc 1.0-rc3 or higher.") + } + localRunString := getRunString([]string{ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + preCheckpointFileName := "/tmp/pre-checkpoint-" + cid + ".tar.gz" + checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + result := podmanTest.Podman([]string{"container", "checkpoint", "-P", "-e", preCheckpointFileName, cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", "-e", checkpointFileName, cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + + result = podmanTest.Podman([]string{"rm", "-f", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + + result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName, "--import-previous", preCheckpointFileName}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + os.Remove(checkpointFileName) + os.Remove(preCheckpointFileName) + }) }) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index a076ada6b..2668b1e7b 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -122,7 +122,7 @@ var _ = SynchronizedBeforeSuite(func() []byte { } // Pull cirros but don't put it into the cache - pullImages := []string{cirros, fedoraToolbox} + pullImages := []string{cirros, fedoraToolbox, volumeTest} pullImages = append(pullImages, CACHE_IMAGES...) for _, image := range pullImages { podman.createArtifact(image) @@ -378,10 +378,17 @@ func GetRandomIPAddress() string { // RunTopContainer runs a simple container in the background that // runs top. If the name passed != "", it will have a name func (p *PodmanTestIntegration) RunTopContainer(name string) *PodmanSessionIntegration { + return p.RunTopContainerWithArgs(name, nil) +} + +// RunTopContainerWithArgs runs a simple container in the background that +// runs top. If the name passed != "", it will have a name, command args can also be passed in +func (p *PodmanTestIntegration) RunTopContainerWithArgs(name string, args []string) *PodmanSessionIntegration { var podmanArgs = []string{"run"} if name != "" { podmanArgs = append(podmanArgs, "--name", name) } + podmanArgs = append(podmanArgs, args...) podmanArgs = append(podmanArgs, "-d", ALPINE, "top") return p.Podman(podmanArgs) } @@ -476,13 +483,7 @@ func (p *PodmanTestIntegration) CleanupVolume() { session := p.Podman([]string{"volume", "rm", "-fa"}) session.Wait(90) - // Stop remove service on volume cleanup - p.StopRemoteService() - - // Nuke tempdir - if err := os.RemoveAll(p.TempDir); err != nil { - fmt.Printf("%q\n", err) - } + p.Cleanup() } // InspectContainerToJSON takes the session output of an inspect @@ -538,12 +539,7 @@ func (p *PodmanTestIntegration) CreatePodWithLabels(name string, labels map[stri } func (p *PodmanTestIntegration) RunTopContainerInPod(name, pod string) *PodmanSessionIntegration { - var podmanArgs = []string{"run", "--pod", pod} - if name != "" { - podmanArgs = append(podmanArgs, "--name", name) - } - podmanArgs = append(podmanArgs, "-d", ALPINE, "top") - return p.Podman(podmanArgs) + return p.RunTopContainerWithArgs(name, []string{"--pod", pod}) } func (p *PodmanTestIntegration) RunHealthCheck(cid string) error { diff --git a/test/e2e/config.go b/test/e2e/config.go index e66cd6846..2552595ad 100644 --- a/test/e2e/config.go +++ b/test/e2e/config.go @@ -15,6 +15,7 @@ var ( healthcheck = "quay.io/libpod/alpine_healthcheck:latest" ImageCacheDir = "/tmp/podman/imagecachedir" fedoraToolbox = "registry.fedoraproject.org/f32/fedora-toolbox:latest" + volumeTest = "quay.io/libpod/volume-plugin-test-img:latest" // This image has seccomp profiles that blocks all syscalls. // The intention behind blocking all syscalls is to prevent diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index 35153ba05..5a5e4b7a5 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -56,3 +56,17 @@ umask = "0002" [engine] network_cmd_options=["allow_host_loopback=true"] + +# We need to ensure each test runs on a separate plugin instance... +# For now, let's just make a bunch of plugin paths and have each test use one. +[engine.volume_plugins] +testvol0 = "/run/docker/plugins/testvol0.sock" +testvol1 = "/run/docker/plugins/testvol1.sock" +testvol2 = "/run/docker/plugins/testvol2.sock" +testvol3 = "/run/docker/plugins/testvol3.sock" +testvol4 = "/run/docker/plugins/testvol4.sock" +testvol5 = "/run/docker/plugins/testvol5.sock" +testvol6 = "/run/docker/plugins/testvol6.sock" +testvol7 = "/run/docker/plugins/testvol7.sock" +testvol8 = "/run/docker/plugins/testvol8.sock" +testvol9 = "/run/docker/plugins/testvol9.sock" diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index f61f52589..b180d881a 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -119,6 +119,203 @@ var _ = Describe("Podman exec", func() { Expect(session.ExitCode()).To(Equal(100)) }) + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + 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)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + }) + + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + }) + + It("podman exec --privileged", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + setup := podmanTest.RunTopContainer("test-privileged") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + }) + + It("podman exec --privileged container not running as root", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin"}) + 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)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000")) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=root", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + }) + + It("podman exec with user with cap-add", func() { + capAdd := "--cap-add=net_bind_service" + session := podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capAdd}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + }) + + It("podman exec with user with and cap-drop cap-add", func() { + capAdd := "--cap-add=net_bind_service" + capDrop := "--cap-drop=all" + session := podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bndPerms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + effPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capDrop, capAdd}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapInh /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapPrm /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + + session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapAmb /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(effPerms)) + }) + + It("podman exec --privileged with user", func() { + session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + bindPerms := session.OutputToString() + + setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--privileged", "--user=bin"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(bindPerms)) + + 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)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + }) + 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/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 765844265..be9727591 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -59,8 +59,7 @@ var _ = Describe("Podman generate systemd", func() { session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) - found, _ := session.ErrorGrepString("bogus is not a valid restart policy") - Expect(found).Should(BeTrue()) + Expect(session.ErrorToString()).To(ContainSubstring("bogus is not a valid restart policy")) }) It("podman generate systemd good timeout value", func() { @@ -71,9 +70,8 @@ var _ = Describe("Podman generate systemd", func() { session = podmanTest.Podman([]string{"generate", "systemd", "--time", "1234", "foobar"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - - found, _ := session.GrepString(" stop -t 1234 ") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=1294")) + Expect(session.OutputToString()).To(ContainSubstring(" stop -t 1234 ")) }) It("podman generate systemd", func() { @@ -84,6 +82,9 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "nginx"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + + // The podman commands in the unit should not contain the root flags + Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd --files --name", func() { @@ -98,9 +99,7 @@ var _ = Describe("Podman generate systemd", func() { for _, file := range session.OutputToStringArray() { os.Remove(file) } - - found, _ := session.GrepString("/container-nginx.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("/container-nginx.service")) }) It("podman generate systemd with timeout", func() { @@ -111,9 +110,7 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "--time", "5", "nginx"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - - found, _ := session.GrepString("podman stop -t 5") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) }) It("podman generate systemd pod --name", func() { @@ -134,35 +131,19 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" start foo-1") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("-infra") // infra container - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" stop -t 42 foo-2") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("PIDFile=") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("/userdata/conmon.pid") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring(" start foo-1")) + Expect(session.OutputToString()).To(ContainSubstring("-infra")) // infra container + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring(" stop -t 42 foo-2")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("PIDFile=")) + Expect(session.OutputToString()).To(ContainSubstring("/userdata/conmon.pid")) + + // The podman commands in the unit should not contain the root flags + Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd pod --name --files", func() { @@ -182,11 +163,8 @@ var _ = Describe("Podman generate systemd", func() { os.Remove(file) } - found, _ := session.GrepString("/pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("/container-foo-1.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("/pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("/container-foo-1.service")) }) It("podman generate systemd --new --name foo", func() { @@ -199,14 +177,13 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" --replace ") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("stop --ignore --cidfile %t/container-foo.ctr-id -t 42") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) + Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) + if !IsRemote() { + // The podman commands in the unit should contain the root flags if generate systemd --new is used + Expect(session.OutputToString()).To(ContainSubstring(" --runroot")) + } }) It("podman generate systemd --new --name=foo", func() { @@ -219,14 +196,9 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString(" --replace ") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("stop --ignore --cidfile %t/container-foo.ctr-id -t 42") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) + Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) }) It("podman generate systemd --new without explicit detaching param", func() { @@ -239,8 +211,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("--cgroups=no-conmon -d") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("--cgroups=no-conmon -d")) }) It("podman generate systemd --new with explicit detaching param in middle", func() { @@ -253,8 +224,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("--name foo alpine top") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("--name foo alpine top")) }) It("podman generate systemd --new pod", func() { @@ -277,8 +247,8 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# con-foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# con-foo.service")) + }) It("podman generate systemd --separator _", func() { @@ -291,8 +261,7 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# container_foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# container_foo.service")) }) It("podman generate systemd pod --pod-prefix p", func() { @@ -313,17 +282,10 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# p-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# container-foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=p-foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# p-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p-foo.service")) }) It("podman generate systemd pod --pod-prefix p --container-prefix con --separator _ change all prefixes/separator", func() { @@ -344,20 +306,11 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# p_foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=con_foo-1.service con_foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# con_foo-1.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("# con_foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=p_foo.service") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# p_foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=con_foo-1.service con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("# con_foo-1.service")) + Expect(session.OutputToString()).To(ContainSubstring("# con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p_foo.service")) }) It("podman generate systemd pod with containers --new", func() { @@ -383,26 +336,13 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.ExitCode()).To(Equal(0)) // Grepping the output (in addition to unit tests) - found, _ := session.GrepString("# pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("Requires=container-foo-1.service container-foo-2.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("BindsTo=pod-foo.service") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10") - Expect(found).To(BeTrue()) - - found, _ = session.GrepString("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id") - Expect(found).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo")) + Expect(session.OutputToString()).To(ContainSubstring("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id")) + Expect(session.OutputToString()).To(ContainSubstring("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10")) + Expect(session.OutputToString()).To(ContainSubstring("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")) }) It("podman generate systemd --format json", func() { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index f009e333e..5930462d5 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -825,9 +825,16 @@ var _ = Describe("Podman play kube", func() { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) + cmd := inspect.OutputToString() + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + ep := inspect.OutputToString() + // Use the defined command to override the image's command - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(ep).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) + Expect(cmd).To(ContainSubstring(strings.Join(defaultCtrArg, " "))) }) // If you do not supply command or args for a Container, the defaults defined in the Docker image are used. @@ -840,12 +847,17 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + // this image's ENTRYPOINT is `/entrypoint.sh` + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) - // 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]`)) + // and its COMMAND is `/etc/docker/registry/config.yml` + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[/etc/docker/registry/config.yml]`)) }) // If you supply a command but no args for a Container, only the supplied command is used. @@ -859,12 +871,18 @@ var _ = Describe("Podman play kube", func() { 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)) // Use the defined command to override the image's command, and don't set the args // so the full command in result should not contains the image's command - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`echo hello`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + // an empty command is reported as '[]' + Expect(inspect.OutputToString()).To(ContainSubstring(`[]`)) }) // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied. @@ -877,12 +895,16 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + // this image's ENTRYPOINT is `/entrypoint.sh` + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) 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]`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) // If you supply a command and args, @@ -897,10 +919,15 @@ var _ = Describe("Podman play kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`echo`)) + + inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[hello]`)) }) It("podman play kube test correct output", func() { @@ -917,11 +944,6 @@ var _ = Describe("Podman play kube", func() { logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) Expect(logs.OutputToString()).To(ContainSubstring("hello world")) - - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"}) - inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello world]`)) }) It("podman play kube test restartPolicy", func() { @@ -1286,12 +1308,11 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) // yaml's command should override the image's Entrypoint - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) }) It("podman play kube deployment more than 1 replica test correct command", func() { @@ -1306,12 +1327,11 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) - correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Cmd }}'"}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) } }) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 225da785c..9f63c1d5d 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -6,6 +6,7 @@ import ( "sort" . "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" @@ -280,6 +281,69 @@ var _ = Describe("Podman ps", func() { Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) }) + It("podman pod ps filter network", func() { + net := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + session = podmanTest.Podman([]string{"pod", "create", "--network", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + podWithNet := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + podWithoutNet := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "ps", "--no-trunc", "--filter", "network=" + net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(podWithNet)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podWithoutNet))) + }) + + It("podman pod ps --format networks", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + if isRootless() { + // rootless container don't have a network by default + Expect(session.OutputToString()).To(Equal("")) + } else { + // default network name is podman + Expect(session.OutputToString()).To(Equal("podman")) + } + + net1 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net1) + net2 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net2) + + session = podmanTest.Podman([]string{"pod", "create", "--network", net1 + "," + net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + pid := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}", "--filter", "id=" + pid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + // the output is not deterministic so check both possible orders + Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) + }) + It("pod no infra should ps", func() { session := podmanTest.Podman([]string{"pod", "create", "--infra=false"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index a034ec2d1..1709b4f81 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -17,7 +17,7 @@ var _ = Describe("Podman pod stats", func() { ) BeforeEach(func() { - SkipIfRootless("Tests fail with both CGv1/2 + required --cgroup-manager=cgroupfs") + SkipIfRootlessCgroupsV1("Tests fail with both CGv1 + required --cgroup-manager=cgroupfs") if isContainerized() { SkipIfCgroupV1("All tests fail Error: unable to load cgroup at ...: cgroup deleted") } @@ -176,9 +176,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on net=host post", func() { - // --net=host not supported for rootless pods at present - // 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") + SkipIfRootless("--net=host not supported for rootless pods at present") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) podCreate.WaitWithDefaultTimeout() diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 0c5d817ba..13701fc3b 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -8,6 +8,7 @@ import ( "strings" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" "github.com/docker/go-units" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -724,4 +725,67 @@ var _ = Describe("Podman ps", func() { }) + It("podman ps filter network", func() { + net := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"network", "create", net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net) + + session = podmanTest.Podman([]string{"create", "--network", net, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + ctrWithNet := session.OutputToString() + + session = podmanTest.Podman([]string{"create", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + ctrWithoutNet := session.OutputToString() + + session = podmanTest.Podman([]string{"ps", "--all", "--no-trunc", "--filter", "network=" + net}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + Expect(session.OutputToString()).To(ContainSubstring(ctrWithNet)) + Expect(session.OutputToString()).To(Not(ContainSubstring(ctrWithoutNet))) + }) + + It("podman ps --format networks", func() { + session := podmanTest.Podman([]string{"create", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + + session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + if isRootless() { + // rootless container don't have a network by default + Expect(session.OutputToString()).To(Equal("")) + } else { + // default network name is podman + Expect(session.OutputToString()).To(Equal("podman")) + } + + net1 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net1}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net1) + net2 := stringid.GenerateNonCryptoID() + session = podmanTest.Podman([]string{"network", "create", net2}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(net2) + + session = podmanTest.Podman([]string{"create", "--network", net1 + "," + net2, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}", "--filter", "id=" + cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + // the output is not deterministic so check both possible orders + Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) + }) + }) diff --git a/test/e2e/rename_test.go b/test/e2e/rename_test.go new file mode 100644 index 000000000..7affbaf56 --- /dev/null +++ b/test/e2e/rename_test.go @@ -0,0 +1,92 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/containers/podman/v2/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("podman rename", 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) + + }) + + It("podman rename on non-existent container", func() { + session := podmanTest.Podman([]string{"rename", "doesNotExist", "aNewName"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("Podman rename on existing container with bad name", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "invalid<>:char" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Not(Equal(0))) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(ctrName)) + }) + + It("Successfully rename a created container", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "aNewName" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(newName)) + }) + + It("Successfully rename a running container", func() { + ctrName := "testCtr" + ctr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr.ExitCode()).To(Equal(0)) + + newName := "aNewName" + rename := podmanTest.Podman([]string{"rename", ctrName, newName}) + rename.WaitWithDefaultTimeout() + Expect(rename.ExitCode()).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring(newName)) + }) +}) diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index 114bd481a..584ccd22b 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -196,4 +196,33 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0])) Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) + + It("Podman restart a container in a pod and hosts shouln't duplicated", func() { + // Fixes: https://github.com/containers/podman/issues/8921 + + _, ec, _ := podmanTest.CreatePod("foobar99") + Expect(ec).To(Equal(0)) + + session := podmanTest.RunTopContainerInPod("host-restart-test", "foobar99") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + testCmd := []string{"exec", "host-restart-test", "sh", "-c", "wc -l < /etc/hosts"} + + // before restart + beforeRestart := podmanTest.Podman(testCmd) + beforeRestart.WaitWithDefaultTimeout() + Expect(beforeRestart.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"restart", "host-restart-test"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + afterRestart := podmanTest.Podman(testCmd) + afterRestart.WaitWithDefaultTimeout() + Expect(afterRestart.ExitCode()).To(Equal(0)) + + // line count should be equal + Expect(beforeRestart.OutputToString()).To(Equal(afterRestart.OutputToString())) + }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index b8e14530c..cbaae7186 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -622,7 +622,7 @@ var _ = Describe("Podman run networking", func() { It("podman run in custom CNI network with --static-ip", func() { SkipIfRootless("Rootless mode does not support --ip") - netName := "podmantestnetwork" + netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.30.128" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName}) create.WaitWithDefaultTimeout() @@ -639,9 +639,27 @@ var _ = Describe("Podman run networking", func() { Expect(create.ExitCode()).To(BeZero()) }) + It("podman rootless fails custom CNI network with --uidmap", func() { + SkipIfNotRootless("The configuration works with rootless") + + netName := stringid.GenerateNonCryptoID() + create := podmanTest.Podman([]string{"network", "create", netName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(BeZero()) + defer podmanTest.removeCNINetwork(netName) + + run := podmanTest.Podman([]string{"run", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "true"}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(125)) + + remove := podmanTest.Podman([]string{"network", "rm", netName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(BeZero()) + }) + It("podman run with new:pod and static-ip", func() { SkipIfRootless("Rootless does not support --ip") - netName := "podmantestnetwork2" + netName := stringid.GenerateNonCryptoID() ipAddr := "10.25.40.128" podname := "testpod" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.40.0/24", netName}) diff --git a/test/e2e/run_ns_test.go b/test/e2e/run_ns_test.go index 51657cb1e..29d2d4395 100644 --- a/test/e2e/run_ns_test.go +++ b/test/e2e/run_ns_test.go @@ -105,6 +105,14 @@ var _ = Describe("Podman run ns", func() { Expect(session).To(ExitWithError()) }) + It("podman run mounts fresh cgroup", func() { + session := podmanTest.Podman([]string{"run", fedoraMinimal, "grep", "cgroup", "/proc/self/mountinfo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).ToNot(ContainSubstring("..")) + }) + It("podman run --ipc=host --pid=host", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cmd := exec.Command("ls", "-l", "/proc/self/ns/pid") diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go index 760de55b6..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,38 +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 - 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", "--user=bin", "--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-drop CapEff", func() { @@ -110,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_test.go b/test/e2e/run_test.go index 4888a676b..19060ecdc 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -342,6 +342,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -352,6 +357,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -367,10 +377,10 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) - session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--user=0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() @@ -382,6 +392,11 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + if os.Geteuid() > 0 { if os.Getenv("SKIP_USERNS") != "" { Skip("Skip userns tests.") @@ -393,6 +408,16 @@ var _ = Describe("Podman run", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) } }) @@ -1415,4 +1440,12 @@ WORKDIR /madethis` Expect(session.ExitCode()).To(Equal(0)) Expect(session.ErrorToString()).To(ContainSubstring("Trying to pull")) }) + + It("podman run container with hostname and hostname environment variable", func() { + hostnameEnv := "test123" + session := podmanTest.Podman([]string{"run", "--hostname", "testctr", "--env", fmt.Sprintf("HOSTNAME=%s", hostnameEnv), ALPINE, "printenv", "HOSTNAME"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(hostnameEnv)) + }) }) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index f809c5afe..1d86ae744 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -124,6 +124,16 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) }) + It("podman search format json list tags", func() { + search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", "alpine"}) + search.WaitWithDefaultTimeout() + Expect(search.ExitCode()).To(Equal(0)) + Expect(search.IsJSONOutputValid()).To(BeTrue()) + Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) + Expect(search.OutputToString()).To(ContainSubstring("3.10")) + Expect(search.OutputToString()).To(ContainSubstring("2.7")) + }) + It("podman search no-trunc flag", func() { search := podmanTest.Podman([]string{"search", "--no-trunc", "alpine"}) search.WaitWithDefaultTimeout() 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/volume_plugin_test.go b/test/e2e/volume_plugin_test.go new file mode 100644 index 000000000..16edab27c --- /dev/null +++ b/test/e2e/volume_plugin_test.go @@ -0,0 +1,184 @@ +package integration + +import ( + "fmt" + "os" + "path/filepath" + + . "github.com/containers/podman/v2/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman volume plugins", 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() + os.Setenv("CONTAINERS_CONF", "config/containers.conf") + SkipIfRemote("Volume plugins only supported as local") + SkipIfRootless("Root is required for volume plugin testing") + os.MkdirAll("/run/docker/plugins", 0755) + }) + + AfterEach(func() { + podmanTest.CleanupVolume() + f := CurrentGinkgoTestDescription() + processTestResult(f) + os.Unsetenv("CONTAINERS_CONF") + }) + + It("volume create with nonexistent plugin errors", func() { + session := podmanTest.Podman([]string{"volume", "create", "--driver", "notexist", "test_volume_name"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("volume create with not-running plugin does not error", func() { + session := podmanTest.Podman([]string{"volume", "create", "--driver", "testvol0", "test_volume_name"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("volume create and remove with running plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol1" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls1.WaitWithDefaultTimeout() + Expect(ls1.ExitCode()).To(Equal(0)) + arrOutput := ls1.OutputToStringArray() + Expect(len(arrOutput)).To(Equal(1)) + Expect(arrOutput[0]).To(ContainSubstring(volName)) + + remove := podmanTest.Podman([]string{"volume", "rm", volName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(Equal(0)) + + ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls2.WaitWithDefaultTimeout() + Expect(ls2.ExitCode()).To(Equal(0)) + Expect(len(ls2.OutputToStringArray())).To(Equal(0)) + }) + + It("volume inspect with running plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol2" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Driver }}", volName}) + volInspect.WaitWithDefaultTimeout() + Expect(volInspect.ExitCode()).To(Equal(0)) + Expect(volInspect.OutputToString()).To(ContainSubstring(pluginName)) + }) + + It("remove plugin with stopped plugin succeeds", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol3" + ctrName := "pluginCtr" + plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls1.WaitWithDefaultTimeout() + Expect(ls1.ExitCode()).To(Equal(0)) + arrOutput := ls1.OutputToStringArray() + Expect(len(arrOutput)).To(Equal(1)) + Expect(arrOutput[0]).To(ContainSubstring(volName)) + + stop := podmanTest.Podman([]string{"stop", "--timeout", "0", ctrName}) + stop.WaitWithDefaultTimeout() + Expect(stop.ExitCode()).To(Equal(0)) + + // Remove should exit non-zero because missing plugin + remove := podmanTest.Podman([]string{"volume", "rm", volName}) + remove.WaitWithDefaultTimeout() + Expect(remove.ExitCode()).To(Not(Equal(0))) + + // But the volume should still be gone + ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) + ls2.WaitWithDefaultTimeout() + Expect(ls2.ExitCode()).To(Equal(0)) + Expect(len(ls2.OutputToStringArray())).To(Equal(0)) + }) + + It("use plugin in containers", func() { + podmanTest.AddImageToRWStore(volumeTest) + + pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes") + os.Mkdir(pluginStatePath, 0755) + + // Keep this distinct within tests to avoid multiple tests using the same plugin. + pluginName := "testvol4" + plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) + plugin.WaitWithDefaultTimeout() + Expect(plugin.ExitCode()).To(Equal(0)) + + volName := "testVolume1" + create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + ctr1 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "sh", "-c", "touch /test/testfile && echo helloworld > /test/testfile"}) + ctr1.WaitWithDefaultTimeout() + Expect(ctr1.ExitCode()).To(Equal(0)) + + ctr2 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "cat", "/test/testfile"}) + ctr2.WaitWithDefaultTimeout() + Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2.OutputToString()).To(ContainSubstring("helloworld")) + + // HACK: `volume rm -f` is timing out trying to remove containers using the volume. + // Solution: remove them manually... + // TODO: fix this when I get back + rmAll := podmanTest.Podman([]string{"rm", "-af"}) + rmAll.WaitWithDefaultTimeout() + Expect(rmAll.ExitCode()).To(Equal(0)) + }) +}) diff --git a/test/python/docker/test_containers.py b/test/python/docker/test_containers.py index 5a9f761a6..01e049ed4 100644 --- a/test/python/docker/test_containers.py +++ b/test/python/docker/test_containers.py @@ -179,11 +179,3 @@ class TestContainers(unittest.TestCase): filters = {"name": "top"} ctnrs = self.client.containers.list(all=True, filters=filters) self.assertEqual(len(ctnrs), 1) - - def test_rename_container(self): - top = self.client.containers.get(TestContainers.topContainerId) - - # rename bogus container - with self.assertRaises(errors.APIError) as error: - top.rename(name="newname") - self.assertEqual(error.exception.response.status_code, 404) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 76caf282b..e7c88408e 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -228,4 +228,17 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag} } +# Regression test for #8931 +@test "podman images - bare manifest list" { + # Create an empty manifest list and list images. + + run_podman inspect --format '{{.ID}}' $IMAGE + iid=$output + + run_podman manifest create test:1.0 + run_podman images --format '{{.ID}}' --no-trunc + [[ "$output" == *"sha256:$iid"* ]] + + run_podman rmi test:1.0 +} # vim: filetype=sh diff --git a/test/system/050-stop.bats b/test/system/050-stop.bats index f604ea2e2..548fd56ee 100644 --- a/test/system/050-stop.bats +++ b/test/system/050-stop.bats @@ -67,4 +67,32 @@ load helpers done } +# Regression test for #8501 +@test "podman stop - unlock while waiting for timeout" { + # Test that the container state transitions to "stopping" and that other + # commands can get the container's lock. To do that, run a container that + # ingores SIGTERM such that the Podman would wait 20 seconds for the stop + # to finish. This gives us enough time to try some commands and inspect + # 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" + + # Stop the container in the background + $PODMAN stop -t 20 stopme & + + # Other commands can acquire the lock + run_podman ps -a + + # The container state transitioned to "stopping" + run_podman inspect --format '{{.State.Status}}' stopme + is "$output" "stopping" "Status of container should be 'stopping'" + + run_podman kill stopme + + # Exit code should be 137 as it was killed + run_podman inspect --format '{{.State.ExitCode}}' stopme + is "$output" "137" "Exit code of killed container" +} + # vim: filetype=sh diff --git a/test/system/120-load.bats b/test/system/120-load.bats index 272e2ae93..902cd9f5e 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -59,15 +59,13 @@ verify_iid_and_name() { local new_tag=t1$(random_string 6 | tr A-Z a-z) run_podman rmi $fqin - new_fqin=localhost/$new_name:$new_tag - run_podman load -i $archive $new_fqin + run_podman load -i $archive run_podman images --format '{{.Repository}}:{{.Tag}}' --sort tag is "${lines[0]}" "$IMAGE" "image is preserved" is "${lines[1]}" "$fqin" "image is reloaded with old fqin" - is "${lines[2]}" "$new_fqin" "image is reloaded with new fqin too" # Clean up - run_podman rmi $fqin $new_fqin + run_podman rmi $fqin } @@ -118,28 +116,6 @@ verify_iid_and_name() { verify_iid_and_name $img_name } -@test "podman load - NAME and NAME:TAG arguments work" { - get_iid_and_name - run_podman save $iid -o $archive - run_podman rmi $iid - - # Load with just a name (note: names must be lower-case) - random_name=$(random_string 20 | tr A-Z a-z) - run_podman load -i $archive $random_name - verify_iid_and_name "localhost/$random_name:latest" - - # Load with NAME:TAG arg - run_podman rmi $iid - random_tag=$(random_string 10 | tr A-Z a-z) - run_podman load -i $archive $random_name:$random_tag - verify_iid_and_name "localhost/$random_name:$random_tag" - - # Cleanup: restore desired image name - run_podman tag $iid $img_name - run_podman rmi "$random_name:$random_tag" -} - - @test "podman load - will not read from tty" { if [ ! -t 0 ]; then skip "STDIN is not a tty" diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats index 20fdd068f..6a89247e6 100644 --- a/test/system/400-unprivileged-access.bats +++ b/test/system/400-unprivileged-access.bats @@ -75,7 +75,7 @@ EOF is "$output" "/var/lib/containers/storage" "GraphRoot in expected place" GRAPH_ROOT="$output" run_podman info --format '{{.Store.RunRoot}}' - is "$output" "/var/run/containers/storage" "RunRoot in expected place" + is "$output" ".*/run/containers/storage" "RunRoot in expected place" RUN_ROOT="$output" # The main test: find all world-writable files or directories underneath diff --git a/test/testvol/main.go b/test/testvol/main.go new file mode 100644 index 000000000..14f253aa7 --- /dev/null +++ b/test/testvol/main.go @@ -0,0 +1,309 @@ +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "sync" + "time" + + "github.com/docker/go-plugins-helpers/volume" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "testvol", + Short: "testvol - volume plugin for Podman", + Long: `Creates simple directory volumes using the Volume Plugin API for testing volume plugin functionality`, + RunE: func(cmd *cobra.Command, args []string) error { + return startServer(config.sockName) + }, + PersistentPreRunE: before, +} + +// Configuration for the volume plugin +type cliConfig struct { + logLevel string + sockName string + path string +} + +// Default configuration is stored here. Will be overwritten by flags. +var config cliConfig = cliConfig{ + logLevel: "error", + sockName: "test-volume-plugin", +} + +func init() { + rootCmd.Flags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin") + rootCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points") + rootCmd.PersistentFlags().StringVar(&config.logLevel, "log-level", config.logLevel, "Log messages including and over the specified level: debug, info, warn, error, fatal, panic") +} + +func before(cmd *cobra.Command, args []string) error { + if config.logLevel == "" { + config.logLevel = "error" + } + + level, err := logrus.ParseLevel(config.logLevel) + if err != nil { + return err + } + + logrus.SetLevel(level) + + return nil +} + +func main() { + if err := rootCmd.Execute(); err != nil { + logrus.Errorf("Error running volume plugin: %v", err) + os.Exit(1) + } + + os.Exit(0) +} + +// startServer runs the HTTP server and responds to requests +func startServer(socketPath string) error { + logrus.Debugf("Starting server...") + + if config.path == "" { + path, err := ioutil.TempDir("", "test_volume_plugin") + if err != nil { + return errors.Wrapf(err, "error getting directory for plugin") + } + config.path = path + } else { + pathStat, err := os.Stat(config.path) + if err != nil { + return errors.Wrapf(err, "unable to access requested plugin state directory") + } + if !pathStat.IsDir() { + return errors.Errorf("cannot use %v as plugin state dir as it is not a directory", config.path) + } + } + + handle, err := makeDirDriver(config.path) + if err != nil { + return errors.Wrapf(err, "error making volume driver") + } + logrus.Infof("Using %s for volume path", config.path) + + server := volume.NewHandler(handle) + if err := server.ServeUnix(socketPath, 0); err != nil { + return errors.Wrapf(err, "error starting server") + } + return nil +} + +// DirDriver is a trivial volume driver implementation. +// the volumes field maps name to volume +type DirDriver struct { + lock sync.Mutex + volumesPath string + volumes map[string]*dirVol +} + +type dirVol struct { + name string + path string + options map[string]string + mounts map[string]bool + createTime time.Time +} + +// Make a new DirDriver. +func makeDirDriver(path string) (volume.Driver, error) { + drv := new(DirDriver) + drv.volumesPath = path + drv.volumes = make(map[string]*dirVol) + + return drv, nil +} + +// Capabilities returns the capabilities of the driver. +func (d *DirDriver) Capabilities() *volume.CapabilitiesResponse { + logrus.Infof("Hit Capabilities() endpoint") + + return &volume.CapabilitiesResponse{ + volume.Capability{ + "local", + }, + } +} + +// Create creates a volume. +func (d *DirDriver) Create(opts *volume.CreateRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Create() endpoint") + + if _, exists := d.volumes[opts.Name]; exists { + return errors.Errorf("volume with name %s already exists", opts.Name) + } + + newVol := new(dirVol) + newVol.name = opts.Name + newVol.mounts = make(map[string]bool) + newVol.options = make(map[string]string) + newVol.createTime = time.Now() + for k, v := range opts.Options { + newVol.options[k] = v + } + + volPath := filepath.Join(d.volumesPath, opts.Name) + if err := os.Mkdir(volPath, 0755); err != nil { + return errors.Wrapf(err, "error making volume directory") + } + newVol.path = volPath + + d.volumes[opts.Name] = newVol + + logrus.Debugf("Made volume with name %s and path %s", newVol.name, newVol.path) + + return nil +} + +// List lists all volumes available. +func (d *DirDriver) List() (*volume.ListResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit List() endpoint") + + vols := new(volume.ListResponse) + vols.Volumes = []*volume.Volume{} + + for _, vol := range d.volumes { + newVol := new(volume.Volume) + newVol.Name = vol.name + newVol.Mountpoint = vol.path + newVol.CreatedAt = vol.createTime.String() + vols.Volumes = append(vols.Volumes, newVol) + logrus.Debugf("Adding volume %s to list response", newVol.Name) + } + + return vols, nil +} + +// Get retrieves a single volume. +func (d *DirDriver) Get(req *volume.GetRequest) (*volume.GetResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Get() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Did not find volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + logrus.Debugf("Found volume %s", req.Name) + + resp := new(volume.GetResponse) + resp.Volume = new(volume.Volume) + resp.Volume.Name = vol.name + resp.Volume.Mountpoint = vol.path + resp.Volume.CreatedAt = vol.createTime.String() + + return resp, nil +} + +// Remove removes a single volume. +func (d *DirDriver) Remove(req *volume.RemoveRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Remove() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Did not find volume %s", req.Name) + return errors.Errorf("no volume with name %s found") + } + logrus.Debugf("Found volume %s", req.Name) + + if len(vol.mounts) > 0 { + logrus.Debugf("Cannot remove %s, is mounted", req.Name) + return errors.Errorf("volume %s is mounted and cannot be removed") + } + + delete(d.volumes, req.Name) + + if err := os.RemoveAll(vol.path); err != nil { + return errors.Wrapf(err, "error removing mountpoint of volume %s", req.Name) + } + + logrus.Debugf("Removed volume %s", req.Name) + + return nil +} + +// Path returns the path a single volume is mounted at. +func (d *DirDriver) Path(req *volume.PathRequest) (*volume.PathResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Path() endpoint") + + // TODO: Should we return error if not mounted? + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + return &volume.PathResponse{ + vol.path, + }, nil +} + +// Mount mounts the volume. +func (d *DirDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, error) { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Mount() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return nil, errors.Errorf("no volume with name %s found", req.Name) + } + + vol.mounts[req.ID] = true + + return &volume.MountResponse{ + vol.path, + }, nil +} + +// Unmount unmounts the volume. +func (d *DirDriver) Unmount(req *volume.UnmountRequest) error { + d.lock.Lock() + defer d.lock.Unlock() + + logrus.Infof("Hit Unmount() endpoint") + + vol, exists := d.volumes[req.Name] + if !exists { + logrus.Debugf("Cannot locate volume %s", req.Name) + return errors.Errorf("no volume with name %s found", req.Name) + } + + mount := vol.mounts[req.ID] + if !mount { + logrus.Debugf("Volume %s is not mounted by %s", req.Name, req.ID) + return errors.Errorf("volume %s is not mounted by %s", req.Name, req.ID) + } + + delete(vol.mounts, req.ID) + + return nil +} |