From b3eaa08c5fc8164c62052aaf37776ee1813e1b47 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Mon, 18 Oct 2021 11:54:44 -0400 Subject: Generate Kube should not print default structs If podman uses Workdir="/" or the workdir specified in the image, it should not add it to the yaml. If Podman find environment variables in the image, they should not get added to the yaml. If the container or pod do not have changes to SELinux we should not print seLinuxOpt{} If the container or pod do not change any dns options the yaml should not have a dnsOption={} If the container is not privileged it should not have privileged=false in the yaml. Fixes: https://github.com/containers/podman/issues/11995 Signed-off-by: Daniel J Walsh --- test/e2e/generate_kube_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'test/e2e') diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 07515fe7b..21f4ad8fa 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] -- cgit v1.2.3-54-g00ecf From 3b67336b608efd72a0e7a023b85b1b3dc2dc4746 Mon Sep 17 00:00:00 2001 From: cdoern Date: Mon, 4 Oct 2021 10:18:01 -0400 Subject: Pod Rm Infra Improvements Made changes so that if the pod contains all exited containers and only infra is running, remove the pod. resolves #11713 Signed-off-by: cdoern --- docs/source/markdown/podman-pod-rm.1.md | 2 +- libpod/container_internal.go | 2 +- libpod/runtime_pod_linux.go | 3 +-- test/e2e/pod_rm_test.go | 17 +++++++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) (limited to 'test/e2e') diff --git a/docs/source/markdown/podman-pod-rm.1.md b/docs/source/markdown/podman-pod-rm.1.md index d185385b6..ce91dab5b 100644 --- a/docs/source/markdown/podman-pod-rm.1.md +++ b/docs/source/markdown/podman-pod-rm.1.md @@ -7,7 +7,7 @@ podman\-pod\-rm - Remove one or more stopped pods and containers **podman pod rm** [*options*] *pod* ## DESCRIPTION -**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. +**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. If all containers added by the user are in an exited state, the pod will be removed. ## OPTIONS diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 2ca49758d..d71179017 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -2100,7 +2100,7 @@ func (c *Container) checkReadyForRemoval() error { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in invalid state", c.ID()) } - if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) && !c.IsInfra() { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed without force", c.ID(), c.state.State.String()) } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index a90ea6125..3a6098de8 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -177,10 +177,9 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) if err != nil { return err } - numCtrs := len(ctrs) - // If the only container in the pod is the pause container, remove the pod and container unconditionally. + // If the only running container in the pod is the pause container, remove the pod and container unconditionally. pauseCtrID := p.state.InfraContainerID if numCtrs == 1 && ctrs[0].ID() == pauseCtrID { removeCtrs = true diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index c5d91d679..ac1f322ef 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -301,4 +301,21 @@ var _ = Describe("Podman pod rm", func() { Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) + + It("podman pod rm with exited containers", func() { + _, ec, podid := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--pod", podid, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--pod", podid, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + result := podmanTest.Podman([]string{"pod", "rm", podid}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + }) }) -- cgit v1.2.3-54-g00ecf From 47afa6d96218605f9ca5b78c5a420f38e4c84cf9 Mon Sep 17 00:00:00 2001 From: Hironori Shiina Date: Fri, 22 Oct 2021 21:04:48 -0400 Subject: Fix a few problems in 'podman logs --tail' with journald driver The following problems regarding `logs --tail` with the journald log driver are fixed: - One more line than a specified value is displayed. - '--tail 0' displays all lines while the other log drivers displays nothing. - Partial lines are not considered. - If the journald events backend is used and a container has exited, nothing is displayed. Integration tests that should have detected the bugs are also fixed. The tests are executed with json-file log driver three times without this fix. Signed-off-by: Hironori Shiina --- libpod/container_log_linux.go | 33 ++++++++++++++------- test/e2e/logs_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 11 deletions(-) (limited to 'test/e2e') diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index 562169ce2..4029d0af7 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -121,7 +121,24 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption }() tailQueue := []*logs.LogLine{} // needed for options.Tail - doTail := options.Tail > 0 + doTail := options.Tail >= 0 + doTailFunc := func() { + // Flush *once* we hit the end of the journal. + startIndex := int64(len(tailQueue)) + outputLines := int64(0) + for startIndex > 0 && outputLines < options.Tail { + startIndex-- + for startIndex > 0 && tailQueue[startIndex].Partial() { + startIndex-- + } + outputLines++ + } + for i := startIndex; i < int64(len(tailQueue)); i++ { + logChannel <- tailQueue[i] + } + tailQueue = nil + doTail = false + } lastReadCursor := "" for { select { @@ -152,16 +169,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption // Hit the end of the journal (so far?). if cursor == lastReadCursor { if doTail { - // Flush *once* we hit the end of the journal. - startIndex := int64(len(tailQueue)-1) - options.Tail - if startIndex < 0 { - startIndex = 0 - } - for i := startIndex; i < int64(len(tailQueue)); i++ { - logChannel <- tailQueue[i] - } - tailQueue = nil - doTail = false + doTailFunc() } // Unless we follow, quit. if !options.Follow { @@ -194,6 +202,9 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption return } if status == events.Exited { + if doTail { + doTailFunc() + } return } continue diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index 71d30f063..16e48da19 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() -- cgit v1.2.3-54-g00ecf From a67bf0f9213e38e74681134a9265c88121960808 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 26 Oct 2021 15:32:01 +0200 Subject: Slirp4netns with ipv6 set net.ipv6.conf.default.accept_dad=0 Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds. Since slirp4netns is run it is own namespace and not directly routed we can skip this to make the ipv6 address immediately available. We change the default to make sure the slirp tap interface gets the correct value assigned so DAD is disabled for it. Also make sure to change this value back to the original after slirp4netns is ready in case users rely on this sysctl. Fixes #11062 Signed-off-by: Paul Holzinger --- libpod/networking_slirp4netns.go | 39 +++++++++++++++++++++++++++++++++++++++ test/e2e/run_networking_test.go | 20 ++++++++++++++++++++ 2 files changed, 59 insertions(+) (limited to 'test/e2e') diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index c06d215e1..e24b54032 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/rootlessport" @@ -58,6 +59,8 @@ type slirp4netnsNetworkOptions struct { outboundAddr6 string } +const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad" + func checkSlirpFlags(path string) (*slirpFeatures, error) { cmd := exec.Command(path, "--help") out, err := cmd.CombinedOutput() @@ -297,6 +300,39 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { } cmd.Stdout = logFile cmd.Stderr = logFile + + var slirpReadyChan (chan struct{}) + + if netOptions.enableIPv6 { + slirpReadyChan = make(chan struct{}) + defer close(slirpReadyChan) + go func() { + err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error { + // Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds. + // Since slirp4netns is run it is own namespace and not directly routed + // we can skip this to make the ipv6 address immediately available. + // We change the default to make sure the slirp tap interface gets the + // correct value assigned so DAD is disabled for it + // Also make sure to change this value back to the original after slirp4netns + // is ready in case users rely on this sysctl. + orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl) + if err != nil { + return err + } + err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644) + if err != nil { + return err + } + // wait for slirp to finish setup + <-slirpReadyChan + return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644) + }) + if err != nil { + logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err) + } + }() + } + if err := cmd.Start(); err != nil { return errors.Wrapf(err, "failed to start slirp4netns process") } @@ -310,6 +346,9 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { return err } + if slirpReadyChan != nil { + slirpReadyChan <- struct{}{} + } // Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself _, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 84707732b..8d3b19b88 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) -- cgit v1.2.3-54-g00ecf From 2b85684ad1d2de0f412831e6225c176e0519bca3 Mon Sep 17 00:00:00 2001 From: Chris Evich Date: Tue, 26 Oct 2021 16:30:17 -0400 Subject: Fix systemd PID1 test Previously this test used an ad-hoc timeout mechanism to synchronize with output of the container ID. However, depending on runtime conditions this may not correctly correspond with complete startup of the systemd process. Consequently this test fails under some conditions with an error like: `System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down` Fix this by using the more appropriate `WaitContainerReady()` against output from system startup, close to finalization. In this way, the test status command cannot run until systemd is fully operational. Signed-off-by: Chris Evich --- test/e2e/systemd_test.go | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'test/e2e') 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"}) -- cgit v1.2.3-54-g00ecf From 246782133cc36e66db0f4facccd7dabcd76a92d5 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 27 Oct 2021 09:44:54 +0200 Subject: runtime: check for pause pid existence check that the pause pid exists before trying to move it to a separate scope. Closes: https://github.com/containers/podman/issues/12065 Signed-off-by: Giuseppe Scrivano --- libpod/runtime.go | 6 +++++- test/e2e/system_reset_test.go | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'test/e2e') diff --git a/libpod/runtime.go b/libpod/runtime.go index c22d87324..a99f55fb3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -541,7 +541,11 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { return err } if became { - utils.MovePauseProcessToScope(pausePid) + // Check if the pause process was created. If it was created, then + // move it to its own systemd scope. + if _, err = os.Stat(pausePid); err == nil { + utils.MovePauseProcessToScope(pausePid) + } os.Exit(ret) } } 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() -- cgit v1.2.3-54-g00ecf From 0ded340e6bbc7952678d93029726ec83992e5010 Mon Sep 17 00:00:00 2001 From: Praveen Kumar Date: Fri, 29 Oct 2021 11:10:26 -0700 Subject: Fix help message case for `podman version` This is a cosmetic change. The help message for `podman version` is in title case whereas all other command help messages are not in title case. This stands out as inconsistent when looking at the output of `podman help`. Signed-off-by: Praveen Kumar --- cmd/podman/system/version.go | 2 +- test/e2e/version_test.go | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'test/e2e') diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 4502b156c..f71af8e72 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -20,7 +20,7 @@ var ( versionCommand = &cobra.Command{ Use: "version [options]", Args: validate.NoArgs, - Short: "Display the Podman Version Information", + Short: "Display the Podman version information", RunE: version, ValidArgsFunction: completion.AutocompleteNone, } 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"), + ) + }) }) -- cgit v1.2.3-54-g00ecf From 18c322d1c05f776bf754c30091520c6d51a5cd4d Mon Sep 17 00:00:00 2001 From: Miloslav Trmač Date: Thu, 21 Oct 2021 22:11:37 +0200 Subject: Use INTEGRATION_ROOT instead of current directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should not change behavior, just to set a consistent precedent for code introduced in future commits. Signed-off-by: Miloslav Trmač --- test/e2e/trust_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'test/e2e') diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go index 7f97f280a..26b58737c 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,11 +39,7 @@ 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() -- cgit v1.2.3-54-g00ecf From df736396e193df99be1977543187c013cba91643 Mon Sep 17 00:00:00 2001 From: Miloslav Trmač Date: Thu, 21 Oct 2021 22:19:23 +0200 Subject: Tighten the expected output of the "podman image trust show" test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... to include all fields. Signed-off-by: Miloslav Trmač --- test/e2e/trust_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test/e2e') diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go index 26b58737c..efb00cad5 100644 --- a/test/e2e/trust_test.go +++ b/test/e2e/trust_test.go @@ -45,11 +45,11 @@ var _ = Describe("Podman trust", func() { 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() { -- cgit v1.2.3-54-g00ecf From df9e0fdcb055412d066cb240575cdf013ae281d8 Mon Sep 17 00:00:00 2001 From: Miloslav Trmač Date: Thu, 21 Oct 2021 22:32:34 +0200 Subject: Fix tests of podman image trust --raw and --json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead using the OS-wide system default policy, use the one in this repo, and adjust the expected results (as well as making the test stricter). Signed-off-by: Miloslav Trmač --- test/e2e/trust_test.go | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'test/e2e') diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go index efb00cad5..b591e1c02 100644 --- a/test/e2e/trust_test.go +++ b/test/e2e/trust_test.go @@ -73,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")) }) }) -- cgit v1.2.3-54-g00ecf From dd6551055a39eb338cdcc8691436aabc0aabf62d Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 2 Nov 2021 12:51:10 +0100 Subject: test: run --cgroups=split in new cgroup the --cgroups=split test changes the current cgroup as it creates a sub-cgroup. This can cause a race condition in tests that are reading the current cgroup. Closes: https://github.com/containers/podman/issues/11191 Signed-off-by: Giuseppe Scrivano --- test/e2e/common_test.go | 2 +- test/e2e/libpod_suite_remote_test.go | 16 +++++++++++++++- test/e2e/libpod_suite_test.go | 14 +++++++++++++- test/e2e/run_test.go | 4 ++-- test/utils/podmantest_test.go | 2 +- test/utils/utils.go | 16 ++++++++++------ 6 files changed, 42 insertions(+), 12 deletions(-) (limited to 'test/e2e') diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 20ed72c59..dbf51f4e4 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -685,7 +685,7 @@ func SkipIfContainerized(reason string) { // 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/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index 3115c246f..ad511cc9e 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -38,11 +38,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..6d2d3fee8 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/containers/podman/v3/pkg/rootless" ) func IsRemote() bool { @@ -23,9 +25,19 @@ 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 { + 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/run_test.go b/test/e2e/run_test.go index b6743f4b7..ca39989cd 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1322,13 +1322,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/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..d4d5e6e2f 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 -- cgit v1.2.3-54-g00ecf