diff options
Diffstat (limited to 'test')
44 files changed, 1347 insertions, 364 deletions
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index d3fde9f9d..b7bcaf81d 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -218,6 +218,9 @@ if ! grep -q '400 Bad Request' "${TMPD}/headers.txt"; then BUILD_TEST_ERROR="1" fi +t POST libpod/images/prune 200 +t POST libpod/images/prune 200 length=0 [] + cleanBuildTest if [[ "${BUILD_TEST_ERROR}" ]]; then exit 1 diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at index 985b26411..f45e85f61 100644 --- a/test/apiv2/40-pods.at +++ b/test/apiv2/40-pods.at @@ -110,11 +110,11 @@ t GET libpod/pods/fakename/top 404 \ .cause="no such pod" t GET libpod/pods/foo/top 200 \ - .Processes[0][-1]="/pause " \ + .Processes[0][-1]="/pause" \ .Titles[-1]="COMMAND" t GET libpod/pods/foo/top?ps_args=args,pid 200 \ - .Processes[0][0]="/pause " \ + .Processes[0][0]="/pause" \ .Processes[0][1]="1" \ .Titles[0]="COMMAND" \ .Titles[1]="PID" \ diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index 853e9da88..101044bbb 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -1,8 +1,11 @@ +import multiprocessing +import queue import random +import threading import unittest -import json import requests +import time from dateutil.parser import parse from .fixtures import APITestCase @@ -16,7 +19,10 @@ class ContainerTestCase(APITestCase): self.assertEqual(len(obj), 1) def test_list_filters(self): - r = requests.get(self.podman_url + "/v1.40/containers/json?filters%3D%7B%22status%22%3A%5B%22running%22%5D%7D") + r = requests.get( + self.podman_url + + "/v1.40/containers/json?filters%3D%7B%22status%22%3A%5B%22running%22%5D%7D" + ) self.assertEqual(r.status_code, 200, r.text) payload = r.json() containerAmnt = len(payload) @@ -33,18 +39,18 @@ class ContainerTestCase(APITestCase): self.assertId(r.content) _ = parse(r.json()["Created"]) - r = requests.post( self.podman_url + "/v1.40/containers/create?name=topcontainer", - json={"Cmd": ["top"], - "Image": "alpine:latest", - "Healthcheck": { - "Test": ["CMD", "pidof", "top"], - "Interval": 5000000000, - "Timeout": 2000000000, - "Retries": 3, - "StartPeriod": 5000000000 - } + json={ + "Cmd": ["top"], + "Image": "alpine:latest", + "Healthcheck": { + "Test": ["CMD", "pidof", "top"], + "Interval": 5000000000, + "Timeout": 2000000000, + "Retries": 3, + "StartPeriod": 5000000000, + }, }, ) self.assertEqual(r.status_code, 201, r.text) @@ -67,7 +73,7 @@ class ContainerTestCase(APITestCase): self.assertEqual(r.status_code, 200, r.text) self.assertId(r.content) out = r.json() - hc = out["Config"]["Healthcheck"]["Test"] + hc = out["Config"]["Healthcheck"]["Test"] self.assertListEqual(["CMD", "pidof", "top"], hc) r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start") @@ -84,7 +90,9 @@ class ContainerTestCase(APITestCase): self.assertIn(r.status_code, (200, 409), r.text) if r.status_code == 200: self.assertId(r.content) - r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false&one-shot=true"))) + r = requests.get( + self.uri(self.resolve_container("/containers/{}/stats?stream=false&one-shot=true")) + ) self.assertIn(r.status_code, (200, 409), r.text) if r.status_code == 200: self.assertId(r.content) @@ -136,9 +144,15 @@ class ContainerTestCase(APITestCase): payload = r.json() container_id = payload["Id"] self.assertIsNotNone(container_id) - r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0") + r = requests.get( + self.podman_url + + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0" + ) self.assertEqual(r.status_code, 200, r.text) - r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1") + r = requests.get( + self.podman_url + + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1" + ) self.assertEqual(r.status_code, 200, r.text) def test_commit(self): @@ -257,6 +271,63 @@ class ContainerTestCase(APITestCase): r = requests.delete(self.podman_url + f"/v1.40/containers/{container_id}") self.assertEqual(r.status_code, 204, r.text) + def test_top_no_stream(self): + uri = self.uri(self.resolve_container("/containers/{}/top")) + q = queue.Queue() + + def _impl(fifo): + fifo.put(requests.get(uri, params={"stream": False}, timeout=2)) + + top = threading.Thread(target=_impl, args=(q,)) + top.start() + time.sleep(2) + self.assertFalse(top.is_alive(), f"GET {uri} failed to return in 2s") + + qr = q.get(False) + self.assertEqual(qr.status_code, 200, qr.text) + + qr.close() + top.join() + + def test_top_stream(self): + uri = self.uri(self.resolve_container("/containers/{}/top")) + q = queue.Queue() + + stop_thread = False + + def _impl(fifo, stop): + try: + with requests.get(uri, params={"stream": True, "delay": 1}, stream=True) as r: + r.raise_for_status() + fifo.put(r) + for buf in r.iter_lines(chunk_size=None): + if stop(): + break + fifo.put(buf) + except Exception: + pass + + top = threading.Thread(target=_impl, args=(q, (lambda: stop_thread))) + top.start() + time.sleep(4) + self.assertTrue(top.is_alive(), f"GET {uri} exited too soon") + stop_thread = True + + for _ in range(10): + try: + qr = q.get_nowait() + if qr is not None: + self.assertEqual(qr.status_code, 200) + qr.close() + break + except queue.Empty: + pass + finally: + time.sleep(1) + else: + self.fail("Server failed to respond in 10s") + top.join() + if __name__ == "__main__": unittest.main() diff --git a/test/apiv2/python/rest_api/test_v2_0_0_volume.py b/test/apiv2/python/rest_api/test_v2_0_0_volume.py index f5231e17c..2156318b0 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_volume.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_volume.py @@ -7,7 +7,7 @@ from .fixtures import APITestCase class VolumeTestCase(APITestCase): - def test_volume(self): + def test_volume_crud(self): name = f"Volume_{random.getrandbits(160):x}" ls = requests.get(self.podman_url + "/v1.40/volumes") @@ -70,6 +70,71 @@ class VolumeTestCase(APITestCase): self.assertIn(name, payload["VolumesDeleted"]) self.assertGreater(payload["SpaceReclaimed"], 0) + def test_volume_label(self): + name = f"Volume_{random.getrandbits(160):x}" + expected = { + "Production": "False", + "Database": "Foxbase", + } + + create = requests.post( + self.podman_url + "/v4.0.0/libpod/volumes/create", + json={"name": name, "label": expected}, + ) + self.assertEqual(create.status_code, 201, create.text) + + inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + volume = inspect.json() + self.assertIn("Labels", volume) + self.assertNotIn("Label", volume) + self.assertDictEqual(expected, volume["Labels"]) + + def test_volume_labels(self): + name = f"Volume_{random.getrandbits(160):x}" + expected = { + "Production": "False", + "Database": "Foxbase", + } + + create = requests.post( + self.podman_url + "/v4.0.0/libpod/volumes/create", + json={"name": name, "labels": expected}, + ) + self.assertEqual(create.status_code, 201, create.text) + + inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + volume = inspect.json() + self.assertIn("Labels", volume) + self.assertDictEqual(expected, volume["Labels"]) + + def test_volume_label_override(self): + name = f"Volume_{random.getrandbits(160):x}" + create = requests.post( + self.podman_url + "/v4.0.0/libpod/volumes/create", + json={ + "Name": name, + "Label": { + "Database": "dbase", + }, + "Labels": { + "Database": "sqlserver", + }, + }, + ) + self.assertEqual(create.status_code, 201, create.text) + + inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + volume = inspect.json() + self.assertIn("Labels", volume) + self.assertNotIn("Label", volume) + self.assertDictEqual({"Database": "sqlserver"}, volume["Labels"]) + if __name__ == "__main__": unittest.main() diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 770a7c7bd..6b294802d 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -1,15 +1,19 @@ package integration import ( + "encoding/json" "fmt" "net" "os" "os/exec" + "path/filepath" "strings" "time" + "github.com/checkpoint-restore/go-criu/v5/stats" "github.com/containers/podman/v3/pkg/checkpoint/crutils" "github.com/containers/podman/v3/pkg/criu" + "github.com/containers/podman/v3/pkg/domain/entities" . "github.com/containers/podman/v3/test/utils" "github.com/containers/podman/v3/utils" . "github.com/onsi/ginkgo" @@ -1156,4 +1160,183 @@ var _ = Describe("Podman checkpoint", func() { os.Remove(fileName) }) } + + It("podman checkpoint container with export (migration) and --ipc host", func() { + localRunString := getRunString([]string{"--rm", "--ipc", "host", ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + cid := session.OutputToString() + fileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Remove exported checkpoint + os.Remove(fileName) + }) + + It("podman checkpoint container with export and statistics", func() { + localRunString := getRunString([]string{ + "--rm", + ALPINE, + "top", + }) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + cid := session.OutputToString() + fileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + result := podmanTest.Podman([]string{ + "container", + "checkpoint", + "-l", "-e", + fileName, + }) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Extract checkpoint archive + destinationDirectory, err := CreateTempDirInTempDir() + Expect(err).ShouldNot(HaveOccurred()) + + tarsession := SystemExec( + "tar", + []string{ + "xf", + fileName, + "-C", + destinationDirectory, + }, + ) + Expect(tarsession).Should(Exit(0)) + + _, err = os.Stat(filepath.Join(destinationDirectory, stats.StatsDump)) + Expect(err).ShouldNot(HaveOccurred()) + + Expect(os.RemoveAll(destinationDirectory)).To(BeNil()) + + // Remove exported checkpoint + os.Remove(fileName) + }) + + It("podman checkpoint and restore containers with --print-stats", func() { + session1 := podmanTest.Podman(getRunString([]string{redis})) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + + session2 := podmanTest.Podman(getRunString([]string{redis, "top"})) + session2.WaitWithDefaultTimeout() + Expect(session2).Should(Exit(0)) + + result := podmanTest.Podman([]string{ + "container", + "checkpoint", + "-a", + "--print-stats", + }) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + + type checkpointStatistics struct { + PodmanDuration int64 `json:"podman_checkpoint_duration"` + ContainerStatistics []*entities.CheckpointReport `json:"container_statistics"` + } + + cS := new(checkpointStatistics) + err := json.Unmarshal([]byte(result.OutputToString()), cS) + Expect(err).ShouldNot(HaveOccurred()) + + Expect(len(cS.ContainerStatistics)).To(Equal(2)) + Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[0].RuntimeDuration)) + Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[1].RuntimeDuration)) + Expect(cS.ContainerStatistics[0].RuntimeDuration).To( + BeNumerically(">", cS.ContainerStatistics[0].CRIUStatistics.FrozenTime), + ) + Expect(cS.ContainerStatistics[1].RuntimeDuration).To( + BeNumerically(">", cS.ContainerStatistics[1].CRIUStatistics.FrozenTime), + ) + + ps := podmanTest.Podman([]string{ + "ps", + "-q", + "--no-trunc", + }) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse()) + Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) + + result = podmanTest.Podman([]string{ + "container", + "restore", + "-a", + "--print-stats", + }) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) + + type restoreStatistics struct { + PodmanDuration int64 `json:"podman_restore_duration"` + ContainerStatistics []*entities.RestoreReport `json:"container_statistics"` + } + + rS := new(restoreStatistics) + err = json.Unmarshal([]byte(result.OutputToString()), rS) + Expect(err).ShouldNot(HaveOccurred()) + + Expect(len(cS.ContainerStatistics)).To(Equal(2)) + Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[0].RuntimeDuration)) + Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[1].RuntimeDuration)) + Expect(cS.ContainerStatistics[0].RuntimeDuration).To( + BeNumerically(">", cS.ContainerStatistics[0].CRIUStatistics.RestoreTime), + ) + Expect(cS.ContainerStatistics[1].RuntimeDuration).To( + BeNumerically(">", cS.ContainerStatistics[1].CRIUStatistics.RestoreTime), + ) + + result = podmanTest.Podman([]string{ + "rm", + "-t", + "0", + "-fa", + }) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + }) diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 7228682f3..200faae2d 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -208,9 +208,7 @@ var _ = SynchronizedAfterSuite(func() {}, // PodmanTestCreate creates a PodmanTestIntegration instance for the tests func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { - var ( - podmanRemoteBinary string - ) + var podmanRemoteBinary string host := GetHostDistributionInfo() cwd, _ := os.Getwd() @@ -220,12 +218,11 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { podmanBinary = os.Getenv("PODMAN_BINARY") } - if remote { - podmanRemoteBinary = filepath.Join(cwd, "../../bin/podman-remote") - if os.Getenv("PODMAN_REMOTE_BINARY") != "" { - podmanRemoteBinary = os.Getenv("PODMAN_REMOTE_BINARY") - } + podmanRemoteBinary = filepath.Join(cwd, "../../bin/podman-remote") + if os.Getenv("PODMAN_REMOTE_BINARY") != "" { + podmanRemoteBinary = os.Getenv("PODMAN_REMOTE_BINARY") } + conmonBinary := filepath.Join("/usr/libexec/podman/conmon") altConmonBinary := "/usr/bin/conmon" if _, err := os.Stat(conmonBinary); os.IsNotExist(err) { @@ -271,12 +268,13 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { p := &PodmanTestIntegration{ PodmanTest: PodmanTest{ - PodmanBinary: podmanBinary, - ArtifactPath: ARTIFACT_DIR, - TempDir: tempDir, - RemoteTest: remote, - ImageCacheFS: storageFs, - ImageCacheDir: ImageCacheDir, + PodmanBinary: podmanBinary, + RemotePodmanBinary: podmanRemoteBinary, + ArtifactPath: ARTIFACT_DIR, + TempDir: tempDir, + RemoteTest: remote, + ImageCacheFS: storageFs, + ImageCacheDir: ImageCacheDir, }, ConmonBinary: conmonBinary, CrioRoot: filepath.Join(tempDir, "crio"), @@ -289,8 +287,8 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration { CgroupManager: cgroupManager, Host: host, } + if remote { - p.PodmanTest.RemotePodmanBinary = podmanRemoteBinary uuid := stringid.GenerateNonCryptoID() if !rootless.IsRootless() { p.RemoteSocket = fmt.Sprintf("unix:/run/podman/podman-%s.sock", uuid) @@ -632,6 +630,19 @@ func SkipIfNotRootless(reason string) { } } +func SkipIfSystemdNotRunning(reason string) { + checkReason(reason) + + cmd := exec.Command("systemctl", "list-units") + err := cmd.Run() + if err != nil { + if _, ok := err.(*exec.Error); ok { + ginkgo.Skip("[notSystemd]: not running " + reason) + } + Expect(err).ToNot(HaveOccurred()) + } +} + func SkipIfNotSystemd(manager, reason string) { checkReason(reason) if manager != "systemd" { @@ -683,9 +694,44 @@ func SkipIfContainerized(reason string) { } } +func SkipIfRemote(reason string) { + checkReason(reason) + if !IsRemote() { + return + } + ginkgo.Skip("[remote]: " + reason) +} + +// SkipIfInContainer skips a test if the test is run inside a container +func SkipIfInContainer(reason string) { + checkReason(reason) + if os.Getenv("TEST_ENVIRON") == "container" { + Skip("[container]: " + reason) + } +} + +// SkipIfNotActive skips a test if the given systemd unit is not active +func SkipIfNotActive(unit string, reason string) { + checkReason(reason) + + var buffer bytes.Buffer + cmd := exec.Command("systemctl", "is-active", unit) + cmd.Stdout = &buffer + err := cmd.Start() + Expect(err).ToNot(HaveOccurred()) + + err = cmd.Wait() + Expect(err).ToNot(HaveOccurred()) + + Expect(err).ToNot(HaveOccurred()) + if strings.TrimSpace(buffer.String()) != "active" { + Skip(fmt.Sprintf("[systemd]: unit %s is not active: %s", unit, reason)) + } +} + // PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd string, env []string) *PodmanSessionIntegration { - podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil) + podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil, nil) return &PodmanSessionIntegration{podmanSession} } diff --git a/test/e2e/config/containers-netns.conf b/test/e2e/config/containers-netns.conf new file mode 100644 index 000000000..3f796f25d --- /dev/null +++ b/test/e2e/config/containers-netns.conf @@ -0,0 +1,3 @@ +[containers] + +netns = "host" diff --git a/test/e2e/config/containers-netns2.conf b/test/e2e/config/containers-netns2.conf new file mode 100644 index 000000000..1ffd100f5 --- /dev/null +++ b/test/e2e/config/containers-netns2.conf @@ -0,0 +1,3 @@ +[containers] + +netns = "bridge" diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 69941494b..cd382eba9 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -67,6 +67,10 @@ var _ = Describe("Podman generate kube", func() { err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(pod.Spec.HostNetwork).To(Equal(false)) + Expect(pod.Spec.SecurityContext).To(BeNil()) + Expect(pod.Spec.DNSConfig).To(BeNil()) + Expect(pod.Spec.Containers[0].WorkingDir).To(Equal("")) + Expect(pod.Spec.Containers[0].Env).To(BeNil()) numContainers := 0 for range pod.Spec.Containers { @@ -103,6 +107,7 @@ var _ = Describe("Podman generate kube", func() { err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(kube.OutputToString()).To(ContainSubstring("type: spc_t")) + }) It("podman generate service kube on container with --security-opt type", func() { @@ -1079,7 +1084,7 @@ USER test1` top1.WaitWithDefaultTimeout() Expect(top1).Should(Exit(0)) - top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) + top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--workdir", "/root", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) top2.WaitWithDefaultTimeout() Expect(top2).Should(Exit(0)) @@ -1090,6 +1095,8 @@ USER test1` pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + Expect(pod.Spec.Containers[0].WorkingDir).To(Equal("")) + Expect(pod.Spec.Containers[1].WorkingDir).To(Equal("/root")) for _, ctr := range []string{"top1", "top2"} { v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr] diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index b2666c789..c9a6f926f 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -52,6 +52,18 @@ var _ = Describe("Podman healthcheck run", func() { Expect(hc).Should(Exit(125)) }) + It("podman healthcheck from image's config (not container config)", func() { + // Regression test for #12226: a health check may be defined in + // the container or the container-config of an image. + session := podmanTest.Podman([]string{"create", "--name", "hc", "quay.io/libpod/healthcheck:config-only", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Healthcheck}}", "hc"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(Exit(0)) + Expect(hc.OutputToString()).To(Equal("{[CMD-SHELL curl -f http://localhost/ || exit 1] 0s 5m0s 3s 0}")) + }) + It("podman disable healthcheck with --health-cmd=none on valid container", func() { session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "none", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go index 9fd8d7e27..3e7e8da48 100644 --- a/test/e2e/image_scp_test.go +++ b/test/e2e/image_scp_test.go @@ -22,12 +22,14 @@ var _ = Describe("podman image scp", func() { ) BeforeEach(func() { + ConfPath.Value, ConfPath.IsSet = os.LookupEnv("CONTAINERS_CONF") conf, err := ioutil.TempFile("", "containersconf") if err != nil { panic(err) } os.Setenv("CONTAINERS_CONF", conf.Name()) + tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) @@ -38,6 +40,7 @@ var _ = Describe("podman image scp", func() { AfterEach(func() { podmanTest.Cleanup() + os.Remove(os.Getenv("CONTAINERS_CONF")) if ConfPath.IsSet { os.Setenv("CONTAINERS_CONF", ConfPath.Value) @@ -58,6 +61,29 @@ var _ = Describe("podman image scp", func() { Expect(scp).To(Exit(0)) }) + It("podman image scp root to rootless transfer", func() { + SkipIfNotRootless("this is a rootless only test, transfering from root to rootless using PodmanAsUser") + if IsRemote() { + Skip("this test is only for non-remote") + } + env := os.Environ() + img := podmanTest.PodmanAsUser([]string{"image", "pull", ALPINE}, 0, 0, "", env) // pull image to root + img.WaitWithDefaultTimeout() + Expect(img).To(Exit(0)) + scp := podmanTest.PodmanAsUser([]string{"image", "scp", "root@localhost::" + ALPINE, "1000:1000@localhost::"}, 0, 0, "", env) //transfer from root to rootless (us) + scp.WaitWithDefaultTimeout() + Expect(scp).To(Exit(0)) + + list := podmanTest.Podman([]string{"image", "list"}) // our image should now contain alpine loaded in from root + list.WaitWithDefaultTimeout() + Expect(list).To(Exit(0)) + Expect(list.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue()) + + scp = podmanTest.PodmanAsUser([]string{"image", "scp", "root@localhost::" + ALPINE}, 0, 0, "", env) //transfer from root to rootless (us) + scp.WaitWithDefaultTimeout() + Expect(scp).To(Exit(0)) + }) + It("podman image scp bogus image", func() { if IsRemote() { Skip("this test is only for non-remote") diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index 3115c246f..1fa29daa1 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -16,20 +16,12 @@ import ( "time" "github.com/containers/podman/v3/pkg/rootless" - "github.com/onsi/ginkgo" ) func IsRemote() bool { return true } -func SkipIfRemote(reason string) { - if len(reason) < 5 { - panic("SkipIfRemote must specify a reason to skip") - } - ginkgo.Skip("[remote]: " + reason) -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} @@ -38,11 +30,25 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration return &PodmanSessionIntegration{podmanSession} } +// PodmanSystemdScope runs the podman command in a new systemd scope +func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration { + var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} + remoteArgs = append(remoteArgs, args...) + + wrapper := []string{"systemd-run", "--scope"} + if rootless.IsRootless() { + wrapper = []string{"systemd-run", "--scope", "--user"} + } + + podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, wrapper, nil) + return &PodmanSessionIntegration{podmanSession} +} + // PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration { var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} remoteArgs = append(remoteArgs, args...) - podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, extraFiles) + podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, nil, extraFiles) return &PodmanSessionIntegration{podmanSession} } diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index cc03ccc96..001a869b1 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -8,24 +8,33 @@ import ( "os" "path/filepath" "strings" + + "github.com/containers/podman/v3/pkg/rootless" ) func IsRemote() bool { return false } -func SkipIfRemote(string) { -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) return &PodmanSessionIntegration{podmanSession} } +// PodmanSystemdScope runs the podman command in a new systemd scope +func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration { + wrapper := []string{"systemd-run", "--scope"} + if rootless.IsRootless() { + wrapper = []string{"systemd-run", "--scope", "--user"} + } + podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, wrapper, nil) + return &PodmanSessionIntegration{podmanSession} +} + // PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration { - podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, extraFiles) + podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles) return &PodmanSessionIntegration{podmanSession} } diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 314e09b9a..3beabec4b 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -13,6 +13,19 @@ import ( . "github.com/onsi/gomega/gexec" ) +func isEventBackendJournald(podmanTest *PodmanTestIntegration) bool { + if !podmanTest.RemoteTest { + // If not remote test, '--events-backend' is set to 'file' or 'none' + return false + } + info := podmanTest.Podman([]string{"info", "--format", "{{.Host.EventLogger}}"}) + info.WaitWithDefaultTimeout() + if info.OutputToString() == "journald" { + return true + } + return false +} + var _ = Describe("Podman logs", func() { var ( tempdir string @@ -38,8 +51,18 @@ var _ = Describe("Podman logs", func() { }) for _, log := range []string{"k8s-file", "journald", "json-file"} { + // This is important to move the 'log' var to the correct scope under Ginkgo flow. + log := log + + skipIfJournaldInContainer := func() { + if log == "journald" { + SkipIfInContainer("journalctl inside a container doesn't work correctly") + } + } It("all lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -53,6 +76,8 @@ var _ = Describe("Podman logs", func() { }) It("tail two lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -65,6 +90,8 @@ var _ = Describe("Podman logs", func() { }) It("tail zero lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -77,6 +104,8 @@ var _ = Describe("Podman logs", func() { }) It("tail 99 lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -89,6 +118,8 @@ var _ = Describe("Podman logs", func() { }) It("tail 800 lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -101,6 +132,8 @@ var _ = Describe("Podman logs", func() { }) It("tail 2 lines with timestamps: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -113,6 +146,8 @@ var _ = Describe("Podman logs", func() { }) It("since time 2017-08-07: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -125,6 +160,8 @@ var _ = Describe("Podman logs", func() { }) It("since duration 10m: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -137,6 +174,8 @@ var _ = Describe("Podman logs", func() { }) It("until duration 10m: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -149,6 +188,7 @@ var _ = Describe("Podman logs", func() { }) It("until time NOW: "+log, func() { + skipIfJournaldInContainer() logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() @@ -165,13 +205,17 @@ var _ = Describe("Podman logs", func() { }) It("latest and container name should fail: "+log, func() { + skipIfJournaldInContainer() + results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) results.WaitWithDefaultTimeout() Expect(results).To(ExitWithError()) }) It("two containers showing short container IDs: "+log, func() { + skipIfJournaldInContainer() SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time") + log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) log1.WaitWithDefaultTimeout() Expect(log1).Should(Exit(0)) @@ -192,6 +236,8 @@ var _ = Describe("Podman logs", func() { }) It("podman logs on a created container should result in 0 exit code: "+log, func() { + skipIfJournaldInContainer() + session := podmanTest.Podman([]string{"create", "--log-driver", log, "-t", "--name", "log", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).To(Exit(0)) @@ -202,6 +248,8 @@ var _ = Describe("Podman logs", func() { }) It("streaming output: "+log, func() { + skipIfJournaldInContainer() + containerName := "logs-f" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman-1; sleep 1; echo podman-2"}) @@ -210,6 +258,14 @@ var _ = Describe("Podman logs", func() { results := podmanTest.Podman([]string{"logs", "-f", containerName}) results.WaitWithDefaultTimeout() + + if log == "journald" && !isEventBackendJournald(podmanTest) { + // --follow + journald log-driver is only supported with journald events-backend(PR #10431) + Expect(results).To(Exit(125)) + Expect(results.ErrorToString()).To(ContainSubstring("using --follow with the journald --log-driver but without the journald --events-backend")) + return + } + Expect(results).To(Exit(0)) Expect(results.OutputToString()).To(ContainSubstring("podman-1")) @@ -233,6 +289,8 @@ var _ = Describe("Podman logs", func() { }) It("follow output stopped container: "+log, func() { + skipIfJournaldInContainer() + containerName := "logs-f" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-d", ALPINE, "true"}) @@ -241,10 +299,17 @@ var _ = Describe("Podman logs", func() { results := podmanTest.Podman([]string{"logs", "-f", containerName}) results.WaitWithDefaultTimeout() + if log == "journald" && !isEventBackendJournald(podmanTest) { + // --follow + journald log-driver is only supported with journald events-backend(PR #10431) + Expect(results).To(Exit(125)) + return + } Expect(results).To(Exit(0)) }) It("using container with container log-size: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--log-opt=max-size=10k", "-d", ALPINE, "sh", "-c", "echo podman podman podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -266,6 +331,8 @@ var _ = Describe("Podman logs", func() { }) It("Make sure logs match expected length: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -284,6 +351,8 @@ var _ = Describe("Podman logs", func() { }) It("podman logs test stdout and stderr: "+log, func() { + skipIfJournaldInContainer() + cname := "log-test" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"}) logc.WaitWithDefaultTimeout() diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index 6cddf9285..2205a1263 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -87,6 +87,7 @@ var _ = Describe("Podman network connect and disconnect", func() { dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) dis.WaitWithDefaultTimeout() Expect(dis).Should(Exit(0)) + Expect(dis.ErrorToString()).Should(Equal("")) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() @@ -183,6 +184,7 @@ var _ = Describe("Podman network connect and disconnect", func() { connect := podmanTest.Podman([]string{"network", "connect", newNetName, "test"}) connect.WaitWithDefaultTimeout() Expect(connect).Should(Exit(0)) + Expect(connect.ErrorToString()).Should(Equal("")) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 8aeba9d75..64b46756f 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -11,7 +11,6 @@ import ( "text/template" "time" - "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" @@ -1120,24 +1119,6 @@ var _ = Describe("Podman play kube", func() { Expect(label).To(ContainSubstring("unconfined_u:system_r:spc_t:s0")) }) - It("podman play kube should use default infra_image", func() { - err := writeYaml(checkInfraImagePodYaml, kubeYaml) - Expect(err).To(BeNil()) - - kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) - kube.WaitWithDefaultTimeout() - Expect(kube).Should(Exit(0)) - - podInspect := podmanTest.Podman([]string{"inspect", "check-infra-image", "--format", "{{ .InfraContainerID }}"}) - podInspect.WaitWithDefaultTimeout() - infraContainerID := podInspect.OutputToString() - - conInspect := podmanTest.Podman([]string{"inspect", infraContainerID, "--format", "{{ .ImageName }}"}) - conInspect.WaitWithDefaultTimeout() - infraContainerImage := conInspect.OutputToString() - Expect(infraContainerImage).To(Equal(config.DefaultInfraImage)) - }) - It("podman play kube --no-host", func() { err := writeYaml(checkInfraImagePodYaml, kubeYaml) Expect(err).To(BeNil()) @@ -2421,14 +2402,19 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) err := generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) - kube := podmanTest.Podman([]string{"play", "kube", "--log-driver", "journald", kubeYaml}) + kube := podmanTest.Podman([]string{"play", "kube", "--log-opt=max-size=10k", "--log-driver", "journald", kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .HostConfig.LogConfig.Type }}'"}) + cid := getCtrNameInPod(pod) + inspect := podmanTest.Podman([]string{"inspect", cid, "--format", "'{{ .HostConfig.LogConfig.Type }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring("journald")) + inspect = podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("10kB")) }) It("podman play kube test only creating the containers", func() { @@ -2800,6 +2786,34 @@ invalid kube kind Expect(exists).To(Exit(0)) }) + It("podman play kube use network mode from config", func() { + confPath, err := filepath.Abs("config/containers-netns2.conf") + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confPath) + defer os.Unsetenv("CONTAINERS_CONF") + if IsRemote() { + podmanTest.RestartRemoteService() + } + + pod := getPod() + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "{{.InfraContainerID}}"}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).To(Exit(0)) + infraID := podInspect.OutputToString() + + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.NetworkMode}}", infraID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("bridge")) + }) + It("podman play kube replace", func() { pod := getPod() err := generateKubeYaml("pod", pod, kubeYaml) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 34e879ed4..12aeffd1b 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -957,4 +957,22 @@ ENTRYPOINT ["sleep","99999"] Expect(ctr3.OutputToString()).To(ContainSubstring("hello")) }) + It("podman pod create read network mode from config", func() { + confPath, err := filepath.Abs("config/containers-netns.conf") + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confPath) + defer os.Unsetenv("CONTAINERS_CONF") + if IsRemote() { + podmanTest.RestartRemoteService() + } + + pod := podmanTest.Podman([]string{"pod", "create", "--name", "test", "--infra-name", "test-infra"}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.NetworkMode}}", "test-infra"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).Should(Equal("host")) + }) }) diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index b7e8309fb..7b35acd35 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -146,7 +146,7 @@ var _ = Describe("Podman push", func() { session = podmanTest.Podman([]string{"logs", "registry"}) session.WaitWithDefaultTimeout() - push := podmanTest.Podman([]string{"push", "--format=v2s2", "--creds=podmantest:test", ALPINE, "localhost:5000/tlstest"}) + push := podmanTest.Podman([]string{"push", "--tls-verify=true", "--format=v2s2", "--creds=podmantest:test", ALPINE, "localhost:5000/tlstest"}) push.WaitWithDefaultTimeout() Expect(push).To(ExitWithError()) @@ -163,7 +163,7 @@ var _ = Describe("Podman push", func() { if !IsRemote() { // remote does not support --cert-dir - push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"}) + push = podmanTest.Podman([]string{"push", "--tls-verify=true", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"}) push.WaitWithDefaultTimeout() Expect(push).To(ExitWithError()) } diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go index e2f2937ba..04952bb03 100644 --- a/test/e2e/run_memory_test.go +++ b/test/e2e/run_memory_test.go @@ -1,6 +1,7 @@ package integration import ( + "fmt" "os" "strconv" @@ -67,13 +68,17 @@ var _ = Describe("Podman run memory", func() { Expect(session.OutputToString()).To(Equal("41943040")) }) - It("podman run memory-swappiness test", func() { - SkipIfCgroupV2("memory-swappiness not supported on cgroupV2") - session := podmanTest.Podman([]string{"run", "--memory-swappiness=15", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).To(Equal("15")) - }) + for _, limit := range []string{"0", "15", "100"} { + limit := limit // Keep this value in a proper scope + testName := fmt.Sprintf("podman run memory-swappiness test(%s)", limit) + It(testName, func() { + SkipIfCgroupV2("memory-swappiness not supported on cgroupV2") + session := podmanTest.Podman([]string{"run", fmt.Sprintf("--memory-swappiness=%s", limit), ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(limit)) + }) + } It("podman run kernel-memory test", func() { if podmanTest.Host.Distribution == "ubuntu" { diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index ca242a17c..c64cfd2d5 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -357,6 +357,26 @@ var _ = Describe("Podman run networking", func() { Expect(ncBusy).To(ExitWithError()) }) + It("podman run slirp4netns verify net.ipv6.conf.default.accept_dad=0", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "ip", "addr"}) + session.Wait(30) + Expect(session).Should(Exit(0)) + // check the ipv6 setup id done without delay (https://github.com/containers/podman/issues/11062) + Expect(session.OutputToString()).To(ContainSubstring("inet6 fd00::")) + + const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/all/accept_dad" + + cat := SystemExec("cat", []string{ipv6ConfDefaultAcceptDadSysctl}) + cat.Wait(30) + Expect(cat).Should(Exit(0)) + sysctlValue := cat.OutputToString() + + session = podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "cat", ipv6ConfDefaultAcceptDadSysctl}) + session.Wait(30) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(sysctlValue)) + }) + It("podman run network expose host port 18082 to container port 8000 using slirp4netns port handler", func() { session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=slirp4netns", "-dt", "-p", "18082:8000", ALPINE, "/bin/sh"}) session.Wait(30) @@ -474,6 +494,23 @@ var _ = Describe("Podman run networking", func() { Expect(containerConfig[0].NetworkSettings.Ports["80/tcp"][0].HostPort).ToNot(Equal(80)) }) + It("podman run forward sctp protocol", func() { + SkipIfRootless("sctp protocol only works as root") + session := podmanTest.Podman([]string{"--log-level=info", "run", "--name=test", "-p", "80/sctp", "-p", "81/sctp", ALPINE}) + session.Wait(90) + Expect(session).Should(Exit(0)) + // we can only check logrus on local podman + if !IsRemote() { + // check that the info message for sctp protocol is only displayed once + Expect(strings.Count(session.ErrorToString(), "Port reservation for SCTP is not supported")).To(Equal(1), "`Port reservation for SCTP is not supported` is not displayed exactly one time in the logrus logs") + } + results := podmanTest.Podman([]string{"inspect", "test"}) + results.Wait(30) + Expect(results).Should(Exit(0)) + Expect(results.OutputToString()).To(ContainSubstring(`"80/sctp":`)) + Expect(results.OutputToString()).To(ContainSubstring(`"81/sctp":`)) + }) + It("podman run hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 8502879ff..ed2d8938d 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -188,6 +188,12 @@ var _ = Describe("Podman run", func() { run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(3)) + + // Now registries.conf will be consulted where localhost:5000 + // is set to be insecure. + run = podmanTest.Podman([]string{"run", ALPINE}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) }) It("podman run a container with a --rootfs", func() { @@ -1192,6 +1198,14 @@ USER mail`, BB) Expect(session.OutputToString()).To(ContainSubstring("devpts")) }) + It("podman run --mount type=devpts,target=/dev/pts with uid, gid and mode", func() { + // runc doesn't seem to honor uid= so avoid testing it + session := podmanTest.Podman([]string{"run", "-t", "--mount", "type=devpts,target=/dev/pts,uid=1000,gid=1001,mode=123", fedoraMinimal, "stat", "-c%g-%a", "/dev/pts/0"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("1001-123")) + }) + It("podman run --pod automatically", func() { session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8686"}) session.WaitWithDefaultTimeout() @@ -1367,13 +1381,13 @@ USER mail`, BB) } } - container := podmanTest.Podman([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) + container := podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) container.WaitWithDefaultTimeout() Expect(container).Should(Exit(0)) checkLines(container.OutputToStringArray()) // check that --cgroups=split is honored also when a container runs in a pod - container = podmanTest.Podman([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) + container = podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) container.WaitWithDefaultTimeout() Expect(container).Should(Exit(0)) checkLines(container.OutputToStringArray()) diff --git a/test/e2e/system_connection_test.go b/test/e2e/system_connection_test.go index 842ae8df6..76b442ce8 100644 --- a/test/e2e/system_connection_test.go +++ b/test/e2e/system_connection_test.go @@ -3,7 +3,11 @@ package integration import ( "fmt" "io/ioutil" + "net/url" "os" + "os/exec" + "os/user" + "path/filepath" "github.com/containers/common/pkg/config" . "github.com/containers/podman/v3/test/utils" @@ -19,22 +23,16 @@ var _ = Describe("podman system connection", func() { IsSet bool }{} - var ( - podmanTest *PodmanTestIntegration - ) + var podmanTest *PodmanTestIntegration BeforeEach(func() { ConfPath.Value, ConfPath.IsSet = os.LookupEnv("CONTAINERS_CONF") conf, err := ioutil.TempFile("", "containersconf") - if err != nil { - panic(err) - } + Expect(err).ToNot(HaveOccurred()) os.Setenv("CONTAINERS_CONF", conf.Name()) tempdir, err := CreateTempDirInTempDir() - if err != nil { - panic(err) - } + Expect(err).ToNot(HaveOccurred()) podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() }) @@ -49,196 +47,241 @@ var _ = Describe("podman system connection", func() { } f := CurrentGinkgoTestDescription() - timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) - GinkgoWriter.Write([]byte(timedResult)) + GinkgoWriter.Write( + []byte( + fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()))) }) - It("add ssh://", func() { - cmd := []string{"system", "connection", "add", - "--default", - "--identity", "~/.ssh/id_rsa", - "QA", - "ssh://root@server.fubar.com:2222/run/podman/podman.sock", - } - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("QA")) - Expect(cfg.Engine.ServiceDestinations["QA"]).To(Equal( - config.Destination{ - URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock", - Identity: "~/.ssh/id_rsa", - }, - )) - - cmd = []string{"system", "connection", "rename", - "QA", - "QE", - } - session = podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("QE")) - Expect(cfg.Engine.ServiceDestinations["QE"]).To(Equal( - config.Destination{ - URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock", - Identity: "~/.ssh/id_rsa", - }, - )) - }) + Context("without running API service", func() { + It("add ssh://", func() { + cmd := []string{"system", "connection", "add", + "--default", + "--identity", "~/.ssh/id_rsa", + "QA", + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) - It("add UDS", func() { - cmd := []string{"system", "connection", "add", - "QA-UDS", - "unix:///run/podman/podman.sock", - } - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS")) - Expect(cfg.Engine.ServiceDestinations["QA-UDS"]).To(Equal( - config.Destination{ - URI: "unix:///run/podman/podman.sock", - Identity: "", - }, - )) - - cmd = []string{"system", "connection", "add", - "QA-UDS1", - "--socket-path", "/run/user/podman/podman.sock", - "unix:///run/podman/podman.sock", - } - session = podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - - cfg, err = config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS")) - Expect(cfg.Engine.ServiceDestinations["QA-UDS1"]).To(Equal( - config.Destination{ - URI: "unix:///run/user/podman/podman.sock", - Identity: "", - }, - )) - }) + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg).To(HaveActiveService("QA")) + Expect(cfg).Should(VerifyService( + "QA", + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + "~/.ssh/id_rsa", + )) - It("add tcp", func() { - cmd := []string{"system", "connection", "add", - "QA-TCP", - "tcp://localhost:8888", - } - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("QA-TCP")) - Expect(cfg.Engine.ServiceDestinations["QA-TCP"]).To(Equal( - config.Destination{ - URI: "tcp://localhost:8888", - Identity: "", - }, - )) - }) + cmd = []string{"system", "connection", "rename", + "QA", + "QE", + } + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) - It("remove", func() { - cmd := []string{"system", "connection", "add", - "--default", - "--identity", "~/.ssh/id_rsa", - "QA", - "ssh://root@server.fubar.com:2222/run/podman/podman.sock", - } - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) + Expect(config.ReadCustomConfig()).To(HaveActiveService("QE")) + }) - for i := 0; i < 2; i++ { - cmd = []string{"system", "connection", "remove", "QA"} + It("add UDS", func() { + cmd := []string{"system", "connection", "add", + "QA-UDS", + "unix:///run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) + + Expect(config.ReadCustomConfig()).Should(VerifyService( + "QA-UDS", + "unix:///run/podman/podman.sock", + "", + )) + + cmd = []string{"system", "connection", "add", + "QA-UDS1", + "--socket-path", "/run/user/podman/podman.sock", + "unix:///run/podman/podman.sock", + } session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) + Expect(session.Out.Contents()).Should(BeEmpty()) - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(BeEmpty()) - Expect(cfg.Engine.ServiceDestinations).To(BeEmpty()) - } - }) + Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA-UDS")) + Expect(config.ReadCustomConfig()).Should(VerifyService( + "QA-UDS1", + "unix:///run/user/podman/podman.sock", + "", + )) + }) - It("default", func() { - for _, name := range []string{"devl", "qe"} { + It("add tcp", func() { cmd := []string{"system", "connection", "add", + "QA-TCP", + "tcp://localhost:8888", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) + + Expect(config.ReadCustomConfig()).Should(VerifyService( + "QA-TCP", + "tcp://localhost:8888", + "", + )) + }) + + It("remove", func() { + session := podmanTest.Podman([]string{"system", "connection", "add", + "--default", + "--identity", "~/.ssh/id_rsa", + "QA", + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // two passes to test that removing non-existent connection is not an error + for i := 0; i < 2; i++ { + session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) + + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(BeEmpty()) + Expect(cfg.Engine.ServiceDestinations).To(BeEmpty()) + } + }) + + It("remove --all", func() { + session := podmanTest.Podman([]string{"system", "connection", "add", "--default", "--identity", "~/.ssh/id_rsa", - name, + "QA", "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"system", "connection", "remove", "--all"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) + Expect(session.Err.Contents()).Should(BeEmpty()) + + session = podmanTest.Podman([]string{"system", "connection", "list"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("default", func() { + for _, name := range []string{"devl", "qe"} { + cmd := []string{"system", "connection", "add", + "--default", + "--identity", "~/.ssh/id_rsa", + name, + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) } + + cmd := []string{"system", "connection", "default", "devl"} session := podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - } + Expect(session.Out.Contents()).Should(BeEmpty()) - cmd := []string{"system", "connection", "default", "devl"} - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - - cfg, err := config.ReadCustomConfig() - Expect(err).ShouldNot(HaveOccurred()) - Expect(cfg.Engine.ActiveService).To(Equal("devl")) - - cmd = []string{"system", "connection", "list"} - session = podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("Name *URI *Identity *Default")) - - cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"} - session = podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.OutputToString()).Should(Equal("devl qe")) - }) + Expect(config.ReadCustomConfig()).Should(HaveActiveService("devl")) - It("failed default", func() { - cmd := []string{"system", "connection", "default", "devl"} - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).ShouldNot(Exit(0)) - Expect(session.Err).Should(Say("destination is not defined")) - }) + cmd = []string{"system", "connection", "list"} + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out).Should(Say("Name *URI *Identity *Default")) + + cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"} + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("devl qe")) + }) + + It("failed default", func() { + cmd := []string{"system", "connection", "default", "devl"} + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).ShouldNot(Exit(0)) + Expect(session.Err).Should(Say("destination is not defined")) + }) + + It("failed rename", func() { + cmd := []string{"system", "connection", "rename", "devl", "QE"} + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).ShouldNot(Exit(0)) + Expect(session.Err).Should(Say("destination is not defined")) + }) - It("failed rename", func() { - cmd := []string{"system", "connection", "rename", "devl", "QE"} - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).ShouldNot(Exit(0)) - Expect(session.Err).Should(Say("destination is not defined")) + It("empty list", func() { + cmd := []string{"system", "connection", "list"} + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).Should(Equal(1)) + Expect(session.Err.Contents()).Should(BeEmpty()) + }) }) - It("empty list", func() { - cmd := []string{"system", "connection", "list"} - session := podmanTest.Podman(cmd) - session.WaitWithDefaultTimeout() - Expect(session).Should(Exit(0)) - Expect(session.Out).Should(Say("")) - Expect(session.Err).Should(Say("")) + Context("sshd and API services required", func() { + BeforeEach(func() { + // These tests are unique in as much as they require podman, podman-remote, systemd and sshd. + // podman-remote commands will be executed by ginkgo directly. + SkipIfContainerized("sshd is not available when running in a container") + SkipIfRemote("connection heuristic requires both podman and podman-remote binaries") + SkipIfNotRootless("FIXME: setup ssh keys when root") + SkipIfSystemdNotRunning("cannot test connection heuristic if systemd is not running") + SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running") + }) + + It("add ssh:// socket path using connection heuristic", func() { + u, err := user.Current() + Expect(err).ShouldNot(HaveOccurred()) + + cmd := exec.Command(podmanTest.RemotePodmanBinary, + "system", "connection", "add", + "--default", + "--identity", filepath.Join(u.HomeDir, ".ssh", "id_ed25519"), + "QA", + fmt.Sprintf("ssh://%s@localhost", u.Username)) + + session, err := Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary)) + Eventually(session, DefaultWaitTimeout).Should(Exit(0)) + Expect(session.Out.Contents()).Should(BeEmpty()) + Expect(session.Err.Contents()).Should(BeEmpty()) + + uri := url.URL{ + Scheme: "ssh", + User: url.User(u.Username), + Host: "localhost:22", + Path: fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid), + } + + Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA")) + Expect(config.ReadCustomConfig()).Should(VerifyService( + "QA", + uri.String(), + filepath.Join(u.HomeDir, ".ssh", "id_ed25519"), + )) + }) }) }) diff --git a/test/e2e/system_reset_test.go b/test/e2e/system_reset_test.go index 102526a46..93ab166cd 100644 --- a/test/e2e/system_reset_test.go +++ b/test/e2e/system_reset_test.go @@ -60,6 +60,8 @@ var _ = Describe("podman system reset", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + Expect(session.ErrorToString()).To(Not(ContainSubstring("Failed to add pause process"))) + // If remote then the API service should have exited // On local tests this is a noop podmanTest.StartRemoteService() diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index a1b25b723..98def3d8f 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -4,7 +4,6 @@ import ( "io/ioutil" "os" "strings" - "time" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" @@ -82,27 +81,13 @@ WantedBy=multi-user.target run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", ubi_init, "/sbin/init"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) - ctrID := run.OutputToString() logs := podmanTest.Podman([]string{"logs", ctrName}) logs.WaitWithDefaultTimeout() Expect(logs).Should(Exit(0)) // Give container 10 seconds to start - started := false - for i := 0; i < 10; i++ { - runningCtrs := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) - runningCtrs.WaitWithDefaultTimeout() - Expect(runningCtrs).Should(Exit(0)) - - if strings.Contains(runningCtrs.OutputToString(), ctrID) { - started = true - break - } - - time.Sleep(1 * time.Second) - } - + started := podmanTest.WaitContainerReady(ctrName, "Reached target Multi-User System.", 30, 1) Expect(started).To(BeTrue()) systemctl := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "systemctl", "status", "--no-pager"}) diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go index 7f97f280a..b591e1c02 100644 --- a/test/e2e/trust_test.go +++ b/test/e2e/trust_test.go @@ -14,7 +14,8 @@ import ( var _ = Describe("Podman trust", func() { var ( - tempdir string + tempdir string + err error podmanTest *PodmanTestIntegration ) @@ -38,21 +39,17 @@ var _ = Describe("Podman trust", func() { }) It("podman image trust show", func() { - path, err := os.Getwd() - if err != nil { - os.Exit(1) - } - session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Dir(path), "--policypath", filepath.Join(filepath.Dir(path), "policy.json")}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json")}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) outArray := session.OutputToStringArray() Expect(len(outArray)).To(Equal(3)) - // image order is not guaranteed. All we can do is check that - // these strings appear in output, we can't cross-check them. - Expect(session.OutputToString()).To(ContainSubstring("accept")) - Expect(session.OutputToString()).To(ContainSubstring("reject")) - Expect(session.OutputToString()).To(ContainSubstring("signed")) + // Repository order is not guaranteed. So, check that + // all expected lines appear in output; we also check total number of lines, so that handles all of them. + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^default\s+accept\s*$`)) + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^docker.io/library/hello-world\s+reject\s*$`)) + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^registry.access.redhat.com\s+signedBy\s+security@redhat.com, security@redhat.com\s+https://access.redhat.com/webassets/docker/content/sigstore\s*$`)) }) It("podman image trust set", func() { @@ -76,24 +73,52 @@ var _ = Describe("Podman trust", func() { }) It("podman image trust show --json", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--json"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) var teststruct []map[string]string json.Unmarshal(session.Out.Contents(), &teststruct) - Expect(teststruct[0]["name"]).To(Equal("* (default)")) - Expect(teststruct[0]["repo_name"]).To(Equal("default")) - Expect(teststruct[0]["type"]).To(Equal("accept")) - Expect(teststruct[1]["type"]).To(Equal("insecureAcceptAnything")) + Expect(len(teststruct)).To(Equal(3)) + // To ease comparison, group the unordered array of repos by repo (and we expect only one entry by repo, so order within groups doesn’t matter) + repoMap := map[string][]map[string]string{} + for _, e := range teststruct { + key := e["name"] + repoMap[key] = append(repoMap[key], e) + } + Expect(repoMap).To(Equal(map[string][]map[string]string{ + "* (default)": {{ + "name": "* (default)", + "repo_name": "default", + "sigstore": "", + "transport": "", + "type": "accept", + }}, + "docker.io/library/hello-world": {{ + "name": "docker.io/library/hello-world", + "repo_name": "docker.io/library/hello-world", + "sigstore": "", + "transport": "", + "type": "reject", + }}, + "registry.access.redhat.com": {{ + "name": "registry.access.redhat.com", + "repo_name": "registry.access.redhat.com", + "sigstore": "https://access.redhat.com/webassets/docker/content/sigstore", + "transport": "", + "type": "signedBy", + "gpg_id": "security@redhat.com, security@redhat.com", + }}, + })) }) It("podman image trust show --raw", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--raw"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--raw"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) + contents, err := ioutil.ReadFile(filepath.Join(INTEGRATION_ROOT, "test/policy.json")) + Expect(err).ShouldNot(HaveOccurred()) Expect(session.IsJSONOutputValid()).To(BeTrue()) - Expect(session.OutputToString()).To(ContainSubstring("default")) - Expect(session.OutputToString()).To(ContainSubstring("insecureAcceptAnything")) + Expect(string(session.Out.Contents())).To(Equal(string(contents) + "\n")) }) }) diff --git a/test/e2e/unshare_test.go b/test/e2e/unshare_test.go index 79ce68e89..cf1b8db53 100644 --- a/test/e2e/unshare_test.go +++ b/test/e2e/unshare_test.go @@ -51,7 +51,7 @@ var _ = Describe("Podman unshare", func() { }) It("podman unshare --rootles-cni", func() { - session := podmanTest.Podman([]string{"unshare", "--rootless-cni", "ip", "addr"}) + session := podmanTest.Podman([]string{"unshare", "--rootless-netns", "ip", "addr"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("tap0")) diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go index 75986e671..9398248b8 100644 --- a/test/e2e/version_test.go +++ b/test/e2e/version_test.go @@ -31,7 +31,6 @@ var _ = Describe("Podman version", func() { f := CurrentGinkgoTestDescription() processTestResult(f) podmanTest.SeedImages() - }) It("podman version", func() { @@ -96,4 +95,13 @@ var _ = Describe("Podman version", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) + + It("podman help", func() { + session := podmanTest.Podman([]string{"help"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should( + ContainSubstring("Display the Podman version information"), + ) + }) }) diff --git a/test/python/docker/compat/test_containers.py b/test/python/docker/compat/test_containers.py index 1ad1e7f15..e6f7d992d 100644 --- a/test/python/docker/compat/test_containers.py +++ b/test/python/docker/compat/test_containers.py @@ -251,3 +251,17 @@ class TestContainers(unittest.TestCase): ctr.start() ret, out = ctr.exec_run(["stat", "-c", "%u:%g", "/workspace"]) self.assertEqual(out.rstrip(), b'1042:1043', "UID/GID set in dockerfile") + + + def test_non_existant_workdir(self): + dockerfile = (B'FROM quay.io/libpod/alpine:latest\n' + B'USER root\n' + B'WORKDIR /workspace/scratch\n' + B'RUN touch test') + img: Image + img, out = self.client.images.build(fileobj=io.BytesIO(dockerfile)) + ctr: Container = self.client.containers.create(image=img.id, detach=True, command="top", + volumes=["test_non_existant_workdir:/workspace"]) + ctr.start() + ret, out = ctr.exec_run(["stat", "/workspace/scratch/test"]) + self.assertEqual(ret, 0, "Working directory created if it doesn't exist") diff --git a/test/registries.conf b/test/registries.conf index 0559c9e52..8e4671760 100644 --- a/test/registries.conf +++ b/test/registries.conf @@ -15,3 +15,9 @@ location="mirror.gcr.io" [[registry]] prefix="docker.io/library" location="quay.io/libpod" + +# For testing #11933 to make sure that registries.conf is consulted unless +# --tls-verify is used during container creation. +[[registry]] +location="localhost:5000" +insecure=true diff --git a/test/system/001-basic.bats b/test/system/001-basic.bats index 78b8ecdfd..03f07d602 100644 --- a/test/system/001-basic.bats +++ b/test/system/001-basic.bats @@ -120,9 +120,7 @@ function setup() { fi run_podman 125 --remote - is "$output" "Error: missing command 'podman COMMAND' -Try 'podman --help' for more information." \ - "podman --remote show usage message without running endpoint" + is "$output" ".*Usage:" "podman --remote show usage message without running endpoint" } # This is for development only; it's intended to make sure our timeout diff --git a/test/system/011-image.bats b/test/system/011-image.bats new file mode 100644 index 000000000..5150e875e --- /dev/null +++ b/test/system/011-image.bats @@ -0,0 +1,54 @@ +#!/usr/bin/env bats + +load helpers + +function setup() { + skip_if_remote "--sign-by does not work with podman-remote" + + basic_setup + + export _GNUPGHOME_TMP=$PODMAN_TMPDIR/.gnupg + mkdir --mode=0700 $_GNUPGHOME_TMP $PODMAN_TMPDIR/signatures + + cat >$PODMAN_TMPDIR/keydetails <<EOF + %echo Generating a basic OpenPGP key + Key-Type: RSA + Key-Length: 2048 + Subkey-Type: RSA + Subkey-Length: 2048 + Name-Real: Foo + Name-Comment: Foo + Name-Email: foo@bar.com + Expire-Date: 0 + %no-ask-passphrase + %no-protection + # Do a commit here, so that we can later print "done" :-) + %commit + %echo done +EOF + GNUPGHOME=$_GNUPGHOME_TMP gpg --verbose --batch --gen-key $PODMAN_TMPDIR/keydetails +} + +function check_signature() { + local sigfile=$1 + ls -laR $PODMAN_TMPDIR/signatures + run_podman inspect --format '{{.Digest}}' $PODMAN_TEST_IMAGE_FQN + local repodigest=${output/:/=} + + local dir="$PODMAN_TMPDIR/signatures/libpod/${PODMAN_TEST_IMAGE_NAME}@${repodigest}" + test -d $dir || die "Missing signature directory $dir" + test -e "$dir/$sigfile" || die "Missing signature file '$sigfile'" + + # Confirm good signature + run env GNUPGHOME=$_GNUPGHOME_TMP gpg --verify "$dir/$sigfile" + is "$output" ".*Good signature from .Foo.*<foo@bar.com>" \ + "gpg --verify $sigfile" +} + + +@test "podman image - sign with no sigfile" { + GNUPGHOME=$_GNUPGHOME_TMP run_podman image sign --sign-by foo@bar.com --directory $PODMAN_TMPDIR/signatures "docker://$PODMAN_TEST_IMAGE_FQN" + check_signature "signature-1" +} + +# vim: filetype=sh diff --git a/test/system/015-help.bats b/test/system/015-help.bats index b0795b524..a87081687 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -149,12 +149,12 @@ function check_help() { count=$(expr $count + 1) done - # Any command that takes subcommands, must throw error if called + # Any command that takes subcommands, prints its help and errors if called # without one. dprint "podman $@" run_podman '?' "$@" is "$status" 125 "'podman $*' without any subcommand - exit status" - is "$output" "Error: missing command .*$@ COMMAND" \ + is "$output" ".*Usage:.*Error: missing command '.*$@ COMMAND'" \ "'podman $*' without any subcommand - expected error message" # Assume that 'NoSuchCommand' is not a command diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index 44b66676e..7fb3e62e4 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -30,6 +30,17 @@ load helpers run_podman rm $cid } +function _additional_events_backend() { + local driver=$1 + # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend. + if [[ $driver = "journald" ]]; then + run_podman info --format '{{.Host.EventLogger}}' >/dev/null + if [[ $output != "journald" ]]; then + echo "--events-backend journald" + fi + fi +} + function _log_test_multi() { local driver=$1 @@ -42,10 +53,12 @@ function _log_test_multi() { etc='.*' fi + local events_backend=$(_additional_events_backend $driver) + # Simple helper to make the container starts, below, easier to read local -a cid doit() { - run_podman run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2"; + run_podman ${events_backend} run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2"; cid+=($(echo "${output:0:12}")) } @@ -57,7 +70,7 @@ function _log_test_multi() { doit c1 "echo a;sleep 10;echo d;sleep 3" doit c2 "sleep 1;echo b;sleep 2;echo c;sleep 3" - run_podman logs -f c1 c2 + run_podman ${events_backend} logs -f c1 c2 is "$output" \ "${cid[0]} a$etc ${cid[1]} b$etc @@ -187,15 +200,20 @@ function _log_test_follow() { contentA=$(random_string) contentB=$(random_string) contentC=$(random_string) + local events_backend=$(_additional_events_backend $driver) + + if [[ -n "${events_backend}" ]]; then + skip_if_remote "remote does not support --events-backend" + fi # Note: it seems we need at least three log lines to hit #11461. - run_podman run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC" - run_podman logs -f $cname + run_podman ${events_backend} run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC" + run_podman ${events_backend} logs -f $cname is "$output" "$contentA $contentB $contentC" "logs -f on exitted container works" - run_podman rm -t 0 -f $cname + run_podman ${events_backend} rm -t 0 -f $cname } @test "podman logs - --follow k8s-file" { diff --git a/test/system/070-build.bats b/test/system/070-build.bats index d3dc14d81..3c47b1f5b 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -39,6 +39,8 @@ EOF cat >$dockerfile <<EOF FROM $IMAGE RUN echo $rand_content > /$rand_filename +VOLUME /a/b/c +VOLUME ['/etc/foo', '/etc/bar'] EOF run_podman buildx build --load -t build_test --format=docker $tmpdir @@ -47,6 +49,33 @@ EOF run_podman run --rm build_test cat /$rand_filename is "$output" "$rand_content" "reading generated file in image" + # Make sure the volumes are created at surprising yet Docker-compatible + # destinations (see bugzilla.redhat.com/show_bug.cgi?id=2014149). + run_podman run --rm build_test find /[ /etc/bar\] -print + is "$output" "/\[ +/\[/etc +/\[/etc/foo, +/etc/bar]" "weird VOLUME gets converted to directories with brackets and comma" + + # Now confirm that each volume got a unique device ID + run_podman run --rm build_test stat -c '%D' / /a /a/b /a/b/c /\[ /\[/etc /\[/etc/foo, /etc /etc/bar\] + # First, the non-volumes should all be the same... + is "${lines[0]}" "${lines[1]}" "devnum( / ) = devnum( /a )" + is "${lines[0]}" "${lines[2]}" "devnum( / ) = devnum( /a/b )" + is "${lines[0]}" "${lines[4]}" "devnum( / ) = devnum( /[ )" + is "${lines[0]}" "${lines[5]}" "devnum( / ) = devnum( /[etc )" + is "${lines[0]}" "${lines[7]}" "devnum( / ) = devnum( /etc )" + is "${lines[6]}" "${lines[8]}" "devnum( /[etc/foo, ) = devnum( /etc/bar] )" + # ...then, each volume should be different + if [[ "${lines[0]}" = "${lines[3]}" ]]; then + die "devnum( / ) (${lines[0]}) = devnum( volume0 ) (${lines[3]}) -- they should differ" + fi + if [[ "${lines[0]}" = "${lines[6]}" ]]; then + die "devnum( / ) (${lines[0]}) = devnum( volume1 ) (${lines[6]}) -- they should differ" + fi + # FIXME: is this expected? I thought /a/b/c and /[etc/foo, would differ + is "${lines[3]}" "${lines[6]}" "devnum( volume0 ) = devnum( volume1 )" + run_podman rmi -f build_test } diff --git a/test/system/120-load.bats b/test/system/120-load.bats index e9959271f..a5508b2f4 100644 --- a/test/system/120-load.bats +++ b/test/system/120-load.bats @@ -126,6 +126,26 @@ verify_iid_and_name() { verify_iid_and_name $img_name } +@test "podman load - from URL" { + get_iid_and_name + run_podman save $img_name -o $archive + run_podman rmi $iid + + HOST_PORT=$(random_free_port) + SERVER=http://127.0.0.1:$HOST_PORT + + # Bind-mount the archive to a container running httpd + run_podman run -d --name myweb -p "$HOST_PORT:80" \ + -v $archive:/var/www/image.tar:Z \ + -w /var/www \ + $IMAGE /bin/busybox-extras httpd -f -p 80 + + run_podman load -i $SERVER/image.tar + verify_iid_and_name $img_name + + run_podman rm -f -t0 myweb +} + @test "podman load - redirect corrupt payload" { run_podman 125 load <<< "Danger, Will Robinson!! This is a corrupt tarball!" is "$output" \ diff --git a/test/system/130-kill.bats b/test/system/130-kill.bats index d85f0a6a9..1ff3a7b61 100644 --- a/test/system/130-kill.bats +++ b/test/system/130-kill.bats @@ -6,9 +6,22 @@ load helpers @test "podman kill - test signal handling in containers" { + + # Prepare for 'logs -f' + run_podman info --format '{{.Host.LogDriver}}' + log_driver=$output + run_podman info --format '{{.Host.EventLogger}}' + event_logger=$output + opt_log_driver= + if [ $log_driver = "journald" ] && [ $event_logger != "journald" ]; then + # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend. + # Set '--log driver' temporally because remote doesn't support '--events-backend'. + opt_log_driver="--log-driver k8s-file" + fi + # Start a container that will handle all signals by emitting 'got: N' local -a signals=(1 2 3 4 5 6 8 10 12 13 14 15 16 20 21 22 23 24 25 26 64) - run_podman run -d $IMAGE sh -c \ + run_podman run -d ${opt_log_driver} $IMAGE sh -c \ "for i in ${signals[*]}; do trap \"echo got: \$i\" \$i; done; echo READY; while ! test -e /stop; do sleep 0.05; done; diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 86f3610ab..60cfbc9dd 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -61,6 +61,29 @@ function teardown() { } +@test "podman pod create - custom infra image" { + image="i.do/not/exist:image" + + tmpdir=$PODMAN_TMPDIR/pod-test + run mkdir -p $tmpdir + containersconf=$tmpdir/containers.conf + cat >$containersconf <<EOF +[engine] +infra_image="$image" +EOF + + run_podman 125 pod create --infra-image $image + is "$output" ".*initializing source docker://$image:.*" + + CONTAINERS_CONF=$containersconf run_podman 125 pod create + is "$output" ".*initializing source docker://$image:.*" +} + +function rm_podman_pause_image() { + run_podman version --format "{{.Server.Version}}-{{.Server.Built}}" + run_podman rmi -f "localhost/podman-pause:$output" +} + @test "podman pod - communicating between pods" { podname=pod$(random_string) run_podman 1 pod exists $podname @@ -100,19 +123,14 @@ function teardown() { # Clean up. First the nc -l container... run_podman rm $cid1 - # ...then, from pause container, find the image ID of the pause image... - run_podman pod inspect --format '{{(index .Containers 0).ID}}' $podname - pause_cid="$output" - run_podman container inspect --format '{{.Image}}' $pause_cid - pause_iid="$output" - # ...then rm the pod, then rmi the pause image so we don't leave strays. run_podman pod rm $podname - run_podman rmi $pause_iid # Pod no longer exists run_podman 1 pod exists $podid run_podman 1 pod exists $podname + + rm_podman_pause_image } @test "podman pod - communicating via /dev/shm " { @@ -133,6 +151,10 @@ function teardown() { # Pod no longer exists run_podman 1 pod exists $podid run_podman 1 pod exists $podname + + # Pause image hasn't been pulled + run_podman 1 image exists k8s.gcr.io/pause:3.5 + rm_podman_pause_image } # Random byte @@ -303,16 +325,25 @@ EOF run_podman rm $cid run_podman pod rm -t 0 -f mypod run_podman rmi $infra_image - } @test "podman pod create should fail when infra-name is already in use" { local infra_name="infra_container_$(random_string 10 | tr A-Z a-z)" - run_podman pod create --infra-name "$infra_name" + local pod_name="$(random_string 10 | tr A-Z a-z)" + + # Note that the internal pause image is built even when --infra-image is + # set to the K8s one. + run_podman pod create --name $pod_name --infra-name "$infra_name" --infra-image "k8s.gcr.io/pause:3.5" run_podman '?' pod create --infra-name "$infra_name" if [ $status -eq 0 ]; then die "Podman should fail when user try to create two pods with the same infra-name value" fi + run_podman pod rm -f $pod_name + run_podman images -a + + # Pause image hasn't been pulled + run_podman 1 image exists k8s.gcr.io/pause:3.5 + rm_podman_pause_image } # vim: filetype=sh diff --git a/test/system/272-system-connection.bats b/test/system/272-system-connection.bats index 5a90d9398..4e9ac4dd6 100644 --- a/test/system/272-system-connection.bats +++ b/test/system/272-system-connection.bats @@ -34,10 +34,7 @@ function teardown() { | xargs -l1 --no-run-if-empty umount # Remove all system connections - run_podman system connection ls --format json - while read name; do - run_podman system connection rm "$name" - done < <(jq -r '.[].Name' <<<"$output") + run_podman system connection rm --all basic_teardown } @@ -53,7 +50,8 @@ function _run_podman_remote() { # Very basic test, does not actually connect at any time @test "podman system connection - basic add / ls / remove" { run_podman system connection ls - is "$output" "" "system connection ls: no connections" + is "$output" "Name URI Identity Default" \ + "system connection ls: no connections" c1="c1_$(random_string 15)" c2="c2_$(random_string 15)" diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index cb73cf24d..21350ed36 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -78,6 +78,9 @@ load helpers if [[ -z $cidr ]]; then # regex to match that we are in 10.X subnet match="10\..*" + # force bridge networking also for rootless + # this ensures that rootless + bridge + userns + ports works + network_arg="--network bridge" else # Issue #9828 make sure a custom slir4netns cidr also works network_arg="--network slirp4netns:cidr=$cidr" @@ -167,6 +170,13 @@ load helpers $IMAGE nc -l -n -v -p $myport cid="$output" + # FIXME: debugging for #11871 + run_podman exec $cid cat /etc/resolv.conf + if is_rootless; then + run_podman unshare --rootless-cni cat /etc/resolv.conf + fi + ps uxww + # check that dns is working inside the container run_podman exec $cid nslookup google.com @@ -420,6 +430,7 @@ load helpers is "$output" "[${cid:0:12}]" "short container id in network aliases" run_podman network disconnect $netname $cid + is "$output" "" "Output should be empty (no errors)" # check that we cannot curl (timeout after 3 sec) run curl --max-time 3 -s $SERVER/index.txt @@ -428,6 +439,7 @@ load helpers fi run_podman network connect $netname $cid + is "$output" "" "Output should be empty (no errors)" # curl should work again run curl --max-time 3 -s $SERVER/index.txt @@ -444,8 +456,23 @@ load helpers die "MAC address did not change after podman network disconnect/connect" fi + # Disconnect/reconnect of a container *with no ports* should succeed quietly + run_podman network disconnect $netname $background_cid + is "$output" "" "disconnect of container with no open ports" + run_podman network connect $netname $background_cid + is "$output" "" "(re)connect of container with no open ports" + + # FIXME FIXME FIXME: #11825: bodhi tests are failing, remote+rootless only, + # with "dnsmasq: failed to create inotify". This error has never occurred + # in CI, and Ed has been unable to reproduce it on 1minutetip. This next + # line is a suggestion from Paul Holzinger for trying to shed light on + # the system context before the failure. This output will be invisible + # if the test passes. + for foo in /proc/\*/fd/*; do readlink -f $foo; done |grep '^/proc/.*inotify' |cut -d/ -f3 | xargs -I '{}' -- ps --no-headers -o '%p %U %a' -p '{}' |uniq -c |sort -n + # connect a second network run_podman network connect $netname2 $cid + is "$output" "" "Output should be empty (no errors)" # check network2 alias for container short id run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").Aliases}}" diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 8cf279ada..c3e5e9354 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -76,6 +76,12 @@ RELABEL="system_u:object_r:container_file_t:s0" is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened" fi + # Make sure that the K8s pause image isn't pulled but the local podman-pause is built. + run_podman images + run_podman 1 image exists k8s.gcr.io/pause + run_podman version --format "{{.Server.Version}}-{{.Server.Built}}" + run_podman image exists localhost/podman-pause:$output + run_podman stop -a -t 0 run_podman pod rm -t 0 -f test_pod } diff --git a/test/upgrade/test-upgrade.bats b/test/upgrade/test-upgrade.bats index 5cb302a85..c0d601586 100644 --- a/test/upgrade/test-upgrade.bats +++ b/test/upgrade/test-upgrade.bats @@ -97,8 +97,10 @@ podman \$opts run --name myfailedcontainer --label mylabel=$LABEL_FAILED \ podman \$opts run -d --name myrunningcontainer --label mylabel=$LABEL_RUNNING \ --network bridge \ -p $HOST_PORT:80 \ + -p 127.0.0.1:8080-8082:8080-8082 \ -v $pmroot/var/www:/var/www \ -w /var/www \ + --mac-address aa:bb:cc:dd:ee:ff \ $IMAGE /bin/busybox-extras httpd -f -p 80 podman \$opts pod create --name mypod @@ -121,8 +123,15 @@ EOF # Clean up vestiges of previous run $PODMAN rm -f podman_parent || true - # Not entirely a NOP! This is just so we get /run/crun created on a CI VM - $PODMAN run --rm $OLD_PODMAN true + + local netname=testnet-$(random_string 10) + $PODMAN network create $netname + + # Not entirely a NOP! This is just so we get the /run/... mount points created on a CI VM + # --mac-address is needed to create /run/cni, --network is needed to create /run/containers for dnsname + $PODMAN run --rm --mac-address 78:28:a6:8d:24:8a --network $netname $OLD_PODMAN true + $PODMAN network rm -f $netname + # # Use new-podman to run the above script under old-podman. @@ -134,7 +143,8 @@ EOF # # mount /etc/containers/storage.conf to use the same storage settings as on the host # mount /dev/shm because the container locks are stored there - # mount /var/lib/cni and /etc/cni/net.d for cni networking + # mount /var/lib/cni, /run/cni and /etc/cni/net.d for cni networking + # mount /run/containers for the dnsname plugin # $PODMAN run -d --name podman_parent --pid=host \ --privileged \ @@ -145,6 +155,8 @@ EOF -v /dev/fuse:/dev/fuse \ -v /run/crun:/run/crun \ -v /run/netns:/run/netns:rshared \ + -v /run/containers:/run/containers \ + -v /run/cni:/run/cni \ -v /var/lib/cni:/var/lib/cni \ -v /etc/cni/net.d:/etc/cni/net.d \ -v /dev/shm:/dev/shm \ @@ -185,7 +197,7 @@ EOF is "${lines[1]}" "mycreatedcontainer--Created----$LABEL_CREATED" "created" is "${lines[2]}" "mydonecontainer--Exited (0).*----<no value>" "done" is "${lines[3]}" "myfailedcontainer--Exited (17) .*----$LABEL_FAILED" "fail" - is "${lines[4]}" "myrunningcontainer--Up .*--0.0.0.0:$HOST_PORT->80/tcp--$LABEL_RUNNING" "running" + is "${lines[4]}" "myrunningcontainer--Up .*--0\.0\.0\.0:$HOST_PORT->80\/tcp, 127\.0\.0\.1\:8080-8082->8080-8082\/tcp--$LABEL_RUNNING" "running" # For debugging: dump containers and IDs if [[ -n "$PODMAN_UPGRADE_TEST_DEBUG" ]]; then diff --git a/test/utils/matchers.go b/test/utils/matchers.go index 07c1232e7..17ff3ea75 100644 --- a/test/utils/matchers.go +++ b/test/utils/matchers.go @@ -2,57 +2,164 @@ package utils import ( "fmt" + "net/url" + "github.com/containers/common/pkg/config" + . "github.com/onsi/gomega" "github.com/onsi/gomega/format" "github.com/onsi/gomega/gexec" + "github.com/onsi/gomega/matchers" + "github.com/onsi/gomega/types" ) +// HaveActiveService verifies the given service is the active service +func HaveActiveService(name interface{}) OmegaMatcher { + return WithTransform( + func(cfg *config.Config) string { + return cfg.Engine.ActiveService + }, + Equal(name)) +} + +type ServiceMatcher struct { + types.GomegaMatcher + Name interface{} + URI interface{} + Identity interface{} + failureMessage string + negatedFailureMessage string +} + +func VerifyService(name, uri, identity interface{}) OmegaMatcher { + return &ServiceMatcher{ + Name: name, + URI: uri, + Identity: identity, + } +} + +func (matcher *ServiceMatcher) Match(actual interface{}) (success bool, err error) { + cfg, ok := actual.(*config.Config) + if !ok { + return false, fmt.Errorf("ServiceMatcher matcher expects a config.Config") + } + + if _, err = url.Parse(matcher.URI.(string)); err != nil { + return false, err + } + + success, err = HaveKey(matcher.Name).Match(cfg.Engine.ServiceDestinations) + if !success || err != nil { + matcher.failureMessage = HaveKey(matcher.Name).FailureMessage(cfg.Engine.ServiceDestinations) + matcher.negatedFailureMessage = HaveKey(matcher.Name).NegatedFailureMessage(cfg.Engine.ServiceDestinations) + return + } + + sd := cfg.Engine.ServiceDestinations[matcher.Name.(string)] + success, err = Equal(matcher.URI).Match(sd.URI) + if !success || err != nil { + matcher.failureMessage = Equal(matcher.URI).FailureMessage(sd.URI) + matcher.negatedFailureMessage = Equal(matcher.URI).NegatedFailureMessage(sd.URI) + return + } + + success, err = Equal(matcher.Identity).Match(sd.Identity) + if !success || err != nil { + matcher.failureMessage = Equal(matcher.Identity).FailureMessage(sd.Identity) + matcher.negatedFailureMessage = Equal(matcher.Identity).NegatedFailureMessage(sd.Identity) + return + } + + return true, nil +} + +func (matcher *ServiceMatcher) FailureMessage(_ interface{}) string { + return matcher.failureMessage +} + +func (matcher *ServiceMatcher) NegatedFailureMessage(_ interface{}) string { + return matcher.negatedFailureMessage +} + +type URLMatcher struct { + matchers.EqualMatcher +} + +// VerifyURL matches when actual is a valid URL and matches expected +func VerifyURL(uri interface{}) OmegaMatcher { + return &URLMatcher{matchers.EqualMatcher{Expected: uri}} +} + +func (matcher *URLMatcher) Match(actual interface{}) (bool, error) { + e, ok := matcher.Expected.(string) + if !ok { + return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", matcher.Expected) + } + e_uri, err := url.Parse(e) + if err != nil { + return false, err + } + + a, ok := actual.(string) + if !ok { + return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", actual) + } + a_uri, err := url.Parse(a) + if err != nil { + return false, err + } + + return (&matchers.EqualMatcher{Expected: e_uri}).Match(a_uri) +} + +type ExitMatcher struct { + types.GomegaMatcher + Expected int + Actual int +} + // ExitWithError matches when assertion is > argument. Default 0 -// Modeled after the gomega Exit() matcher -func ExitWithError(optionalExitCode ...int) *exitMatcher { +// Modeled after the gomega Exit() matcher and also operates on sessions. +func ExitWithError(optionalExitCode ...int) *ExitMatcher { exitCode := 0 if len(optionalExitCode) > 0 { exitCode = optionalExitCode[0] } - return &exitMatcher{exitCode: exitCode} + return &ExitMatcher{Expected: exitCode} } -type exitMatcher struct { - exitCode int - actualExitCode int -} - -func (m *exitMatcher) Match(actual interface{}) (success bool, err error) { +// Match follows gexec.Matcher interface +func (matcher *ExitMatcher) Match(actual interface{}) (success bool, err error) { exiter, ok := actual.(gexec.Exiter) if !ok { return false, fmt.Errorf("ExitWithError must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n#{format.Object(actual, 1)}") } - m.actualExitCode = exiter.ExitCode() - if m.actualExitCode == -1 { + matcher.Actual = exiter.ExitCode() + if matcher.Actual == -1 { return false, nil } - return m.actualExitCode > m.exitCode, nil + return matcher.Actual > matcher.Expected, nil } -func (m *exitMatcher) FailureMessage(actual interface{}) (message string) { - if m.actualExitCode == -1 { +func (matcher *ExitMatcher) FailureMessage(_ interface{}) (message string) { + if matcher.Actual == -1 { return "Expected process to exit. It did not." } - return format.Message(m.actualExitCode, "to be greater than exit code:", m.exitCode) + return format.Message(matcher.Actual, "to be greater than exit code: ", matcher.Expected) } -func (m *exitMatcher) NegatedFailureMessage(actual interface{}) (message string) { - if m.actualExitCode == -1 { +func (matcher *ExitMatcher) NegatedFailureMessage(_ interface{}) (message string) { + switch { + case matcher.Actual == -1: return "you really shouldn't be able to see this!" - } else { - if m.exitCode == -1 { - return "Expected process not to exit. It did." - } - return format.Message(m.actualExitCode, "is less than or equal to exit code:", m.exitCode) + case matcher.Expected == -1: + return "Expected process not to exit. It did." } + return format.Message(matcher.Actual, "is less than or equal to exit code: ", matcher.Expected) } -func (m *exitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { + +func (matcher *ExitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { session, ok := actual.(*gexec.Session) if ok { return session.ExitCode() == -1 diff --git a/test/utils/podmantest_test.go b/test/utils/podmantest_test.go index 9bd1c4a66..1bb9ecb6b 100644 --- a/test/utils/podmantest_test.go +++ b/test/utils/podmantest_test.go @@ -23,7 +23,7 @@ var _ = Describe("PodmanTest test", func() { FakeOutputs["check"] = []string{"check"} os.Setenv("HOOK_OPTION", "hook_option") env := os.Environ() - session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil) + session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil, nil) os.Unsetenv("HOOK_OPTION") session.WaitWithDefaultTimeout() Expect(session.Command.Process).ShouldNot(BeNil()) diff --git a/test/utils/utils.go b/test/utils/utils.go index bfefc58ec..8d1edb23a 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -66,27 +66,31 @@ func (p *PodmanTest) MakeOptions(args []string, noEvents, noCache bool) []string // PodmanAsUserBase exec podman as user. uid and gid is set for credentials usage. env is used // to record the env for debugging -func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, extraFiles []*os.File) *PodmanSession { +func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, wrapper []string, extraFiles []*os.File) *PodmanSession { var command *exec.Cmd podmanOptions := p.MakeOptions(args, noEvents, noCache) podmanBinary := p.PodmanBinary if p.RemoteTest { podmanBinary = p.RemotePodmanBinary } + + runCmd := append(wrapper, podmanBinary) if p.RemoteTest { podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...) } if env == nil { - fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(podmanOptions, " ")) + fmt.Printf("Running: %s %s\n", strings.Join(runCmd, " "), strings.Join(podmanOptions, " ")) } else { - fmt.Printf("Running: (env: %v) %s %s\n", env, podmanBinary, strings.Join(podmanOptions, " ")) + fmt.Printf("Running: (env: %v) %s %s\n", env, strings.Join(runCmd, " "), strings.Join(podmanOptions, " ")) } if uid != 0 || gid != 0 { pythonCmd := fmt.Sprintf("import os; import sys; uid = %d; gid = %d; cwd = '%s'; os.setgid(gid); os.setuid(uid); os.chdir(cwd) if len(cwd)>0 else True; os.execv(sys.argv[1], sys.argv[1:])", gid, uid, cwd) - nsEnterOpts := append([]string{"-c", pythonCmd, podmanBinary}, podmanOptions...) + runCmd = append(runCmd, podmanOptions...) + nsEnterOpts := append([]string{"-c", pythonCmd}, runCmd...) command = exec.Command("python", nsEnterOpts...) } else { - command = exec.Command(podmanBinary, podmanOptions...) + runCmd = append(runCmd, podmanOptions...) + command = exec.Command(runCmd[0], runCmd[1:]...) } if env != nil { command.Env = env @@ -106,7 +110,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string // PodmanBase exec podman with default env. func (p *PodmanTest) PodmanBase(args []string, noEvents, noCache bool) *PodmanSession { - return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil) + return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil, nil) } // WaitForContainer waits on a started container @@ -393,7 +397,7 @@ func tagOutputToMap(imagesOutput []string) map[string]map[string]bool { return m } -// GetHostDistributionInfo returns a struct with its distribution name and version +// GetHostDistributionInfo returns a struct with its distribution Name and version func GetHostDistributionInfo() HostOS { f, err := os.Open(OSReleasePath) defer f.Close() @@ -487,13 +491,3 @@ func RandomString(n int) string { } return string(b) } - -//SkipIfInContainer skips a test if the test is run inside a container -func SkipIfInContainer(reason string) { - if len(reason) < 5 { - panic("SkipIfInContainer must specify a reason to skip") - } - if os.Getenv("TEST_ENVIRON") == "container" { - Skip("[container]: " + reason) - } -} |