From 69543d6c7aff9b31d8ad3f151f05267aa34e8e6c Mon Sep 17 00:00:00 2001 From: Aditya R Date: Wed, 2 Mar 2022 15:07:06 +0530 Subject: container: workdir resolution must consider symlink if explicitly configured While resolving `workdir` we mostly create a `workdir` when `stat` fails with `ENOENT` or `ErrNotExist` however following cases are not true when user explicitly specifies a `workdir` while `running` using `--workdir` which tells `podman` to only use workdir if its exists on the container. Following configuration is implicity set with other `run` mechanism like `podman play kube` Problem with explicit `--workdir` or similar implicit config in `podman play kube` is that currently podman ignores the fact that workdir can also be a `symlink` and actual `link` could be valid. Hence following commit ensures that in such scenarios when a `workdir` is not found and we cannot create a `workdir` podman must perform a check to ensure that if `workdir` is a `symlink` and `link` is resolved successfully and resolved link is present on the container then we return as it is. Docker performs a similar behviour. Signed-off-by: Aditya R --- libpod/container_internal_linux.go | 56 +++++++++++++++++++++++++++++++ test/e2e/build/workdir-symlink/Dockerfile | 5 +++ test/e2e/build_test.go | 13 +++++++ test/e2e/play_kube_test.go | 35 +++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 test/e2e/build/workdir-symlink/Dockerfile diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index afa351c17..f7c8c190f 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -173,6 +173,57 @@ func (c *Container) prepare() error { return nil } +// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks, +// and final resolved target is present either on volume, mount or inside of container +// otherwise it returns false. Following function is meant for internal use only and +// can change at any point of time. +func (c *Container) isWorkDirSymlink(resolvedPath string) bool { + // We cannot create workdir since explicit --workdir is + // set in config but workdir could also be a symlink. + // If its a symlink lets check if resolved link is present + // on the container or not. + + // If we can resolve symlink and resolved link is present on the container + // then return nil cause its a valid use-case. + + maxSymLinks := 0 + for { + // Linux only supports a chain of 40 links. + // Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13 + if maxSymLinks > 40 { + break + } + resolvedSymlink, err := os.Readlink(resolvedPath) + if err != nil { + // End sym-link resolution loop. + break + } + if resolvedSymlink != "" { + _, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink) + if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) { + // Resolved symlink exists on external volume or mount + return true + } + if err != nil { + // Could not resolve path so end sym-link resolution loop. + break + } + if resolvedSymlinkWorkdir != "" { + resolvedPath = resolvedSymlinkWorkdir + _, err := os.Stat(resolvedSymlinkWorkdir) + if err == nil { + // Symlink resolved successfully and resolved path exists on container, + // this is a valid use-case so return nil. + logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink) + return true + } + } + } + maxSymLinks++ + } + return false +} + // resolveWorkDir resolves the container's workdir and, depending on the // configuration, will create it, or error out if it does not exist. // Note that the container must be mounted before. @@ -205,6 +256,11 @@ func (c *Container) resolveWorkDir() error { // the path exists on the container. if err != nil { if os.IsNotExist(err) { + // If resolved Workdir path gets marked as a valid symlink, + // return nil cause this is valid use-case. + if c.isWorkDirSymlink(resolvedWorkdir) { + return nil + } return errors.Errorf("workdir %q does not exist on container %s", workdir, c.ID()) } // This might be a serious error (e.g., permission), so diff --git a/test/e2e/build/workdir-symlink/Dockerfile b/test/e2e/build/workdir-symlink/Dockerfile new file mode 100644 index 000000000..abc9b47ee --- /dev/null +++ b/test/e2e/build/workdir-symlink/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine +RUN mkdir /tmp/destination +RUN ln -s /tmp/destination /tmp/link +WORKDIR /tmp/link +CMD ["echo", "hello"] diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index a1c2f5e54..14fa12fa2 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -259,6 +259,19 @@ var _ = Describe("Podman build", func() { Expect(session.OutputToString()).NotTo(ContainSubstring("io.podman.annotations.seccomp")) }) + It("podman build where workdir is a symlink and run without creating new workdir", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/workdir-symlink/Dockerfile", "-t", "test-symlink", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--workdir", "/tmp/link", "test-symlink"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("hello")) + }) + It("podman build --http_proxy flag", func() { os.Setenv("http_proxy", "1.2.3.4") if IsRemote() { diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 48bc2b9da..f0f0c6dc4 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -38,6 +38,21 @@ spec: hostname: unknown ` +var workdirSymlinkPodYaml = ` +apiVersion: v1 +kind: Pod +metadata: + labels: + app: test-symlink + name: test-symlink +spec: + containers: + - image: test-symlink + name: test-symlink + resources: {} + restartPolicy: Never +` + var podnameEqualsContainerNameYaml = ` apiVersion: v1 kind: Pod @@ -1332,6 +1347,26 @@ var _ = Describe("Podman play kube", func() { Expect(sharednamespaces).To(ContainSubstring("pid")) }) + It("podman play kube should be able to run image where workdir is a symlink", func() { + session := podmanTest.Podman([]string{ + "build", "-f", "build/workdir-symlink/Dockerfile", "-t", "test-symlink", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + err := writeYaml(workdirSymlinkPodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + logs := podmanTest.Podman([]string{"pod", "logs", "-c", "test-symlink-test-symlink", "test-symlink"}) + logs.WaitWithDefaultTimeout() + Expect(logs).Should(Exit(0)) + Expect(logs.OutputToString()).To(ContainSubstring("hello")) + }) + It("podman play kube should not rename pod if container in pod has same name", func() { err := writeYaml(podnameEqualsContainerNameYaml, kubeYaml) Expect(err).To(BeNil()) -- cgit v1.2.3-54-g00ecf From f002a0c8898bf187d5071b86ee0d1c5e07cf6416 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 3 Mar 2022 13:23:07 +0100 Subject: libpod: pods do not use cgroups if --cgroups=disabled do not attempt to use cgroups with pods if the cgroups are disabled. A similar check is already in place for containers. Closes: https://github.com/containers/podman/issues/13411 Signed-off-by: Giuseppe Scrivano --- libpod/runtime_pod_linux.go | 71 +++++++++++++++++++++------------------- test/e2e/containers_conf_test.go | 5 +++ 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 155ad5c2d..230491c1a 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package libpod @@ -59,50 +60,52 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option pod.valid = true // Check Cgroup parent sanity, and set it if it was not set - switch r.config.Engine.CgroupManager { - case config.CgroupfsCgroupsManager: - canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent) - if canUseCgroup { + if r.config.Cgroups() != "disabled" { + switch r.config.Engine.CgroupManager { + case config.CgroupfsCgroupsManager: + canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent) + if canUseCgroup { + if pod.config.CgroupParent == "" { + pod.config.CgroupParent = CgroupfsDefaultCgroupParent + } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") + } + // If we are set to use pod cgroups, set the cgroup parent that + // all containers in the pod will share + // No need to create it with cgroupfs - the first container to + // launch should do it for us + if pod.config.UsePodCgroup { + pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + if p.InfraContainerSpec != nil { + p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath + } + } + } + case config.SystemdCgroupsManager: if pod.config.CgroupParent == "" { - pod.config.CgroupParent = CgroupfsDefaultCgroupParent - } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs") + if rootless.IsRootless() { + pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent + } else { + pod.config.CgroupParent = SystemdDefaultCgroupParent + } + } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { + return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") } // If we are set to use pod cgroups, set the cgroup parent that // all containers in the pod will share - // No need to create it with cgroupfs - the first container to - // launch should do it for us if pod.config.UsePodCgroup { - pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID()) + cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID())) + if err != nil { + return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID()) + } + pod.state.CgroupPath = cgroupPath if p.InfraContainerSpec != nil { p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath } } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager) } - case config.SystemdCgroupsManager: - if pod.config.CgroupParent == "" { - if rootless.IsRootless() { - pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent - } else { - pod.config.CgroupParent = SystemdDefaultCgroupParent - } - } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") { - return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups") - } - // If we are set to use pod cgroups, set the cgroup parent that - // all containers in the pod will share - if pod.config.UsePodCgroup { - cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID())) - if err != nil { - return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID()) - } - pod.state.CgroupPath = cgroupPath - if p.InfraContainerSpec != nil { - p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath - } - } - default: - return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager) } if pod.config.UsePodCgroup { diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index bfed01854..09cd68042 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -562,6 +562,11 @@ var _ = Describe("Verify podman containers.conf usage", func() { inspect = podmanTest.Podman([]string{"inspect", "--format", "{{ .HostConfig.Cgroups }}", result.OutputToString()}) inspect.WaitWithDefaultTimeout() Expect(inspect.OutputToString()).To(Equal("disabled")) + + // Check we can also create a pod when cgroups=disabled + result = podmanTest.Podman([]string{"pod", "create"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) }) It("podman containers.conf runtime", func() { -- cgit v1.2.3-54-g00ecf From 5aa6b6919874815a12d28aaf1262bb3ec1de3be4 Mon Sep 17 00:00:00 2001 From: Urvashi Mohnani Date: Fri, 4 Mar 2022 15:01:46 -0500 Subject: Throw an error if kube yaml has duplicate ctr names Error out if the kube yaml passed to play kube has more than one container or init container with the same name. Signed-off-by: Urvashi Mohnani --- pkg/domain/infra/abi/play.go | 11 +++++++++++ test/e2e/play_kube_test.go | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 308a1d0ee..9adf5eb12 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -356,7 +356,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } + ctrNames := make(map[string]string) for _, initCtr := range podYAML.Spec.InitContainers { + // Error out if same name is used for more than one container + if _, ok := ctrNames[initCtr.Name]; ok { + return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, initCtr.Name) + } + ctrNames[initCtr.Name] = "" // Init containers cannot have either of lifecycle, livenessProbe, readinessProbe, or startupProbe set if initCtr.Lifecycle != nil || initCtr.LivenessProbe != nil || initCtr.ReadinessProbe != nil || initCtr.StartupProbe != nil { return nil, errors.Errorf("cannot create an init container that has either of lifecycle, livenessProbe, readinessProbe, or startupProbe set") @@ -400,6 +406,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } for _, container := range podYAML.Spec.Containers { if !strings.Contains("infra", container.Name) { + // Error out if the same name is used for more than one container + if _, ok := ctrNames[container.Name]; ok { + return nil, errors.Errorf("the pod %q is invalid; duplicate container name %q detected", podName, container.Name) + } + ctrNames[container.Name] = "" pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options) if err != nil { return nil, err diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index f0f0c6dc4..dbd5a044a 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1888,6 +1888,26 @@ var _ = Describe("Podman play kube", func() { Expect(kube).Should(Exit(0)) }) + It("podman play kube test duplicate container name", func() { + p := getPod(withCtr(getCtr(withName("testctr"), withCmd([]string{"echo", "hello"}))), withCtr(getCtr(withName("testctr"), withCmd([]string{"echo", "world"})))) + + err := generateKubeYaml("pod", p, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).To(ExitWithError()) + + p = getPod(withPodInitCtr(getCtr(withImage(ALPINE), withCmd([]string{"echo", "hello"}), withInitCtr(), withName("initctr"))), withCtr(getCtr(withImage(ALPINE), withName("initctr"), withCmd([]string{"top"})))) + + err = generateKubeYaml("pod", p, kubeYaml) + Expect(err).To(BeNil()) + + kube = podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).To(ExitWithError()) + }) + It("podman play kube test hostname", func() { pod := getPod() err := generateKubeYaml("pod", pod, kubeYaml) -- cgit v1.2.3-54-g00ecf From 6d8d8fb7186628bd6781ee37439752e234319b3e Mon Sep 17 00:00:00 2001 From: Thibault Gagnaux Date: Sun, 6 Mar 2022 15:32:47 +0100 Subject: Fixes: #13301 ("machine rm removes the mounted socket file on macos") [NO NEW TESTS NEEDED] Signed-off-by: Thibault Gagnaux --- pkg/machine/qemu/machine.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index faf01b9ad..d30e51215 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -697,6 +697,11 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun if !opts.SaveImage { files = append(files, v.ImagePath) } + socketPath, err := v.getForwardSocketPath() + if err != nil { + logrus.Error(err) + } + files = append(files, socketPath) files = append(files, v.archRemovalFiles()...) if err := machine.RemoveConnection(v.Name); err != nil { -- cgit v1.2.3-54-g00ecf From e308213a2310c0447c4adbdc635bc75f6b5404db Mon Sep 17 00:00:00 2001 From: Ashley Cui Date: Tue, 8 Mar 2022 09:45:48 -0500 Subject: Move secret-verify-leak containerfile into its own Directory Secret-verify-leak is causing flakes, when running in parallel tests. This is because remote secrets are copied into the context directory to send to the API server, and secret-verify-leak is doing a COPY * and then checking if the temporary secret file ends up in the container or not. Since all the temporary files are prefixed with "podman-build-secret", this test checks if podman-build-secret is in the image. However, when run in parallel with other tests, other temporary podman-build-secrets might be in the context dir. Moving secret-verify-leak into its own directory makes sure that the context dir is used only by this one test. Also renamed Dockerfile -> Containerfile and cleaned up unused Containerfiles. Signed-off-by: Ashley Cui --- test/e2e/build/Containerfile.with-multiple-secret | 3 +++ test/e2e/build/Containerfile.with-secret | 2 ++ test/e2e/build/Dockerfile.test-cp-root-dir | 2 -- test/e2e/build/Dockerfile.with-multiple-secret | 3 --- test/e2e/build/Dockerfile.with-secret | 2 -- test/e2e/build/Dockerfile.with-secret-verify-leak | 3 --- .../build/secret-verify-leak/Containerfile.with-secret-verify-leak | 3 +++ test/e2e/build_test.go | 6 +++--- 8 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 test/e2e/build/Containerfile.with-multiple-secret create mode 100644 test/e2e/build/Containerfile.with-secret delete mode 100644 test/e2e/build/Dockerfile.test-cp-root-dir delete mode 100644 test/e2e/build/Dockerfile.with-multiple-secret delete mode 100644 test/e2e/build/Dockerfile.with-secret delete mode 100644 test/e2e/build/Dockerfile.with-secret-verify-leak create mode 100644 test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak diff --git a/test/e2e/build/Containerfile.with-multiple-secret b/test/e2e/build/Containerfile.with-multiple-secret new file mode 100644 index 000000000..f3478914f --- /dev/null +++ b/test/e2e/build/Containerfile.with-multiple-secret @@ -0,0 +1,3 @@ +FROM alpine +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret +RUN --mount=type=secret,id=mysecret2 cat /run/secrets/mysecret2 diff --git a/test/e2e/build/Containerfile.with-secret b/test/e2e/build/Containerfile.with-secret new file mode 100644 index 000000000..920663a92 --- /dev/null +++ b/test/e2e/build/Containerfile.with-secret @@ -0,0 +1,2 @@ +FROM alpine +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build/Dockerfile.test-cp-root-dir b/test/e2e/build/Dockerfile.test-cp-root-dir deleted file mode 100644 index 9f7de7c32..000000000 --- a/test/e2e/build/Dockerfile.test-cp-root-dir +++ /dev/null @@ -1,2 +0,0 @@ -FROM scratch -COPY Dockerfile.test-cp-root-dir / diff --git a/test/e2e/build/Dockerfile.with-multiple-secret b/test/e2e/build/Dockerfile.with-multiple-secret deleted file mode 100644 index f3478914f..000000000 --- a/test/e2e/build/Dockerfile.with-multiple-secret +++ /dev/null @@ -1,3 +0,0 @@ -FROM alpine -RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret -RUN --mount=type=secret,id=mysecret2 cat /run/secrets/mysecret2 diff --git a/test/e2e/build/Dockerfile.with-secret b/test/e2e/build/Dockerfile.with-secret deleted file mode 100644 index 920663a92..000000000 --- a/test/e2e/build/Dockerfile.with-secret +++ /dev/null @@ -1,2 +0,0 @@ -FROM alpine -RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build/Dockerfile.with-secret-verify-leak b/test/e2e/build/Dockerfile.with-secret-verify-leak deleted file mode 100644 index 0957ac6a6..000000000 --- a/test/e2e/build/Dockerfile.with-secret-verify-leak +++ /dev/null @@ -1,3 +0,0 @@ -FROM alpine -COPY * / -RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak b/test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak new file mode 100644 index 000000000..0957ac6a6 --- /dev/null +++ b/test/e2e/build/secret-verify-leak/Containerfile.with-secret-verify-leak @@ -0,0 +1,3 @@ +FROM alpine +COPY * / +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 14fa12fa2..c5903f037 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -60,7 +60,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with a secret from file", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret", "-t", "secret-test", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.with-secret", "-t", "secret-test", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) @@ -71,7 +71,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with multiple secrets from files", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/Containerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) @@ -83,7 +83,7 @@ var _ = Describe("Podman build", func() { }) It("podman build with a secret from file and verify if secret file is not leaked into image", func() { - session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session := podmanTest.Podman([]string{"build", "-f", "build/secret-verify-leak/Containerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("somesecret")) -- cgit v1.2.3-54-g00ecf From 37a9746332c88474965d0f1f3fb15ce1d56d06bb Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 8 Mar 2022 11:23:27 -0700 Subject: Skip flaky pprof tests pprof tests are way too flaky, and are causing problems for community contributors who don't have privs to press Re-run. There has been no activity or interest in fixing the bug, and it's not something I can fix. So, just disable the test. Signed-off-by: Ed Santiago --- test/e2e/system_service_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/e2e/system_service_test.go b/test/e2e/system_service_test.go index dcf5e03b2..2bc7756d6 100644 --- a/test/e2e/system_service_test.go +++ b/test/e2e/system_service_test.go @@ -58,6 +58,7 @@ var _ = Describe("podman system service", func() { const magicComment = "pprof service listening on" It("are available", func() { + Skip("FIXME: Test is too flaky (#12624)") SkipIfRemote("service subcommand not supported remotely") address := url.URL{ @@ -97,6 +98,7 @@ var _ = Describe("podman system service", func() { }) It("are not available", func() { + Skip("FIXME: Test is too flaky (#12624)") SkipIfRemote("service subcommand not supported remotely") address := url.URL{ -- cgit v1.2.3-54-g00ecf From 32802d6a6d65ec4d2416b23c53d1b34096252bbb Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 8 Mar 2022 08:57:21 -0500 Subject: Fix handling of tmpfs-mode for tmpfs creation in compat mode The permissions on disk were wrong since we were not converting to octal. Fixes: https://github.com/containers/podman/issues/13108 [NO NEW TESTS NEEDED] Since we don't currently test using the docker client Signed-off-by: Daniel J Walsh --- cmd/podman/common/create_opts.go | 2 +- test/apiv2/20-containers.at | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index a4f94616c..b110b3d85 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -117,7 +117,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c case mount.TypeTmpfs: if m.TmpfsOptions != nil { addField(&builder, "tmpfs-size", strconv.FormatInt(m.TmpfsOptions.SizeBytes, 10)) - addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 10)) + addField(&builder, "tmpfs-mode", strconv.FormatUint(uint64(m.TmpfsOptions.Mode), 8)) } case mount.TypeVolume: // All current VolumeOpts are handled above diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index cc5eda88e..49f8fb3fc 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -394,7 +394,8 @@ t GET containers/$cid/json 200 \ .Config.Healthcheck.Retries=3 # compat api: Test for mount options support -payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":755}}]}' +# Sigh, JSON can't handle octal. 0755(octal) = 493(decimal) +payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":493}}]}' t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\} cid=$(jq -r '.Id' <<<"$output") t GET containers/$cid/json 200 \ -- cgit v1.2.3-54-g00ecf From 4a0d74495eb60bcc9aedc806180ade633b343599 Mon Sep 17 00:00:00 2001 From: "Jason T. Greene" Date: Sat, 12 Mar 2022 02:36:44 -0600 Subject: Fixes TTY & resizing on Mac and Windows Signed-off-by: Jason T. Greene --- pkg/bindings/containers/attach.go | 23 ++++---- pkg/bindings/containers/term_unix.go | 25 +++++++++ pkg/bindings/containers/term_windows.go | 69 +++++++++++++++++++++++ pkg/signal/signal_unix.go | 99 +++++++++++++++++++++++++++++++++ pkg/signal/signal_unsupported.go | 2 +- 5 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 pkg/bindings/containers/term_unix.go create mode 100644 pkg/bindings/containers/term_windows.go create mode 100644 pkg/signal/signal_unix.go diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index c6d434c87..f410606e4 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -10,14 +10,12 @@ import ( "net/http" "net/url" "os" - "os/signal" "reflect" "strconv" "time" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/bindings" - sig "github.com/containers/podman/v4/pkg/signal" "github.com/containers/podman/v4/utils" "github.com/moby/term" "github.com/pkg/errors" @@ -94,7 +92,8 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri // Unless all requirements are met, don't use "stdin" is a terminal file, ok := stdin.(*os.File) - needTTY := ok && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty + outFile, outOk := stdout.(*os.File) + needTTY := ok && outOk && terminal.IsTerminal(int(file.Fd())) && ctnr.Config.Tty if needTTY { state, err := setRawTerminal(file) if err != nil { @@ -142,11 +141,10 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri if needTTY { winChange := make(chan os.Signal, 1) - signal.Notify(winChange, sig.SIGWINCH) winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - - attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file) + notifyWinChange(winCtx, winChange, file, outFile) + attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file, outFile) } // If we are attaching around a start, we need to "signal" @@ -345,9 +343,9 @@ func (f *rawFormatter) Format(entry *logrus.Entry) ([]byte, error) { // This is intended to not be run as a goroutine, handling resizing for a container // or exec session. It will call resize once and then starts a goroutine which calls resize on winChange -func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File) { +func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, isExec bool, id string, file *os.File, outFile *os.File) { resize := func() { - w, h, err := terminal.GetSize(int(file.Fd())) + w, h, err := getTermSize(file, outFile) if err != nil { logrus.Warnf("Failed to obtain TTY size: %v", err) } @@ -379,7 +377,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i // Configure the given terminal for raw mode func setRawTerminal(file *os.File) (*terminal.State, error) { - state, err := terminal.MakeRaw(int(file.Fd())) + state, err := makeRawTerm(file) if err != nil { return nil, err } @@ -402,6 +400,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar // TODO: Make this configurable (can't use streams' InputStream as it's // buffered) terminalFile := os.Stdin + terminalOutFile := os.Stdout logrus.Debugf("Starting & Attaching to exec session ID %q", sessionID) @@ -447,7 +446,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar } logrus.SetFormatter(&logrus.TextFormatter{}) }() - w, h, err := terminal.GetSize(int(terminalFile.Fd())) + w, h, err := getTermSize(terminalFile, terminalOutFile) if err != nil { logrus.Warnf("Failed to obtain TTY size: %v", err) } @@ -490,11 +489,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar if needTTY { winChange := make(chan os.Signal, 1) - signal.Notify(winChange, sig.SIGWINCH) winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) + notifyWinChange(winCtx, winChange, terminalFile, terminalOutFile) + attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile, terminalOutFile) } if options.GetAttachInput() { diff --git a/pkg/bindings/containers/term_unix.go b/pkg/bindings/containers/term_unix.go new file mode 100644 index 000000000..2c976393f --- /dev/null +++ b/pkg/bindings/containers/term_unix.go @@ -0,0 +1,25 @@ +//go:build !windows +// +build !windows + +package containers + +import ( + "context" + "os" + "os/signal" + + sig "github.com/containers/podman/v4/pkg/signal" + "golang.org/x/crypto/ssh/terminal" +) + +func makeRawTerm(stdin *os.File) (*terminal.State, error) { + return terminal.MakeRaw(int(stdin.Fd())) +} + +func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) { + signal.Notify(winChange, sig.SIGWINCH) +} + +func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) { + return terminal.GetSize(int(stdin.Fd())) +} diff --git a/pkg/bindings/containers/term_windows.go b/pkg/bindings/containers/term_windows.go new file mode 100644 index 000000000..11d4bd50d --- /dev/null +++ b/pkg/bindings/containers/term_windows.go @@ -0,0 +1,69 @@ +package containers + +import ( + "context" + "os" + "time" + + sig "github.com/containers/podman/v4/pkg/signal" + "golang.org/x/crypto/ssh/terminal" + "golang.org/x/sys/windows" +) + +func makeRawTerm(stdin *os.File) (*terminal.State, error) { + state, err := terminal.MakeRaw(int(stdin.Fd())) + if err != nil { + return nil, err + } + + // Attempt VT if supported (recent versions of Windows 10+) + var raw uint32 + handle := windows.Handle(stdin.Fd()) + if err := windows.GetConsoleMode(handle, &raw); err != nil { + return nil, err + } + + tryVT := raw | windows.ENABLE_VIRTUAL_TERMINAL_INPUT + + if err := windows.SetConsoleMode(handle, tryVT); err != nil { + if err := windows.SetConsoleMode(handle, raw); err != nil { + return nil, err + } + } + + return state, nil +} + +func notifyWinChange(ctx context.Context, winChange chan os.Signal, stdin *os.File, stdout *os.File) { + // Simulate WINCH with polling + go func() { + var lastW int + var lastH int + + d := time.Millisecond * 250 + timer := time.NewTimer(d) + defer timer.Stop() + for ; ; timer.Reset(d) { + select { + case <-ctx.Done(): + return + case <-timer.C: + break + } + + w, h, err := terminal.GetSize(int(stdout.Fd())) + if err != nil { + continue + } + if w != lastW || h != lastH { + winChange <- sig.SIGWINCH + lastW, lastH = w, h + } + } + }() + +} + +func getTermSize(stdin *os.File, stdout *os.File) (width, height int, err error) { + return terminal.GetSize(int(stdout.Fd())) +} diff --git a/pkg/signal/signal_unix.go b/pkg/signal/signal_unix.go new file mode 100644 index 000000000..f35abddc1 --- /dev/null +++ b/pkg/signal/signal_unix.go @@ -0,0 +1,99 @@ +// +build aix darwin dragonfly freebsd netbsd openbsd solaris zos + +// Signal handling for Linux only. +package signal + +import ( + "os" + "syscall" +) + +const ( + sigrtmin = 34 + sigrtmax = 64 + + SIGWINCH = syscall.SIGWINCH +) + +// signalMap is a map of Linux signals. +// These constants are sourced from the Linux version of golang.org/x/sys/unix +// (I don't see much risk of this changing). +// This should work as long as Podman only runs containers on Linux, which seems +// a safe assumption for now. +var signalMap = map[string]syscall.Signal{ + "ABRT": syscall.Signal(0x6), + "ALRM": syscall.Signal(0xe), + "BUS": syscall.Signal(0x7), + "CHLD": syscall.Signal(0x11), + "CLD": syscall.Signal(0x11), + "CONT": syscall.Signal(0x12), + "FPE": syscall.Signal(0x8), + "HUP": syscall.Signal(0x1), + "ILL": syscall.Signal(0x4), + "INT": syscall.Signal(0x2), + "IO": syscall.Signal(0x1d), + "IOT": syscall.Signal(0x6), + "KILL": syscall.Signal(0x9), + "PIPE": syscall.Signal(0xd), + "POLL": syscall.Signal(0x1d), + "PROF": syscall.Signal(0x1b), + "PWR": syscall.Signal(0x1e), + "QUIT": syscall.Signal(0x3), + "SEGV": syscall.Signal(0xb), + "STKFLT": syscall.Signal(0x10), + "STOP": syscall.Signal(0x13), + "SYS": syscall.Signal(0x1f), + "TERM": syscall.Signal(0xf), + "TRAP": syscall.Signal(0x5), + "TSTP": syscall.Signal(0x14), + "TTIN": syscall.Signal(0x15), + "TTOU": syscall.Signal(0x16), + "URG": syscall.Signal(0x17), + "USR1": syscall.Signal(0xa), + "USR2": syscall.Signal(0xc), + "VTALRM": syscall.Signal(0x1a), + "WINCH": syscall.Signal(0x1c), + "XCPU": syscall.Signal(0x18), + "XFSZ": syscall.Signal(0x19), + "RTMIN": sigrtmin, + "RTMIN+1": sigrtmin + 1, + "RTMIN+2": sigrtmin + 2, + "RTMIN+3": sigrtmin + 3, + "RTMIN+4": sigrtmin + 4, + "RTMIN+5": sigrtmin + 5, + "RTMIN+6": sigrtmin + 6, + "RTMIN+7": sigrtmin + 7, + "RTMIN+8": sigrtmin + 8, + "RTMIN+9": sigrtmin + 9, + "RTMIN+10": sigrtmin + 10, + "RTMIN+11": sigrtmin + 11, + "RTMIN+12": sigrtmin + 12, + "RTMIN+13": sigrtmin + 13, + "RTMIN+14": sigrtmin + 14, + "RTMIN+15": sigrtmin + 15, + "RTMAX-14": sigrtmax - 14, + "RTMAX-13": sigrtmax - 13, + "RTMAX-12": sigrtmax - 12, + "RTMAX-11": sigrtmax - 11, + "RTMAX-10": sigrtmax - 10, + "RTMAX-9": sigrtmax - 9, + "RTMAX-8": sigrtmax - 8, + "RTMAX-7": sigrtmax - 7, + "RTMAX-6": sigrtmax - 6, + "RTMAX-5": sigrtmax - 5, + "RTMAX-4": sigrtmax - 4, + "RTMAX-3": sigrtmax - 3, + "RTMAX-2": sigrtmax - 2, + "RTMAX-1": sigrtmax - 1, + "RTMAX": sigrtmax, +} + +// CatchAll catches all signals and relays them to the specified channel. +func CatchAll(sigc chan os.Signal) { + panic("Unsupported on non-linux platforms") +} + +// StopCatch stops catching the signals and closes the specified channel. +func StopCatch(sigc chan os.Signal) { + panic("Unsupported on non-linux platforms") +} diff --git a/pkg/signal/signal_unsupported.go b/pkg/signal/signal_unsupported.go index 9d1733c02..45946f142 100644 --- a/pkg/signal/signal_unsupported.go +++ b/pkg/signal/signal_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos // Signal handling for Linux only. package signal -- cgit v1.2.3-54-g00ecf From e424c64b471b79f67c4a0cf2deb9e7e64289fcab Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 11 Mar 2022 16:33:22 +0100 Subject: slirp: fix setup on ipv6 disabled systems When enable_ipv6=true is set for slirp4netns (default since podman v4), we will try to set the accept sysctl. This sysctl will not exist on systems that have ipv6 disabled. In this case we should not error and just ignore the extra ipv6 setup. Also the current logic to wait for the slirp4 setup was kinda broken, it did not actually wait until the sysctl was set before starting slirp. This should now be fixed by using two `sync.WaitGroup`s. [NO NEW TESTS NEEDED] Fixes #13388 Signed-off-by: Paul Holzinger --- libpod/networking_slirp4netns.go | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index 690f0c1fa..cc44f78f7 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "syscall" "time" @@ -302,11 +303,15 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error { cmd.Stdout = logFile cmd.Stderr = logFile - var slirpReadyChan (chan struct{}) - + var slirpReadyWg, netnsReadyWg *sync.WaitGroup if netOptions.enableIPv6 { - slirpReadyChan = make(chan struct{}) - defer close(slirpReadyChan) + // use two wait groups to make sure we set the sysctl before + // starting slirp and reset it only after slirp is ready + slirpReadyWg = &sync.WaitGroup{} + netnsReadyWg = &sync.WaitGroup{} + slirpReadyWg.Add(1) + netnsReadyWg.Add(1) + go func() { err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error { // Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds. @@ -318,23 +323,37 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error { // is ready in case users rely on this sysctl. orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl) if err != nil { + netnsReadyWg.Done() + // on ipv6 disabled systems the sysctl does not exists + // so we should not error + if errors.Is(err, os.ErrNotExist) { + return nil + } return err } err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644) + netnsReadyWg.Done() if err != nil { return err } - // wait for slirp to finish setup - <-slirpReadyChan + + // wait until slirp4nets is ready before reseting this value + slirpReadyWg.Wait() return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644) }) if err != nil { logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err) } }() + + // wait until we set the sysctl + netnsReadyWg.Wait() } if err := cmd.Start(); err != nil { + if netOptions.enableIPv6 { + slirpReadyWg.Done() + } return errors.Wrapf(err, "failed to start slirp4netns process") } defer func() { @@ -344,11 +363,12 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error { } }() - if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { - return err + err = waitForSync(syncR, cmd, logFile, 1*time.Second) + if netOptions.enableIPv6 { + slirpReadyWg.Done() } - if slirpReadyChan != nil { - slirpReadyChan <- struct{}{} + if err != nil { + return err } // Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself -- cgit v1.2.3-54-g00ecf From b7b8fedea9569076809557c99683bd0f0c79c7f0 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 15 Mar 2022 18:48:32 +0100 Subject: fix empty newline in version output When podman is build without git commit information it will print a empty newline instead. This is undesirable and a regression introduced in commit 7d22cc88ef38. To test build podman with `go build -mod=vendor -o bin/podman ./cmd/podman` and check the output of bin/podman version with and without this commit. [NO NEW TESTS NEEDED] Signed-off-by: Paul Holzinger --- cmd/podman/system/version.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go index 6239482b5..9fb4a966a 100644 --- a/cmd/podman/system/version.go +++ b/cmd/podman/system/version.go @@ -89,9 +89,7 @@ Client:\tPodman Engine Version:\t{{.Version}} API Version:\t{{.APIVersion}} Go Version:\t{{.GoVersion}} -{{if .GitCommit -}} - Git Commit:\t{{.GitCommit}} -{{- end}} +{{if .GitCommit -}}Git Commit:\t{{.GitCommit}}\n{{end -}} Built:\t{{.BuiltTime}} OS/Arch:\t{{.OsArch}} {{- end}} @@ -102,9 +100,7 @@ Server:\tPodman Engine Version:\t{{.Version}} API Version:\t{{.APIVersion}} Go Version:\t{{.GoVersion}} -{{if .GitCommit -}} - Git Commit:\t{{.GitCommit}} -{{- end}} +{{if .GitCommit -}}Git Commit:\t{{.GitCommit}}\n{{end -}} Built:\t{{.BuiltTime}} OS/Arch:\t{{.OsArch}} {{- end}}{{- end}} -- cgit v1.2.3-54-g00ecf From 2f76581cf9713ffef68905e29ce43730c585fabb Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Tue, 15 Mar 2022 11:53:04 -0700 Subject: [CI:DOCS]: Mention netavark limitations for macvlan/ipvlan drivers The example is also improved to add the --subnet option, this option is required with netavark, else you get: Error: macvlan driver needs at least one subnet specified, DHCP is not supported with netavark Signed-off-by: Clayton Craft --- docs/source/markdown/podman-network-create.1.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md index 357a06cea..5a3224501 100644 --- a/docs/source/markdown/podman-network-create.1.md +++ b/docs/source/markdown/podman-network-create.1.md @@ -28,6 +28,10 @@ resolution. Driver to manage the network. Currently `bridge`, `macvlan` and `ipvlan` are supported. Defaults to `bridge`. As rootless the `macvlan` and `ipvlan` driver have no access to the host network interfaces because rootless networking requires a separate network namespace. +Special considerations for the *netavark* backend: +- The `macvlan` driver requires the `--subnet` option, DHCP is currently not supported. +- The `ipvlan` driver is not currently supported. + #### **--opt**=*option*, **-o** Set driver specific options. @@ -114,7 +118,7 @@ podman4 Create a Macvlan based network using the host interface eth0. Macvlan networks can only be used as root. ``` -# podman network create -d macvlan -o parent=eth0 newnet +# podman network create -d macvlan -o parent=eth0 --subnet 192.5.0.0/16 newnet newnet ``` -- cgit v1.2.3-54-g00ecf From e01d9680ce5de8153ad44611f6444a353a5b26da Mon Sep 17 00:00:00 2001 From: Nirmal Patel Date: Sat, 19 Feb 2022 17:55:23 -0500 Subject: Separator is no longer prepended when prefix is empty on podman generate systemd When podman generate systemd is invoked, it previously did not check if container-prefix or pod-prefix are empty. When these are empty, the file name starts with the separator, which is hyphen by default. This results in files like '-containername.service'. The code now checks if these prefixes are empty. If they are, the filename no longer adds a separator. Instead, it uses name or ID of the container or pod. Closes #13272 Signed-off-by: Nirmal Patel --- pkg/api/handlers/libpod/generate.go | 34 ++++++++++++++------- pkg/systemd/generate/common.go | 14 +++++++++ pkg/systemd/generate/containers.go | 4 ++- pkg/systemd/generate/containers_test.go | 42 ++++++++++++++++++++++++++ pkg/systemd/generate/pods.go | 3 +- pkg/systemd/generate/pods_test.go | 44 ++++++++++++++++++++++++++++ test/e2e/generate_systemd_test.go | 52 +++++++++++++++++++++++++++++++++ 7 files changed, 180 insertions(+), 13 deletions(-) diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 7e08dd4a8..28785b00d 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -25,18 +25,15 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { RestartSec uint `schema:"restartSec"` StopTimeout uint `schema:"stopTimeout"` StartTimeout uint `schema:"startTimeout"` - ContainerPrefix string `schema:"containerPrefix"` - PodPrefix string `schema:"podPrefix"` - Separator string `schema:"separator"` + ContainerPrefix *string `schema:"containerPrefix"` + PodPrefix *string `schema:"podPrefix"` + Separator *string `schema:"separator"` Wants []string `schema:"wants"` After []string `schema:"after"` Requires []string `schema:"requires"` }{ - StartTimeout: 0, - StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, - ContainerPrefix: "container", - PodPrefix: "pod", - Separator: "-", + StartTimeout: 0, + StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -44,6 +41,21 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { return } + var ContainerPrefix = "container" + if query.ContainerPrefix != nil { + ContainerPrefix = *query.ContainerPrefix + } + + var PodPrefix = "pod" + if query.PodPrefix != nil { + PodPrefix = *query.PodPrefix + } + + var Separator = "-" + if query.Separator != nil { + Separator = *query.Separator + } + containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.GenerateSystemdOptions{ Name: query.Name, @@ -53,9 +65,9 @@ func GenerateSystemd(w http.ResponseWriter, r *http.Request) { RestartPolicy: query.RestartPolicy, StartTimeout: &query.StartTimeout, StopTimeout: &query.StopTimeout, - ContainerPrefix: query.ContainerPrefix, - PodPrefix: query.PodPrefix, - Separator: query.Separator, + ContainerPrefix: ContainerPrefix, + PodPrefix: PodPrefix, + Separator: Separator, RestartSec: &query.RestartSec, Wants: query.Wants, After: query.After, diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index a6f8f7cd4..e53d37897 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -137,3 +137,17 @@ func removeArg(arg string, args []string) []string { } return newArgs } + +// This function is used to get name of systemd service from prefix, separator, and +// container/pod name. If prefix is empty, the service name does not include the +// separator. This is to avoid a situation where service name starts with the separator +// which is usually hyphen. +func getServiceName(prefix string, separator string, name string) string { + serviceName := name + + if len(prefix) > 0 { + serviceName = prefix + separator + name + } + + return serviceName +} diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index ea829c810..c01bb1baf 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -236,7 +236,9 @@ func containerServiceName(ctr *libpod.Container, options entities.GenerateSystem if options.Name { nameOrID = ctr.Name() } - serviceName := fmt.Sprintf("%s%s%s", options.ContainerPrefix, options.Separator, nameOrID) + + serviceName := getServiceName(options.ContainerPrefix, options.Separator, nameOrID) + return nameOrID, serviceName } diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 2f653a4b9..b9bf7c317 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -87,6 +87,30 @@ ExecStopPost=/usr/bin/podman stop -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking +[Install] +WantedBy=default.target +` + + goodNameEmptyContainerPrefix := `# foobar.service +# autogenerated by Podman CI + +[Unit] +Description=Podman foobar.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStart=/usr/bin/podman start foobar +ExecStop=/usr/bin/podman stop -t 10 foobar +ExecStopPost=/usr/bin/podman stop -t 10 foobar +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +Type=forking + [Install] WantedBy=default.target ` @@ -1206,6 +1230,24 @@ WantedBy=default.target false, true, }, + {"good with name and empty container-prefix", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "foobar", + ContainerNameOrID: "foobar", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + EnvVariable: define.EnvVariable, + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + }, + goodNameEmptyContainerPrefix, + false, + false, + false, + false, + }, } for _, tt := range tests { test := tt diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 003c23e77..78ae6391b 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -242,7 +242,8 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) ( nameOrID = pod.Name() ctrNameOrID = infraCtr.Name() } - serviceName := fmt.Sprintf("%s%s%s", options.PodPrefix, options.Separator, nameOrID) + + serviceName := getServiceName(options.PodPrefix, options.Separator, nameOrID) info := podInfo{ ServiceName: serviceName, diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index b37e0825b..dcb18780c 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -67,6 +67,32 @@ WantedBy=default.target podGood := serviceInfo + headerInfo + podContent podGoodNoHeaderInfo := serviceInfo + podContent + podGoodWithEmptyPrefix := `# 123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman 123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=102 +ExecStart=/usr/bin/podman start jadda-jadda-infra +ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid +Type=forking + +[Install] +WantedBy=default.target +` + podGoodCustomWants := `# pod-123abc.service # autogenerated by Podman CI @@ -580,6 +606,24 @@ WantedBy=default.target false, false, }, + {"pod with empty pod-prefix", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "123abc", + InfraNameOrID: "jadda-jadda-infra", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "bar=arg with space"}, + }, + podGoodWithEmptyPrefix, + false, + false, + false, + }, } for _, tt := range tests { diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 55b9a8037..e4b854332 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -423,6 +423,20 @@ var _ = Describe("Podman generate systemd", func() { }) + It("podman generate systemd --container-prefix ''", func() { + n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n).Should(Exit(0)) + + session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--container-prefix", "", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Grepping the output (in addition to unit tests) + Expect(session.OutputToString()).To(ContainSubstring("# foo.service")) + + }) + It("podman generate systemd --separator _", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() @@ -485,6 +499,44 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p_foo.service")) }) + It("podman generate systemd pod --pod-prefix '' --container-prefix '' --separator _ change all prefixes/separator", func() { + n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) + n.WaitWithDefaultTimeout() + Expect(n).Should(Exit(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n).Should(Exit(0)) + + n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) + n.WaitWithDefaultTimeout() + Expect(n).Should(Exit(0)) + + // test systemd generate with empty pod prefix + session1 := podmanTest.Podman([]string{"generate", "systemd", "--pod-prefix", "", "--name", "foo"}) + session1.WaitWithDefaultTimeout() + Expect(session1).Should(Exit(0)) + + // Grepping the output (in addition to unit tests) + Expect(session1.OutputToString()).To(ContainSubstring("# foo.service")) + Expect(session1.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session1.OutputToString()).To(ContainSubstring("# container-foo-1.service")) + Expect(session1.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) + + // test systemd generate with empty container and pod prefix + session2 := podmanTest.Podman([]string{"generate", "systemd", "--container-prefix", "", "--pod-prefix", "", "--separator", "_", "--name", "foo"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should(Exit(0)) + + // Grepping the output (in addition to unit tests) + Expect(session2.OutputToString()).To(ContainSubstring("# foo.service")) + Expect(session2.OutputToString()).To(ContainSubstring("Requires=foo-1.service foo-2.service")) + Expect(session2.OutputToString()).To(ContainSubstring("# foo-1.service")) + Expect(session2.OutputToString()).To(ContainSubstring("# foo-2.service")) + Expect(session2.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) + + }) + It("podman generate systemd pod with containers --new", func() { tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) -- cgit v1.2.3-54-g00ecf From d2b26ac8a01539dd3b729698955f4f064cd052d5 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 16 Mar 2022 14:04:07 +0100 Subject: podman create: building local pause image: do not read ignore files Make sure to ignore local {container,docker}ignore files when building a local pause image. Otherwise, we may mistakenly not be able to copy catatonit into the build container. Fixes: #13529 Signed-off-by: Valentin Rothberg --- pkg/specgen/generate/pod_create.go | 1 + test/system/010-images.bats | 3 +-- test/system/070-build.bats | 11 ++++++++++- test/system/200-pod.bats | 8 +------- test/system/helpers.bash | 9 +++++++++ 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 68fda3ad7..279507f4c 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -56,6 +56,7 @@ ENTRYPOINT ["/catatonit", "-P"]`, catatonitPath) CommonBuildOpts: &buildahDefine.CommonBuildOptions{}, Output: imageName, Quiet: true, + IgnoreFile: "/dev/null", // makes sure to not read a local .ignorefile (see #13529) IIDFile: "/dev/null", // prevents Buildah from writing the ID on stdout } if _, _, err := rt.Build(context.Background(), buildOptions, tmpF.Name()); err != nil { diff --git a/test/system/010-images.bats b/test/system/010-images.bats index ebd71450f..dbf4b2828 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -248,8 +248,7 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z run_podman inspect --format '{{.ID}}' $IMAGE imageID=$output - run_podman version --format "{{.Server.Version}}-{{.Server.Built}}" - pauseImage=localhost/podman-pause:$output + pauseImage=$(pause_image) run_podman inspect --format '{{.ID}}' $pauseImage pauseID=$output diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 94256b215..e7c4b5b6f 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -607,7 +607,7 @@ EOF done } -# Regression test for #9867 +# Regression test for #9867 and #13529 # Make sure that if you exclude everything in context dir, that # the Containerfile/Dockerfile in the context dir are used @test "podman build with ignore '*'" { @@ -622,6 +622,15 @@ cat >$tmpdir/.dockerignore </dev/null + echo "localhost/podman-pause:$output" +} + ########################### # _add_label_if_missing # make sure skip messages include rootless/remote ########################### -- cgit v1.2.3-54-g00ecf From 5dce69bd9a6afa14a01de86d7ddac5ebb227643a Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 17 Mar 2022 18:54:47 +0100 Subject: podman machine: remove hostip from port Inside the podman machine vm we always remove the hostip from the port mapping because this should only be used on the actual host. Otherwise you run into issues when we would bind 127.0.0.1 or try to bind a host address that is not available in the VM. This was already done for cni/netavark ports and slirp4netns but not for the port bindings inside libpod which are only used as root. [NO NEW TESTS NEEDED] We still do not have machine tests! Fixes #13543 Signed-off-by: Paul Holzinger --- libpod/oci_conmon_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index a328f7621..72864b656 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -1199,7 +1199,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe) if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() { - ports, err := bindPorts(ctr.config.PortMappings) + ports, err := bindPorts(ctr.convertPortMappings()) if err != nil { return 0, err } -- cgit v1.2.3-54-g00ecf From b33a4671db9422145a3c0578756441634d42d6f8 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 18 Mar 2022 15:35:00 +0100 Subject: fix dual stack network e2e flake We need to use different ipv6 subnets for the tests since they can collide otherwise when the tests are run in parallel. In the future we should rethink hardcoding subnets for ipv4/ipv6. This will make it impossible to run these tests if the subnet is already used on the host. Example log: https://storage.googleapis.com/cirrus-ci-6707778565701632-fcae48/artifacts/containers/podman/5711403297275904/html/int-podman-fedora-35-root-host-netavark.log.html#t--podman-network-create-with-multiple-subnets-dual-stack-with-gateway-and-range--1 Signed-off-by: Paul Holzinger --- test/e2e/network_create_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 395759ee6..82b99bd68 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -416,8 +416,8 @@ var _ = Describe("Podman network create", func() { subnet1 := "10.10.3.0/24" gw1 := "10.10.3.10" range1 := "10.10.3.0/26" - subnet2 := "fd52:2a5a:747e:3acd::/64" - gw2 := "fd52:2a5a:747e:3acd::10" + subnet2 := "fd52:2a5a:747e:3ace::/64" + gw2 := "fd52:2a5a:747e:3ace::10" nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--gateway", gw1, "--ip-range", range1, "--subnet", subnet2, "--gateway", gw2, name}) nc.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(name) @@ -440,7 +440,7 @@ var _ = Describe("Podman network create", func() { name := "subnets-" + stringid.GenerateNonCryptoID() subnet1 := "10.10.3.0/24" gw1 := "10.10.3.10" - gw2 := "fd52:2a5a:747e:3acd::10" + gw2 := "fd52:2a5a:747e:3acf::10" nc := podmanTest.Podman([]string{"network", "create", "--subnet", subnet1, "--gateway", gw1, "--gateway", gw2, name}) nc.WaitWithDefaultTimeout() Expect(nc).To(Exit(125)) -- cgit v1.2.3-54-g00ecf From f131c0b240bb313e5a906e0d69b0eb6a5f35cbcf Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 18 Mar 2022 16:56:09 +0100 Subject: fix compose test flake Reading from the nc socket is flaky because docker-compose only starts the containers. We cannot know at this point if the container did already send the message. Give the container 5 seconds time to send the message to prevent flakes. This happened rarely with compose v1 but it looks like it will happen a lot more with compose v2. Example failure log: https://storage.googleapis.com/cirrus-ci-6707778565701632-fcae48/artifacts/containers/podman/6567556239589376/html/compose_v2-podman-fedora-35-rootless-host.log.html Signed-off-by: Paul Holzinger --- test/compose/slirp4netns_opts/tests.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/compose/slirp4netns_opts/tests.sh b/test/compose/slirp4netns_opts/tests.sh index 1efce45c4..cfa84e1e4 100644 --- a/test/compose/slirp4netns_opts/tests.sh +++ b/test/compose/slirp4netns_opts/tests.sh @@ -3,4 +3,18 @@ output="$(cat $OUTFILE)" expected="teststring" +# Reading from the nc socket is flaky because docker-compose only starts +# the containers. We cannot know at this point if the container did already +# send the message. Give the container 5 seconds time to send the message +# to prevent flakes. +local _timeout=5 +while [ $_timeout -gt 0 ]; do + if [ -n "$output" ]; then + break + fi + sleep 1 + _timeout=$(($_timeout - 1)) + output="$(cat $OUTFILE)" +done + is "$output" "$expected" "$testname : nc received teststring" -- cgit v1.2.3-54-g00ecf From a09e0c0be6be13b4391f1704e877faa2665a257c Mon Sep 17 00:00:00 2001 From: Kuldar Kaasik Date: Mon, 21 Mar 2022 09:17:10 +0200 Subject: Fix documentation typo Signed-off-by: Kuldar Kaasik --- docs/source/markdown/podman-system-service.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-system-service.1.md b/docs/source/markdown/podman-system-service.1.md index 3bc4fc7f1..17d8ea06a 100644 --- a/docs/source/markdown/podman-system-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -15,7 +15,7 @@ example *unix:///run/user/1000/podman/podman.sock*) To access the API service inside a container: - mount the socket as a volume -- run the container with `--security-opt label:disable` +- run the container with `--security-opt label=disable` The REST API provided by **podman system service** is split into two parts: a compatibility layer offering support for the Docker v1.40 API, and a Podman-native Libpod layer. Documentation for the latter is available at *https://docs.podman.io/en/latest/_static/api.html*. -- cgit v1.2.3-54-g00ecf From 6036941c170578aa4891443bd5b8d9a103d4a574 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Thu, 17 Mar 2022 12:13:18 -0600 Subject: pod system tests: clean up stray image One of the pod tests was leaving a stray image behind, causing scary red warnings in CI logs. Clean that up. Also, now that #13541 has merged, use 'rmi --ignore' instead of ignoring exit status from rmi Signed-off-by: Ed Santiago --- test/system/200-pod.bats | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index ca931e244..f5fe41924 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -6,7 +6,7 @@ load helpers function teardown() { run_podman pod rm -f -t 0 -a run_podman rm -f -t 0 -a - run_podman ? rmi $(pause_image) + run_podman rmi --ignore $(pause_image) basic_teardown } @@ -317,16 +317,17 @@ EOF @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)" + local infra_image="k8s.gcr.io/pause:3.5" local pod_name="$(random_string 10 | tr A-Z a-z)" - run_podman --noout pod create --name $pod_name --infra-name "$infra_name" --infra-image "k8s.gcr.io/pause:3.5" - is "$output" "" "output should be empty" + run_podman --noout pod create --name $pod_name --infra-name "$infra_name" --infra-image "$infra_image" + is "$output" "" "output from pod create should be empty" 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 + run_podman rmi $infra_image } @test "podman pod create --share" { -- cgit v1.2.3-54-g00ecf From c28401fe0ac5676831821d63e8fcb81d0d84d51e Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 21 Mar 2022 12:33:52 +0100 Subject: podman system df: fix percent calculation The calculate the percentage we need floating point numbers. The current code however casted the result of reclaimable/size to an int first. Casting to an int in go will just discard the decimal points, thus the result was either 0 or 1 so if multiplied by 100 it would show up as 0% or 100%. To fix this we have to multiply by 100 first before casting the result to an int. Also add a check for div by zero which results in NaN and use math.Round() to correctly round a number. Ref #13516 Signed-off-by: Paul Holzinger --- cmd/podman/system/df.go | 7 ++++++- test/e2e/system_df_test.go | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index b2325507a..49918487a 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -2,6 +2,7 @@ package system import ( "fmt" + "math" "os" "strings" "time" @@ -288,6 +289,10 @@ func (d *dfSummary) Size() string { } func (d *dfSummary) Reclaimable() string { - percent := int(float64(d.reclaimable)/float64(d.size)) * 100 + percent := 0 + // make sure to check this to prevent div by zero problems + if d.size > 0 { + percent = int(math.Round(float64(d.reclaimable) / float64(d.size) * float64(100))) + } return fmt.Sprintf("%s (%d%%)", units.HumanSize(float64(d.reclaimable)), percent) } diff --git a/test/e2e/system_df_test.go b/test/e2e/system_df_test.go index 2d75316ad..a9fa5f4ac 100644 --- a/test/e2e/system_df_test.go +++ b/test/e2e/system_df_test.go @@ -41,11 +41,17 @@ var _ = Describe("podman system df", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"volume", "create", "data"}) + // run two containers with volumes to create something in the volume + session = podmanTest.Podman([]string{"run", "-v", "data1:/data", "--name", "container1", BB, "sh", "-c", "echo test > /data/1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"create", "-v", "data:/data", "--name", "container1", BB}) + session = podmanTest.Podman([]string{"run", "-v", "data2:/data", "--name", "container2", BB, "sh", "-c", "echo test > /data/1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // remove one container, we keep the volume + session = podmanTest.Podman([]string{"rm", "container2"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -61,9 +67,10 @@ var _ = Describe("podman system df", func() { images := strings.Fields(session.OutputToStringArray()[1]) containers := strings.Fields(session.OutputToStringArray()[2]) volumes := strings.Fields(session.OutputToStringArray()[3]) - Expect(images[1]).To(Equal(string(totImages))) - Expect(containers[1]).To(Equal("2")) - Expect(volumes[2]).To(Equal("1")) + Expect(images[1]).To(Equal(string(totImages)), "total images expected") + Expect(containers[1]).To(Equal("2"), "total containers expected") + Expect(volumes[2]).To(Equal("2"), "total volumes expected") + Expect(volumes[6]).To(Equal("(50%)"), "percentage usage expected") }) It("podman system df image with no tag", func() { -- cgit v1.2.3-54-g00ecf From 9a3f3db6673a305181f63ab143fdf2b860097d15 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Mon, 21 Mar 2022 14:00:58 +0100 Subject: libpod: drop warning for Fedora 31 drop a warning for runc not supporting cgroup v2 on Fedora 31. [NO NEW TESTS NEEDED] Signed-off-by: Giuseppe Scrivano --- libpod/container_internal.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 51533b3bf..908b69f68 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1087,13 +1087,6 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { // With the spec complete, do an OCI create if _, err = c.ociRuntime.CreateContainer(c, nil); err != nil { - // Fedora 31 is carrying a patch to display improved error - // messages to better handle the V2 transition. This is NOT - // upstream in any OCI runtime. - // TODO: Remove once runc supports cgroupsv2 - if strings.Contains(err.Error(), "this version of runc doesn't work on cgroups v2") { - logrus.Errorf("Oci runtime %q does not support Cgroups V2: use system migrate to mitigate", c.ociRuntime.Name()) - } return err } -- cgit v1.2.3-54-g00ecf From 116a9ef02fd8470bb7e7428cd07a51d4fed2a1c9 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Mon, 21 Mar 2022 14:59:25 -0400 Subject: Makefile: build podman-remote-static with cgo disabled Resolves: #13557 [NO NEW TESTS NEEDED] Signed-off-by: Lokesh Mandvekar --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7f7271bd6..99a4e6f1e 100644 --- a/Makefile +++ b/Makefile @@ -335,7 +335,10 @@ $(SRCBINDIR)/podman$(BINSFX): $(SRCBINDIR) .gopathok $(SOURCES) go.mod go.sum -o $@ ./cmd/podman $(SRCBINDIR)/podman-remote-static: $(SRCBINDIR) .gopathok $(SOURCES) go.mod go.sum - $(GOCMD) build \ + CGO_ENABLED=0 \ + GOOS=$(GOOS) \ + GOARCH=$(GOARCH) \ + $(GO) build \ $(BUILDFLAGS) \ $(GO_LDFLAGS) '$(LDFLAGS_PODMAN_STATIC)' \ -tags "${REMOTETAGS}" \ -- cgit v1.2.3-54-g00ecf From ef202133f512dfd40bf83ee1306039736ff74fcd Mon Sep 17 00:00:00 2001 From: Aditya R Date: Tue, 22 Mar 2022 12:28:52 +0530 Subject: healthcheck: stop showing wrong status when --no-healthcheck is set Containers started with `--no-healthcheck` are configured to contain no healthcheck and test configured as `NONE`. Podman shows wrong status as such use cases. Following commit fixes the faulty behavior of stauts field for containers started with `--no-healthcheck` Signed-off-by: Aditya R --- libpod/container_internal.go | 5 ++++- test/e2e/healthcheck_run_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 908b69f68..0618cc02e 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1261,7 +1261,10 @@ func (c *Container) start() error { } } - if c.config.HealthCheckConfig != nil { + // Check if healthcheck is not nil and --no-healthcheck option is not set. + // If --no-healthcheck is set Test will be always set to `[NONE]` so no need + // to update status in such case. + if c.config.HealthCheckConfig != nil && !(len(c.config.HealthCheckConfig.Test) == 1 && c.config.HealthCheckConfig.Test[0] == "NONE") { if err := c.updateHealthStatus(define.HealthCheckStarting); err != nil { logrus.Error(err) } diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index 866edbf0e..757eaed20 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -54,6 +54,16 @@ var _ = Describe("Podman healthcheck run", func() { Expect(hc).Should(Exit(125)) }) + It("podman disable healthcheck with --no-healthcheck must not show starting on status", func() { + session := podmanTest.Podman([]string{"run", "-dt", "--no-healthcheck", "--name", "hc", healthcheck}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Health.Status}}", "hc"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(Exit(0)) + Expect(hc.OutputToString()).To(Not(ContainSubstring("starting"))) + }) + It("podman run healthcheck and logs should contain healthcheck output", func() { session := podmanTest.Podman([]string{"run", "--name", "test-logs", "-dt", "--health-interval", "1s", "--health-cmd", "echo working", "busybox", "sleep", "3600"}) session.WaitWithDefaultTimeout() -- cgit v1.2.3-54-g00ecf From 4dbb2bf92b2b0a85e5506375267e8a3ccf937bc8 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 2 Mar 2022 10:09:03 +0100 Subject: libpod: drop warning if cgroup doesn't exist do not print a warning on cgroup removal if it doesn't exist. Closes: https://github.com/containers/podman/issues/13382 [NO NEW TESTS NEEDED] Signed-off-by: Giuseppe Scrivano --- libpod/runtime_pod_linux.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 230491c1a..2bbccfdf6 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -6,6 +6,7 @@ package libpod import ( "context" "fmt" + "os" "path" "path/filepath" "strings" @@ -239,7 +240,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, // Don't try if we failed to retrieve the cgroup if err == nil { - if err := conmonCgroup.Update(resLimits); err != nil { + if err := conmonCgroup.Update(resLimits); err != nil && !os.IsNotExist(err) { logrus.Warnf("Error updating pod %s conmon cgroup PID limit: %v", p.ID(), err) } } -- cgit v1.2.3-54-g00ecf From 89a4466ee8944df5976a62e50ef67ba0beef033d Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 22 Mar 2022 14:50:39 +0100 Subject: fix compose test error in retry logic We cannot use local var outside of a function. We have to use a global one. Log: https://storage.googleapis.com/cirrus-ci-6707778565701632-fcae48/artifacts/containers/podman/5970023511490560/html/compose_v2-podman-fedora-35-root-host.log.html Signed-off-by: Paul Holzinger --- test/compose/slirp4netns_opts/tests.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/compose/slirp4netns_opts/tests.sh b/test/compose/slirp4netns_opts/tests.sh index cfa84e1e4..2d41311ad 100644 --- a/test/compose/slirp4netns_opts/tests.sh +++ b/test/compose/slirp4netns_opts/tests.sh @@ -1,20 +1,19 @@ # -*- bash -*- -output="$(cat $OUTFILE)" expected="teststring" # Reading from the nc socket is flaky because docker-compose only starts # the containers. We cannot know at this point if the container did already # send the message. Give the container 5 seconds time to send the message # to prevent flakes. -local _timeout=5 -while [ $_timeout -gt 0 ]; do +container_timeout=5 +while [ $container_timeout -gt 0 ]; do + output="$(< $OUTFILE)" if [ -n "$output" ]; then break fi sleep 1 - _timeout=$(($_timeout - 1)) - output="$(cat $OUTFILE)" + container_timeout=$(($container_timeout - 1)) done is "$output" "$expected" "$testname : nc received teststring" -- cgit v1.2.3-54-g00ecf From 98677c2f04fd5d2ac0f87aeb79d8cf7162865c2e Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 22 Mar 2022 17:52:28 +0100 Subject: podman unshare: document that command cannot be used with remote Fixes #13596 Signed-off-by: Paul Holzinger --- docs/source/markdown/podman-unshare.1.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md index 01393a862..db1bc5387 100644 --- a/docs/source/markdown/podman-unshare.1.md +++ b/docs/source/markdown/podman-unshare.1.md @@ -4,7 +4,7 @@ podman\-unshare - Run a command inside of a modified user namespace ## SYNOPSIS -**podman unshare** [*--*] [*command*] +**podman unshare** [*options*] [*command*] ## DESCRIPTION Launches a process (by default, *$SHELL*) in a new user namespace. The user @@ -24,6 +24,8 @@ The unshare session defines two environment variables: - **CONTAINERS_GRAPHROOT**: the path to the persistent container's data. - **CONTAINERS_RUNROOT**: the path to the volatile container's data. +*IMPORTANT: This command is not available with the remote Podman client.* + ## OPTIONS #### **--help**, **-h** -- cgit v1.2.3-54-g00ecf From 12ab78d3333d1b2bfcc6f4f4a13f88fea5657007 Mon Sep 17 00:00:00 2001 From: John Kristensen Date: Wed, 23 Mar 2022 10:29:35 +1100 Subject: docs: Fix links to Containerfile and containerignore The documentation files for `Containerfile` and `containerignore` have been moved from the `buildah` repository to the `common` repository. ref: https://github.com/containers/buildah/commit/488e8654ea Signed-off-by: John Kristensen --- docs/source/markdown/podman-build.1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 2c8f4005c..ad06eecb9 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -1003,7 +1003,7 @@ Exclude all doc files except Help.doc from the image. This functionality is compatible with the handling of .containerignore files described here: -https://github.com/containers/buildah/blob/main/docs/containerignore.5.md +https://github.com/containers/common/blob/main/docs/containerignore.5.md **registries.conf** (`/etc/containers/registries.conf`) @@ -1025,7 +1025,7 @@ If you are using `useradd` within your build script, you should pass the useradd to stop creating the lastlog file. ## SEE ALSO -**[podman(1)](podman.1.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[useradd(8)](https://www.unix.com/man-page/redhat/8/useradd)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[Containerfile(5)](https://github.com/containers/buildah/blob/main/docs/Containerfile.5.md)**, **[containerignore(5)](https://github.com/containers/buildah/blob/main/docs/containerignore.5.md)** +**[podman(1)](podman.1.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[useradd(8)](https://www.unix.com/man-page/redhat/8/useradd)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[Containerfile(5)](https://github.com/containers/common/blob/main/docs/Containerfile.5.md)**, **[containerignore(5)](https://github.com/containers/common/blob/main/docs/containerignore.5.md)** ## HISTORY Aug 2020, Additional options and .containerignore added by Dan Walsh `` -- cgit v1.2.3-54-g00ecf From 82c01341f792462c78e9390c3c92b5487e26cdf3 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 22 Mar 2022 14:01:44 -0400 Subject: Fix a potential race around the exec cleanup process Every exec session run attached will, on exit, do two things: it will signal the associated `podman exec` that it is finished (to allow Podman to collect the exit code and exit), and spawn a cleanup process to clean up the exec session (in case the `podman exec` process died, we still need to clean up). If an exec session is created that exits almost instantly, but generates a large amount of output (e.g. prints thousands of lines), the cleanup process can potentially execute before `podman exec` has a chance to read the exit code, resulting in errors. Handle this by detecting if the cleanup process has already removed the exec session before handling the error from reading the exec exit code. [NO NEW TESTS NEEDED] I have no idea how to test this in CI. Fixes #13227 Signed-off-by: Matthew Heon --- libpod/container_exec.go | 56 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/libpod/container_exec.go b/libpod/container_exec.go index d1c190905..140267f28 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -341,22 +341,60 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS } lastErr = tmpErr - exitCode, err := c.readExecExitCode(session.ID()) - if err != nil { + exitCode, exitCodeErr := c.readExecExitCode(session.ID()) + + // Lock again. + // Important: we must lock and sync *before* the above error is handled. + // We need info from the database to handle the error. + if !c.batched { + c.lock.Lock() + } + // We can't reuse the old exec session (things may have changed from + // other use, the container was unlocked). + // So re-sync and get a fresh copy. + // If we can't do this, no point in continuing, any attempt to save + // would write garbage to the DB. + if err := c.syncContainer(); err != nil { + if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { + // We can't save status, but since the container has + // been entirely removed, we don't have to; exit cleanly + return lastErr + } if lastErr != nil { logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr) } - lastErr = err - } + return errors.Wrapf(err, "error syncing container %s state to update exec session %s", c.ID(), sessionID) + } + + // Now handle the error from readExecExitCode above. + if exitCodeErr != nil { + newSess, ok := c.state.ExecSessions[sessionID] + if !ok { + // The exec session was removed entirely, probably by + // the cleanup process. When it did so, it should have + // written an event with the exit code. + // Given that, there's nothing more we can do. + logrus.Infof("Container %s exec session %s already removed", c.ID(), session.ID()) + return lastErr + } - logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode) + if newSess.State == define.ExecStateStopped { + // Exec session already cleaned up. + // Exit code should be recorded, so it's OK if we were + // not able to read it. + logrus.Infof("Container %s exec session %s already cleaned up", c.ID(), session.ID()) + return lastErr + } - // Lock again - if !c.batched { - c.lock.Lock() + if lastErr != nil { + logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr) + } + lastErr = exitCodeErr } - if err := writeExecExitCode(c, session.ID(), exitCode); err != nil { + logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode) + + if err := justWriteExecExitCode(c, session.ID(), exitCode); err != nil { if lastErr != nil { logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr) } -- cgit v1.2.3-54-g00ecf From 8bc2f6cd8486c893c292da3aa64965d10cd8ffe8 Mon Sep 17 00:00:00 2001 From: Burt Holzman Date: Wed, 23 Mar 2022 10:42:05 -0500 Subject: Explicitly use IPv4 to check if podman-machine VM is listening When starting a VM that has been configured with volume mounts, the podman client attempts to connect via TCP to localhost, which runs gvproxy to proxy an ephemeral port to the VM's ssh port. Previously, gvproxy was listening on all interfaces and IP addresses, but this behavior has changed to listening only on the IPv4 loopback address. Without this change, if a newer build of gvproxy is used, a podman machine configured with volume mounts will hang forever after "podman machine start" with "Waiting for VM ...". [NO NEW TESTS NEEDED] Signed-off-by: Burt Holzman --- pkg/machine/qemu/machine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index d30e51215..d4a2cffac 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -778,7 +778,7 @@ func (v *MachineVM) isRunning() (bool, error) { func (v *MachineVM) isListening() bool { // Check if we can dial it - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", "localhost", v.Port), 10*time.Millisecond) + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", v.Port), 10*time.Millisecond) if err != nil { return false } -- cgit v1.2.3-54-g00ecf From 9510238ff9692a2145be8dc2c712db208cdfd02b Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 22 Mar 2022 17:37:23 +0100 Subject: podman stats: improve cpu average calc We can just calculate the cpu percent for the time the container is running. There is no need to use datapoints. Signed-off-by: Paul Holzinger --- libpod/define/containerstate.go | 1 - libpod/stats.go | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/libpod/define/containerstate.go b/libpod/define/containerstate.go index 23ba1f451..9ad3aec08 100644 --- a/libpod/define/containerstate.go +++ b/libpod/define/containerstate.go @@ -138,7 +138,6 @@ type ContainerStats struct { CPU float64 CPUNano uint64 CPUSystemNano uint64 - DataPoints int64 SystemNano uint64 MemUsage uint64 MemLimit uint64 diff --git a/libpod/stats.go b/libpod/stats.go index dbb10a27e..7619ef345 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -66,8 +66,8 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de stats.Duration = cgroupStats.CPU.Usage.Total stats.UpTime = time.Duration(stats.Duration) stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano) - stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints) - stats.DataPoints = previousStats.DataPoints + 1 + // calc the average cpu usage for the time the container is running + stats.AvgCPU = calculateCPUPercent(cgroupStats, 0, now, uint64(c.state.StartedTime.UnixNano())) stats.MemUsage = cgroupStats.Memory.Usage.Usage stats.MemLimit = c.getMemLimit() stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 @@ -145,9 +145,3 @@ func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) { } return } - -// calculateAvgCPU calculates the avg CPU percentage given the previous average and the number of data points. -func calculateAvgCPU(statsCPU float64, prevAvg float64, prevData int64) float64 { - avgPer := ((prevAvg * float64(prevData)) + statsCPU) / (float64(prevData) + 1) - return avgPer -} -- cgit v1.2.3-54-g00ecf From 1266f7342057cf2161122f52e48ce80b80f233a4 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 22 Mar 2022 17:10:43 +0100 Subject: podman stats: calc CPU percentage correctly When you run podman stats, the first interval always shows the wrong cpu usage. To calculate cpu percentage we get the cpu time from the cgroup and compare this against the system time between two stats. Since the first time we do not have a previous stats an empty struct is used instead. Thus we do not use the actual running time of the container but the current unix timestamp (time since Jan 1 1970). To fix this we make sure that the previous stats time is set to the container start time, when it is empty. [NO NEW TESTS NEEDED] No idea how I could create a test which would have a predictable cpu usage. See the linked bugzilla for a reproducer. Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2066145 Signed-off-by: Paul Holzinger --- libpod/pod.go | 9 +-------- libpod/stats.go | 12 +++++++++++- pkg/api/handlers/compat/containers_stats.go | 2 +- pkg/domain/infra/abi/containers.go | 7 +------ 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/libpod/pod.go b/libpod/pod.go index 6273ff247..ed2d97b37 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -422,10 +422,6 @@ type PodContainerStats struct { // GetPodStats returns the stats for each of its containers func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerStats) (map[string]*define.ContainerStats, error) { - var ( - ok bool - prevStat *define.ContainerStats - ) p.lock.Lock() defer p.lock.Unlock() @@ -438,10 +434,7 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta } newContainerStats := make(map[string]*define.ContainerStats) for _, c := range containers { - if prevStat, ok = previousContainerStats[c.ID()]; !ok { - prevStat = &define.ContainerStats{} - } - newStats, err := c.GetContainerStats(prevStat) + newStats, err := c.GetContainerStats(previousContainerStats[c.ID()]) // If the container wasn't running, don't include it // but also suppress the error if err != nil && errors.Cause(err) != define.ErrCtrStateInvalid { diff --git a/libpod/stats.go b/libpod/stats.go index 7619ef345..4fff385a3 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -13,7 +13,9 @@ import ( "github.com/pkg/errors" ) -// GetContainerStats gets the running stats for a given container +// GetContainerStats gets the running stats for a given container. +// The previousStats is used to correctly calculate cpu percentages. You +// should pass nil if there is no previous stat for this container. func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { stats := new(define.ContainerStats) stats.ContainerID = c.ID() @@ -35,6 +37,14 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de return stats, define.ErrCtrStateInvalid } + if previousStats == nil { + previousStats = &define.ContainerStats{ + // if we have no prev stats use the container start time as prev time + // otherwise we cannot correctly calculate the CPU percentage + SystemNano: uint64(c.state.StartedTime.UnixNano()), + } + } + cgroupPath, err := c.cGroupPath() if err != nil { return nil, err diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index 99f14d02f..77b16b03e 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -56,7 +56,7 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { return } - stats, err := ctnr.GetContainerStats(&define.ContainerStats{}) + stats, err := ctnr.GetContainerStats(nil) if err != nil { utils.InternalServerError(w, errors.Wrapf(err, "failed to obtain Container %s stats", name)) return diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index a5c4647d7..ef2aa99d3 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1429,12 +1429,7 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri reportStats := []define.ContainerStats{} for _, ctr := range containers { - prev, ok := containerStats[ctr.ID()] - if !ok { - prev = &define.ContainerStats{} - } - - stats, err := ctr.GetContainerStats(prev) + stats, err := ctr.GetContainerStats(containerStats[ctr.ID()]) if err != nil { cause := errors.Cause(err) if queryAll && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { -- cgit v1.2.3-54-g00ecf From ee9030bfdee6f0b6f3eb1e45081cb604c32581d0 Mon Sep 17 00:00:00 2001 From: Aditya R Date: Thu, 24 Mar 2022 14:34:37 +0530 Subject: machine-set: fix example for setting rootful flag Flag is actually named `rootful` however documented as `root`, fix the documented example as actual flag. Both `podman machine init` and `podman machine set` uses flag `rootfull` [NO TESTS NEEDED] [NO NEW TESTS NEEDED] Signed-off-by: Aditya R --- cmd/podman/machine/set.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/podman/machine/set.go b/cmd/podman/machine/set.go index c978206f0..41c08fed5 100644 --- a/cmd/podman/machine/set.go +++ b/cmd/podman/machine/set.go @@ -16,7 +16,7 @@ var ( Long: "Sets an updatable virtual machine setting", RunE: setMachine, Args: cobra.MaximumNArgs(1), - Example: `podman machine set --root=false`, + Example: `podman machine set --rootful=false`, ValidArgsFunction: completion.AutocompleteNone, } ) -- cgit v1.2.3-54-g00ecf From b1cc5043fd6d4acea8bfff2464d61236df6a5e86 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Wed, 23 Mar 2022 00:21:48 -0700 Subject: play: kube: use in-memory kubefile and remove tempfile The PlayKube and PlayKubeDown commands accepted a "path" argument to a YAML file to play. This requires the caller to write the YAML to a file path. The downside of this is apparent in the HTTP handlers which have to use a temporary file on disk to store the YAML file. The file is opened & used as the body of the HTTP request. It's possible to instead pass a io.Reader and use a fully in-memory request body. Add backwards-compatible changes to bindings to allow passing either a filepath or a io.Reader body. Refactor the podman bindings to use a io.Reader instead of a filepath. Simplify the HTTP handlers for PlayKube by removing the now unneeded tempfile. [NO NEW TESTS NEEDED] Signed-off-by: Christian Stewart --- cmd/podman/play/kube.go | 14 +++++++-- pkg/api/handlers/libpod/play.go | 53 +++------------------------------ pkg/bindings/play/play.go | 39 +++++++++++++++--------- pkg/domain/entities/engine_container.go | 4 +-- pkg/domain/infra/abi/play.go | 28 ++++++++--------- pkg/domain/infra/tunnel/play.go | 9 +++--- 6 files changed, 62 insertions(+), 85 deletions(-) diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go index 1a430f2dc..7ff497a5c 100644 --- a/cmd/podman/play/kube.go +++ b/cmd/podman/play/kube.go @@ -185,10 +185,15 @@ func teardown(yamlfile string) error { podRmErrors utils.OutputErrors ) options := new(entities.PlayKubeDownOptions) - reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), yamlfile, *options) + f, err := os.Open(yamlfile) if err != nil { return err } + defer f.Close() + reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), f, *options) + if err != nil { + return errors.Wrap(err, yamlfile) + } // Output stopped pods fmt.Println("Pods stopped:") @@ -218,10 +223,15 @@ func teardown(yamlfile string) error { } func playkube(yamlfile string) error { - report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) + f, err := os.Open(yamlfile) if err != nil { return err } + defer f.Close() + report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), f, kubeOptions.PlayKubeOptions) + if err != nil { + return errors.Wrap(err, yamlfile) + } // Print volumes report for i, volume := range report.Volumes { if i == 0 { diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 515d0e5cf..90e244ec2 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -1,11 +1,8 @@ package libpod import ( - "io" - "io/ioutil" "net" "net/http" - "os" "github.com/containers/image/v5/types" "github.com/containers/podman/v4/libpod" @@ -16,7 +13,6 @@ import ( "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) func PlayKube(w http.ResponseWriter, r *http.Request) { @@ -61,28 +57,6 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { staticMACs = append(staticMACs, mac) } - // Fetch the K8s YAML file from the body, and copy it to a temp file. - tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml") - if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) - return - } - defer func() { - if err := os.Remove(tmpfile.Name()); err != nil { - logrus.Warn(err) - } - }() - if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF { - if err := tmpfile.Close(); err != nil { - logrus.Warn(err) - } - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file")) - return - } - if err := tmpfile.Close(); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file")) - return - } authConf, authfile, err := auth.GetCredentials(r) if err != nil { utils.Error(w, http.StatusBadRequest, err) @@ -114,7 +88,8 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { if _, found := r.URL.Query()["start"]; found { options.Start = types.NewOptionalBool(query.Start) } - report, err := containerEngine.PlayKube(r.Context(), tmpfile.Name(), options) + report, err := containerEngine.PlayKube(r.Context(), r.Body, options) + _ = r.Body.Close() if err != nil { utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file")) return @@ -124,30 +99,10 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { func PlayKubeDown(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml") - if err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) - return - } - defer func() { - if err := os.Remove(tmpfile.Name()); err != nil { - logrus.Warn(err) - } - }() - if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF { - if err := tmpfile.Close(); err != nil { - logrus.Warn(err) - } - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file")) - return - } - if err := tmpfile.Close(); err != nil { - utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file")) - return - } containerEngine := abi.ContainerEngine{Libpod: runtime} options := new(entities.PlayKubeDownOptions) - report, err := containerEngine.PlayKubeDown(r.Context(), tmpfile.Name(), *options) + report, err := containerEngine.PlayKubeDown(r.Context(), r.Body, *options) + _ = r.Body.Close() if err != nil { utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error tearing down YAML file")) return diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index d4018b6b3..8058a8514 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -2,6 +2,7 @@ package play import ( "context" + "io" "net/http" "os" "strconv" @@ -14,20 +15,25 @@ import ( ) func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.PlayKubeReport, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + return KubeWithBody(ctx, f, options) +} + +func KubeWithBody(ctx context.Context, body io.Reader, options *KubeOptions) (*entities.PlayKubeReport, error) { var report entities.PlayKubeReport if options == nil { options = new(KubeOptions) } - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - f, err := os.Open(path) + conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - defer f.Close() params, err := options.ToParams() if err != nil { @@ -46,7 +52,7 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla return nil, err } - response, err := conn.DoRequest(ctx, f, http.MethodPost, "/play/kube", params, header) + response, err := conn.DoRequest(ctx, body, http.MethodPost, "/play/kube", params, header) if err != nil { return nil, err } @@ -60,12 +66,6 @@ func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.Pla } func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error) { - var report entities.PlayKubeReport - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - f, err := os.Open(path) if err != nil { return nil, err @@ -75,7 +75,18 @@ func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error logrus.Warn(err) } }() - response, err := conn.DoRequest(ctx, f, http.MethodDelete, "/play/kube", nil, nil) + + return KubeDownWithBody(ctx, f) +} + +func KubeDownWithBody(ctx context.Context, body io.Reader) (*entities.PlayKubeReport, error) { + var report entities.PlayKubeReport + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(ctx, body, http.MethodDelete, "/play/kube", nil, nil) if err != nil { return nil, err } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5dedceacc..5c169646b 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -67,8 +67,8 @@ type ContainerEngine interface { NetworkPrune(ctx context.Context, options NetworkPruneOptions) ([]*NetworkPruneReport, error) NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) - PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) - PlayKubeDown(ctx context.Context, path string, opts PlayKubeDownOptions) (*PlayKubeReport, error) + PlayKube(ctx context.Context, body io.Reader, opts PlayKubeOptions) (*PlayKubeReport, error) + PlayKubeDown(ctx context.Context, body io.Reader, opts PlayKubeDownOptions) (*PlayKubeReport, error) PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 9adf5eb12..a7555d715 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -33,12 +33,12 @@ import ( v1 "k8s.io/api/core/v1" ) -func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { report := &entities.PlayKubeReport{} validKinds := 0 // read yaml document - content, err := ioutil.ReadFile(path) + content, err := ioutil.ReadAll(body) if err != nil { return nil, err } @@ -52,7 +52,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en // sort kube kinds documentList, err = sortKubeKinds(documentList) if err != nil { - return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path) + return nil, errors.Wrap(err, "unable to sort kube kinds") } ipIndex := 0 @@ -64,7 +64,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en for _, document := range documentList { kind, err := getKubeKind(document) if err != nil { - return nil, errors.Wrapf(err, "unable to read %q as kube YAML", path) + return nil, errors.Wrap(err, "unable to read kube YAML") } switch kind { @@ -73,7 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en var podTemplateSpec v1.PodTemplateSpec if err := yaml.Unmarshal(document, &podYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Pod", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube Pod") } podTemplateSpec.ObjectMeta = podYAML.ObjectMeta @@ -90,7 +90,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en var deploymentYAML v1apps.Deployment if err := yaml.Unmarshal(document, &deploymentYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment") } r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps) @@ -104,7 +104,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en var pvcYAML v1.PersistentVolumeClaim if err := yaml.Unmarshal(document, &pvcYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube PersistentVolumeClaim", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube PersistentVolumeClaim") } r, err := ic.playKubePVC(ctx, &pvcYAML, options) @@ -118,7 +118,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en var configMap v1.ConfigMap if err := yaml.Unmarshal(document, &configMap); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube ConfigMap", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube ConfigMap") } configMaps = append(configMaps, configMap) default: @@ -746,14 +746,14 @@ func getBuildFile(imageName string, cwd string) (string, error) { return "", err } -func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { var ( podNames []string ) reports := new(entities.PlayKubeReport) // read yaml document - content, err := ioutil.ReadFile(path) + content, err := ioutil.ReadAll(body) if err != nil { return nil, err } @@ -767,27 +767,27 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ enti // sort kube kinds documentList, err = sortKubeKinds(documentList) if err != nil { - return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path) + return nil, errors.Wrap(err, "unable to sort kube kinds") } for _, document := range documentList { kind, err := getKubeKind(document) if err != nil { - return nil, errors.Wrapf(err, "unable to read %q as kube YAML", path) + return nil, errors.Wrap(err, "unable to read as kube YAML") } switch kind { case "Pod": var podYAML v1.Pod if err := yaml.Unmarshal(document, &podYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Pod", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube Pod") } podNames = append(podNames, podYAML.ObjectMeta.Name) case "Deployment": var deploymentYAML v1apps.Deployment if err := yaml.Unmarshal(document, &deploymentYAML); err != nil { - return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path) + return nil, errors.Wrap(err, "unable to read YAML as Kube Deployment") } var numReplicas int32 = 1 deploymentName := deploymentYAML.ObjectMeta.Name diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go index 55844730b..24b4cf08b 100644 --- a/pkg/domain/infra/tunnel/play.go +++ b/pkg/domain/infra/tunnel/play.go @@ -2,13 +2,14 @@ package tunnel import ( "context" + "io" "github.com/containers/image/v5/types" "github.com/containers/podman/v4/pkg/bindings/play" "github.com/containers/podman/v4/pkg/domain/entities" ) -func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password) options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps) options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Networks).WithSeccompProfileRoot(opts.SeccompProfileRoot) @@ -23,9 +24,9 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entit if start := opts.Start; start != types.OptionalBoolUndefined { options.WithStart(start == types.OptionalBoolTrue) } - return play.Kube(ic.ClientCtx, path, options) + return play.KubeWithBody(ic.ClientCtx, body, options) } -func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { - return play.KubeDown(ic.ClientCtx, path) +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { + return play.KubeDownWithBody(ic.ClientCtx, body) } -- cgit v1.2.3-54-g00ecf From bca6b80b3e4d352a4bb1592c12ed883a68eaabf4 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 24 Mar 2022 12:19:41 +0100 Subject: podman machine set: clarify --rootful option It is not quite clear what the difference between `podman machine set --rootful` and `podman system connection default` is. Add a small note with the difference, the --rootful option will also affect the socket forwarding. Fixes #13515 Signed-off-by: Paul Holzinger --- docs/source/markdown/podman-machine-set.1.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-machine-set.1.md b/docs/source/markdown/podman-machine-set.1.md index e69779564..44f7746b0 100644 --- a/docs/source/markdown/podman-machine-set.1.md +++ b/docs/source/markdown/podman-machine-set.1.md @@ -22,7 +22,9 @@ container execution. This option will also update the current podman remote connection default if it is currently pointing at the specified machine name (or `podman-machine-default` if no name is specified). -API forwarding, if available, will follow this setting. +Unlike [**podman system connection default**](podman-system-connection-default.1.md) +this option will also make the API socket, if available, forward to the rootful/rootless +socket in the VM. #### **--help** -- cgit v1.2.3-54-g00ecf From 770594381611308c4feeb310d8b8ac68759287db Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 24 Mar 2022 10:46:01 +0100 Subject: Remove experimental warning from podman-remote rpm podman-remote is considered stable and follows the same semver as podman. Fixes a question on the podman mailing list. https://lists.podman.io/archives/list/podman@lists.podman.io/thread/2DVCU5Z54U4PI5ROTBQXHDBLQSAXAHFU/ Signed-off-by: Paul Holzinger --- podman.spec.rpkg | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/podman.spec.rpkg b/podman.spec.rpkg index a6f66ce98..17276db7f 100644 --- a/podman.spec.rpkg +++ b/podman.spec.rpkg @@ -138,10 +138,7 @@ Summary: (Experimental) Remote client for managing %{name} containers %description remote Remote client for managing %{name} containers. -This experimental remote client is under heavy development. Please do not -run %{name}-remote in production. - -%{name}-remote uses the version 2 API to connect to a %{name} client to +%{name}-remote uses the libpod REST API to connect to a %{name} client to manage pods, containers and container images. %{name}-remote supports ssh connections as well. -- cgit v1.2.3-54-g00ecf From 07c5946d9c2b3892a0562a5b78d2061b0c0d1c25 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 23 Mar 2022 18:22:18 +0100 Subject: podman machine start: lookup qemu path again if not found We store the full path to qemu in the machine config. When the path changes on the host the machine can longer be started. To fix it we get the path again when we fail to start the machine due the missing binary. We want to store and use the full path first because otherwise existing machines can break when the qemu version changed. [NO NEW TESTS NEEDED] We still have no machine tests. Fixes #13394 Signed-off-by: Paul Holzinger --- pkg/machine/qemu/machine.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index d4a2cffac..b440d9de2 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -98,7 +98,7 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { return nil, err } - cmd := append([]string{execPath}) + cmd := []string{execPath} // Add memory cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) // Add cpus @@ -434,7 +434,23 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { _, err = os.StartProcess(v.CmdLine[0], cmd, attr) if err != nil { - return err + // check if qemu was not found + if !errors.Is(err, os.ErrNotExist) { + return err + } + // lookup qemu again maybe the path was changed, https://github.com/containers/podman/issues/13394 + cfg, err := config.Default() + if err != nil { + return err + } + cmd[0], err = cfg.FindHelperBinary(QemuCommand, true) + if err != nil { + return err + } + _, err = os.StartProcess(cmd[0], cmd, attr) + if err != nil { + return err + } } fmt.Println("Waiting for VM ...") socketPath, err := getRuntimeDir() -- cgit v1.2.3-54-g00ecf From 1e8c8e912ad1cb4b7c40206edc94ec1cb125ccf2 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 23 Mar 2022 18:26:36 +0100 Subject: fix podman machine start log level detection Use logrus.IsLevelEnabled because this will cover all levels below it as well. Currently this condition would fail for the trace log level. Signed-off-by: Paul Holzinger --- pkg/machine/qemu/machine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index b440d9de2..79f75c144 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -428,7 +428,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { // Disable graphic window when not in debug mode // Done in start, so we're not suck with the debug level we used on init - if logrus.GetLevel() != logrus.DebugLevel { + if !logrus.IsLevelEnabled(logrus.DebugLevel) { cmd = append(cmd, "-display", "none") } -- cgit v1.2.3-54-g00ecf From e93dc6c784d5242c529077de86e3dbaf33e07666 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 24 Mar 2022 11:59:50 +0100 Subject: readConmonPipeData: try to improve error Issue #10927 reports `container create failed (no logs from conmon): EOF` errors. Since we do not know the root cause it would be helpful to try to get as much info as possible out of the error. (buffer).ReadBytes() will return the bytes read even when an error occurs. So when we get an EOF we could still have some valuable information in the buffer. Lets try to unmarshal them and if this fails we add the bytes to the error message. This does not fix the issue but it might help us getting a better error. [NO NEW TESTS NEEDED] Signed-off-by: Paul Holzinger --- libpod/oci_conmon_linux.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 72864b656..7c4fffa5f 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -1585,11 +1585,13 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int, var si *syncInfo rdr := bufio.NewReader(pipe) b, err := rdr.ReadBytes('\n') - if err != nil { + // ignore EOF here, error is returned even when data was read + // if it is no valid json unmarshal will fail below + if err != nil && !errors.Is(err, io.EOF) { ch <- syncStruct{err: err} } if err := json.Unmarshal(b, &si); err != nil { - ch <- syncStruct{err: err} + ch <- syncStruct{err: fmt.Errorf("conmon bytes %q: %w", string(b), err)} return } ch <- syncStruct{si: si} -- cgit v1.2.3-54-g00ecf From 2d01e788b10f0dd22b6c5747b0726ca36fafb61c Mon Sep 17 00:00:00 2001 From: cdoern Date: Tue, 15 Mar 2022 12:13:41 -0400 Subject: add contextDir to tar on remote podman build fails on remote build when using a relative context directory. This is because the context dir was not being added to the tar, so when remote the compat build function would not be able to stat the contextDir. resolves #13293 Signed-off-by: cdoern --- pkg/bindings/images/build.go | 26 +++++++++++++++++++------- test/e2e/build_test.go | 5 +++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index c508cb767..1ed3f19de 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -241,7 +241,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Add("platform", platform) } } - if contextDir, err := filepath.EvalSymlinks(options.ContextDirectory); err == nil { + var err error + var contextDir string + if contextDir, err = filepath.EvalSymlinks(options.ContextDirectory); err == nil { options.ContextDirectory = contextDir } @@ -301,7 +303,6 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO var ( headers http.Header - err error ) if options.SystemContext != nil && options.SystemContext.DockerAuthConfig != nil { headers, err = auth.MakeXRegistryAuthHeader(options.SystemContext, options.SystemContext.DockerAuthConfig.Username, options.SystemContext.DockerAuthConfig.Password) @@ -325,7 +326,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } } - contextDir, err := filepath.Abs(options.ContextDirectory) + contextDir, err = filepath.Abs(options.ContextDirectory) if err != nil { logrus.Errorf("Cannot find absolute path of %v: %v", options.ContextDirectory, err) return nil, err @@ -556,16 +557,27 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { merr = multierror.Append(merr, err) return } - err = filepath.Walk(s, func(path string, info os.FileInfo, err error) error { if err != nil { return err } - if path == s { - return nil // skip root dir + // check if what we are given is an empty dir, if so then continue w/ it. Else return. + // if we are given a file or a symlink, we do not want to exclude it. + if info.IsDir() && s == path { + var p *os.File + p, err = os.Open(path) + if err != nil { + return err + } + defer p.Close() + _, err = p.Readdir(1) + if err != io.EOF { + return nil // non empty root dir, need to return + } else if err != nil { + logrus.Errorf("Error while reading directory %v: %v", path, err) + } } - name := filepath.ToSlash(strings.TrimPrefix(path, s+string(filepath.Separator))) excluded, err := pm.Matches(name) // nolint:staticcheck diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index c5903f037..096c98727 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -734,10 +734,11 @@ RUN ls /dev/test1`, ALPINE) err = os.Mkdir("relative", 0755) Expect(err).To(BeNil()) containerFilePath := filepath.Join("relative", "Containerfile") - fmt.Println(containerFilePath) + err = os.Mkdir("relative/build-root", 0755) + Expect(err).To(BeNil()) err = ioutil.WriteFile(containerFilePath, []byte(containerFile), 0755) Expect(err).To(BeNil()) - build := podmanTest.Podman([]string{"build", "-f", "./relative/Containerfile"}) + build := podmanTest.Podman([]string{"build", "-f", "./relative/Containerfile", "./relative/build-root"}) build.WaitWithDefaultTimeout() Expect(build).To(Exit(0)) err = os.RemoveAll("relative") -- cgit v1.2.3-54-g00ecf From 713ce2a96724e14a6a2acfcb93902826f0c4d2e2 Mon Sep 17 00:00:00 2001 From: Adam Maryniuk Date: Thu, 24 Mar 2022 12:33:05 -0400 Subject: clarifying "loginctl enable-linger" section in doc Signed-off-by: Adam Maryniuk --- docs/source/markdown/podman-generate-systemd.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index fdc9c21a5..9305a0fb6 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -222,7 +222,7 @@ To run the user services placed in `$HOME/.config/systemd/user` on first login o ``` $ systemctl --user enable <.service> ``` -The systemd user instance is killed after the last session for the user is closed. The systemd user instance can be kept running ever after the user logs out by enabling `lingering` using +The systemd user instance is killed after the last session for the user is closed. The systemd user instance can be started at boot and kept running even after the user logs out by enabling `lingering` using ``` $ loginctl enable-linger -- cgit v1.2.3-54-g00ecf From b362367efb7ecbe8aadeac3f5f4d5d17fe27862b Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Sat, 26 Mar 2022 06:39:11 -0400 Subject: Switch all calls to filepath.Walk to filepath.WalkDir WalkDir should be faster the Walk, since we often do not need to stat files. [NO NEW TESTS NEEDED] Existing tests should find errors. Signed-off-by: Daniel J Walsh --- libpod/container_internal.go | 11 ++--------- libpod/volume.go | 12 ++---------- pkg/bindings/images/build.go | 23 ++++++++++++++++++----- pkg/domain/infra/abi/system.go | 22 ++++------------------ pkg/machine/ignition.go | 5 +++-- pkg/machine/qemu/machine.go | 6 +++--- pkg/machine/wsl/machine.go | 7 ++++--- pkg/specgen/generate/config_linux.go | 5 +++-- pkg/util/utils.go | 18 ++++++++++++++++++ pkg/util/utils_linux.go | 9 +++++++-- test/e2e/pod_rm_test.go | 7 ++++--- 11 files changed, 68 insertions(+), 57 deletions(-) diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 0618cc02e..c2642f872 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -99,15 +99,8 @@ func (c *Container) rootFsSize() (int64, error) { // rwSize gets the size of the mutable top layer of the container. func (c *Container) rwSize() (int64, error) { if c.config.Rootfs != "" { - var size int64 - err := filepath.Walk(c.config.Rootfs, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - size += info.Size() - return nil - }) - return size, err + size, err := util.SizeOfPath(c.config.Rootfs) + return int64(size), err } container, err := c.runtime.store.Container(c.ID()) diff --git a/libpod/volume.go b/libpod/volume.go index d60d978ed..2b263f9fb 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -1,13 +1,12 @@ package libpod import ( - "os" - "path/filepath" "time" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/libpod/lock" "github.com/containers/podman/v4/libpod/plugin" + "github.com/containers/podman/v4/pkg/util" ) // Volume is a libpod named volume. @@ -93,14 +92,7 @@ func (v *Volume) Name() string { // Returns the size on disk of volume func (v *Volume) Size() (uint64, error) { - var size uint64 - err := filepath.Walk(v.config.MountPoint, func(path string, info os.FileInfo, err error) error { - if err == nil && !info.IsDir() { - size += (uint64)(info.Size()) - } - return err - }) - return size, err + return util.SizeOfPath(v.config.MountPoint) } // Driver retrieves the volume's driver. diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 1ed3f19de..73d0294e1 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "io/fs" "io/ioutil" "net/http" "net/url" @@ -557,14 +558,14 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { merr = multierror.Append(merr, err) return } - err = filepath.Walk(s, func(path string, info os.FileInfo, err error) error { + err = filepath.WalkDir(s, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } // check if what we are given is an empty dir, if so then continue w/ it. Else return. // if we are given a file or a symlink, we do not want to exclude it. - if info.IsDir() && s == path { + if d.IsDir() && s == path { var p *os.File p, err = os.Open(path) if err != nil { @@ -588,7 +589,11 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { return nil } - if info.Mode().IsRegular() { // add file item + if d.Type().IsRegular() { // add file item + info, err := d.Info() + if err != nil { + return err + } di, isHardLink := checkHardLink(info) if err != nil { return err @@ -624,7 +629,11 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { seen[di] = name } return err - } else if info.Mode().IsDir() { // add folders + } else if d.IsDir() { // add folders + info, err := d.Info() + if err != nil { + return err + } hdr, lerr := tar.FileInfoHeader(info, name) if lerr != nil { return lerr @@ -634,11 +643,15 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { if lerr := tw.WriteHeader(hdr); lerr != nil { return lerr } - } else if info.Mode()&os.ModeSymlink != 0 { // add symlinks as it, not content + } else if d.Type()&os.ModeSymlink != 0 { // add symlinks as it, not content link, err := os.Readlink(path) if err != nil { return err } + info, err := d.Info() + if err != nil { + return err + } hdr, lerr := tar.FileInfoHeader(info, link) if lerr != nil { return lerr diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index d12d14c1f..4361821d5 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -6,7 +6,6 @@ import ( "net/url" "os" "os/exec" - "path/filepath" "github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/config" @@ -269,7 +268,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System } dfVolumes := make([]*entities.SystemDfVolumeReport, 0, len(vols)) - var reclaimableSize int64 + var reclaimableSize uint64 for _, v := range vols { var consInUse int mountPoint, err := v.MountPoint() @@ -282,7 +281,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System // TODO: fix this. continue } - volSize, err := sizeOfPath(mountPoint) + volSize, err := util.SizeOfPath(mountPoint) if err != nil { return nil, err } @@ -301,8 +300,8 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System report := entities.SystemDfVolumeReport{ VolumeName: v.Name(), Links: consInUse, - Size: volSize, - ReclaimableSize: reclaimableSize, + Size: int64(volSize), + ReclaimableSize: int64(reclaimableSize), } dfVolumes = append(dfVolumes, &report) } @@ -313,19 +312,6 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System }, nil } -// sizeOfPath determines the file usage of a given path. it was called volumeSize in v1 -// and now is made to be generic and take a path instead of a libpod volume -func sizeOfPath(path string) (int64, error) { - var size int64 - err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { - if err == nil && !info.IsDir() { - size += info.Size() - } - return err - }) - return size, err -} - func (se *SystemEngine) Reset(ctx context.Context) error { return se.Libpod.Reset(ctx) } diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index b2dabb689..fe47437e3 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -6,6 +6,7 @@ package machine import ( "encoding/json" "fmt" + "io/fs" "io/ioutil" "net/url" "os" @@ -507,8 +508,8 @@ func getCerts(certsDir string, isDir bool) []File { ) if isDir { - err := filepath.Walk(certsDir, func(path string, info os.FileInfo, err error) error { - if err == nil && !info.IsDir() { + err := filepath.WalkDir(certsDir, func(path string, d fs.DirEntry, err error) error { + if err == nil && !d.IsDir() { certPath, err := filepath.Rel(certsDir, path) if err != nil { logrus.Warnf("%s", err) diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 79f75c144..9ada26b9a 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -891,10 +891,10 @@ func GetVMInfos() ([]*machine.ListResponse, error) { var listed []*machine.ListResponse - if err = filepath.Walk(vmConfigDir, func(path string, info os.FileInfo, err error) error { + if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { vm := new(MachineVM) - if strings.HasSuffix(info.Name(), ".json") { - fullPath := filepath.Join(vmConfigDir, info.Name()) + if strings.HasSuffix(d.Name(), ".json") { + fullPath := filepath.Join(vmConfigDir, d.Name()) b, err := ioutil.ReadFile(fullPath) if err != nil { return err diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 5b0c757f0..5128fa313 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "io/fs" "io/ioutil" "net/url" "os" @@ -1175,10 +1176,10 @@ func GetVMInfos() ([]*machine.ListResponse, error) { var listed []*machine.ListResponse - if err = filepath.Walk(vmConfigDir, func(path string, info os.FileInfo, err error) error { + if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { vm := new(MachineVM) - if strings.HasSuffix(info.Name(), ".json") { - fullPath := filepath.Join(vmConfigDir, info.Name()) + if strings.HasSuffix(d.Name(), ".json") { + fullPath := filepath.Join(vmConfigDir, d.Name()) b, err := ioutil.ReadFile(fullPath) if err != nil { return err diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index a5772bc6a..449d8e4e0 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -2,6 +2,7 @@ package generate import ( "fmt" + "io/fs" "io/ioutil" "os" "path" @@ -101,8 +102,8 @@ func DevicesFromPath(g *generate.Generator, devicePath string) error { } // mount the internal devices recursively - if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error { - if f.Mode()&os.ModeDevice == os.ModeDevice { + if err := filepath.WalkDir(resolvedDevicePath, func(dpath string, d fs.DirEntry, e error) error { + if d.Type()&os.ModeDevice == os.ModeDevice { found = true device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src))) if devmode != "" { diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 925ff9830..be2ed2d02 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -3,6 +3,7 @@ package util import ( "encoding/json" "fmt" + "io/fs" "math" "os" "os/user" @@ -731,3 +732,20 @@ func LookupUser(name string) (*user.User, error) { } return user.Lookup(name) } + +// SizeOfPath determines the file usage of a given path. it was called volumeSize in v1 +// and now is made to be generic and take a path instead of a libpod volume +func SizeOfPath(path string) (uint64, error) { + var size uint64 + err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { + if err == nil && !d.IsDir() { + info, err := d.Info() + if err != nil { + return err + } + size += uint64(info.Size()) + } + return err + }) + return size, err +} diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go index 288137ca5..dcfda4e1a 100644 --- a/pkg/util/utils_linux.go +++ b/pkg/util/utils_linux.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "io/fs" "os" "path/filepath" "syscall" @@ -23,17 +24,21 @@ func GetContainerPidInformationDescriptors() ([]string, error) { // Symlinks to nodes are ignored. func FindDeviceNodes() (map[string]string, error) { nodes := make(map[string]string) - err := filepath.Walk("/dev", func(path string, info os.FileInfo, err error) error { + err := filepath.WalkDir("/dev", func(path string, d fs.DirEntry, err error) error { if err != nil { logrus.Warnf("Error descending into path %s: %v", path, err) return filepath.SkipDir } // If we aren't a device node, do nothing. - if info.Mode()&(os.ModeDevice|os.ModeCharDevice) == 0 { + if d.Type()&(os.ModeDevice|os.ModeCharDevice) == 0 { return nil } + info, err := d.Info() + if err != nil { + return err + } // We are a device node. Get major/minor. sysstat, ok := info.Sys().(*syscall.Stat_t) if !ok { diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 7a0d97d28..dbb2d6d13 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -2,6 +2,7 @@ package integration import ( "fmt" + "io/fs" "io/ioutil" "os" "path/filepath" @@ -46,14 +47,14 @@ var _ = Describe("Podman pod rm", func() { Expect(result).Should(Exit(0)) // Also check that we don't leak cgroups - err := filepath.Walk("/sys/fs/cgroup", func(path string, info os.FileInfo, err error) error { + err := filepath.WalkDir("/sys/fs/cgroup", func(path string, d fs.DirEntry, err error) error { if err != nil { return err } - if !info.IsDir() { + if !d.IsDir() { Expect(err).To(BeNil()) } - if strings.Contains(info.Name(), podid) { + if strings.Contains(d.Name(), podid) { return fmt.Errorf("leaking cgroup path %s", path) } return nil -- cgit v1.2.3-54-g00ecf From a4203bd66cf802449cf9dda9498efa83fa0302e8 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 23 Feb 2022 14:18:12 -0500 Subject: Set systemd mode if entrypoint begins with /bin/sh -c Fixes: https://github.com/containers/podman/issues/13324 Signed-off-by: Daniel J Walsh --- pkg/specgen/generate/container_create.go | 11 ++++++++++- test/e2e/systemd_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index a7a7353d0..28c31c7eb 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -267,7 +267,16 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. "/usr/sbin/init": true, "/usr/local/sbin/init": true, } - if useSystemdCommands[command[0]] || (filepath.Base(command[0]) == "systemd") { + // Grab last command incase this is launched from a shell + cmd := command + if len(command) > 2 { + // Podman build will add "/bin/sh" "-c" to + // Entrypoint. Remove and search for systemd + if command[0] == "/bin/sh" && command[1] == "-c" { + cmd = command[2:] + } + } + if useSystemdCommands[cmd[0]] || (filepath.Base(cmd[0]) == "systemd") { useSystemd = true } } diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index f8d8db592..57fc323ce 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -1,8 +1,10 @@ package integration import ( + "fmt" "io/ioutil" "os" + "path/filepath" "strings" . "github.com/containers/podman/v4/test/utils" @@ -130,6 +132,31 @@ WantedBy=default.target Expect(conData[0].Config.SystemdMode).To(BeTrue()) }) + It("podman systemd in command triggers systemd mode", func() { + containerfile := fmt.Sprintf(`FROM %s +RUN mkdir -p /usr/lib/systemd/; touch /usr/lib/systemd/systemd +CMD /usr/lib/systemd/systemd`, ALPINE) + + containerfilePath := filepath.Join(podmanTest.TempDir, "Containerfile") + err := ioutil.WriteFile(containerfilePath, []byte(containerfile), 0755) + Expect(err).To(BeNil()) + session := podmanTest.Podman([]string{"build", "-t", "systemd", "--file", containerfilePath, podmanTest.TempDir}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + ctrName := "testCtr" + run := podmanTest.Podman([]string{"create", "--name", ctrName, "systemd"}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + + result := podmanTest.Podman([]string{"inspect", ctrName}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + conData := result.InspectContainerToJSON() + Expect(conData).To(HaveLen(1)) + Expect(conData[0].Config.SystemdMode).To(BeTrue()) + }) + It("podman create container with --uidmap and conmon PidFile accessible", func() { ctrName := "testCtrUidMap" run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) -- cgit v1.2.3-54-g00ecf From c1d9851a4f290b641a092a0f46af898c75587f0f Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 28 Mar 2022 20:55:29 +0200 Subject: upgrade tests: fix networking problems With podman4 we support netavark, however old versions will still use cni. Since netavark and cni can conflict we should not mix them. Remove the network setup from the inital podman command and create the directories manually to prevent such conflicts. Also the update to 4.0 changes the network db structure. While it is compatible from 3.X to 4.0 it will fail the other way around. In this test it will happen because the cleanup process still uses the old podman while the network connect/disconnect test already changed the db format. Therefore the cleanup process cannot see any networks and will not tear it down. The following start will fail because the ip address is already assigned. Fixes #13679 Signed-off-by: Paul Holzinger --- test/upgrade/test-upgrade.bats | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/test/upgrade/test-upgrade.bats b/test/upgrade/test-upgrade.bats index 58e4fe0a3..063f4d624 100644 --- a/test/upgrade/test-upgrade.bats +++ b/test/upgrade/test-upgrade.bats @@ -105,7 +105,7 @@ podman \$opts run -d --name myrunningcontainer --label mylabel=$LABEL_RUNNING \ podman \$opts pod create --name mypod -podman \$opts network create mynetwork +podman \$opts network create --disable-dns mynetwork echo READY while :;do @@ -113,7 +113,10 @@ while :;do echo STOPPING podman \$opts stop -t 0 myrunningcontainer || true podman \$opts rm -f myrunningcontainer || true - podman \$opts network rm -f mynetwork + # sigh, network rm fails with exec: "ip": executable file not found in $PATH + # we cannot change the images afterwards so we remove it manually (#11403) + # hardcode /etc/cni/net.d dir for now + podman \$opts network rm -f mynetwork || rm -f /etc/cni/net.d/mynetwork.conflist exit 0 fi sleep 0.5 @@ -124,17 +127,14 @@ EOF # Clean up vestiges of previous run $PODMAN rm -f podman_parent || 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 + # Also use --network host to prevent any netavark/cni conflicts + $PODMAN run --rm --network host $OLD_PODMAN true # Podman 4.0 might no longer use cni so /run/cni and /run/containers will no be created in this case - mkdir -p /run/cni /run/containers + # Create directories manually to fix this. Also running with netavark can + # cause connectivity issues since cni and netavark should never be mixed. + mkdir -p /run/netns /run/cni /run/containers /var/lib/cni /etc/cni/net.d # @@ -242,6 +242,8 @@ failed | exited | 17 # if we can connect on an existing running container @test "network - connect" { skip_if_version_older 2.2.0 + touch $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test + run_podman network connect mynetwork myrunningcontainer run_podman network disconnect podman myrunningcontainer run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt @@ -250,7 +252,26 @@ failed | exited | 17 @test "network - restart" { # restart the container and check if we can still use the port + + # https://github.com/containers/podman/issues/13679 + # The upgrade to podman4 changes the network db format. + # While it is compatible from 3.X to 4.0 it will fail the other way around. + # This can be the case when the cleanup process runs before the stop process + # can do the cleanup. + + # Since there is no easy way to fix this and downgrading is not something + # we support, just fix this bug in the tests by manually calling + # network disconnect to teardown the netns. + if test -e $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test; then + run_podman network disconnect mynetwork myrunningcontainer + fi + run_podman stop -t0 myrunningcontainer + + # now connect again, do this before starting the container + if test -e $PODMAN_UPGRADE_WORKDIR/ran-network-connect-test; then + run_podman network connect mynetwork myrunningcontainer + fi run_podman start myrunningcontainer run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt is "$output" "$RANDOM_STRING_1" "curl on restarted container" -- cgit v1.2.3-54-g00ecf From 197b8ad77bbcfd79fa76b38c5466373c165fc927 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 30 Mar 2022 12:59:49 +0200 Subject: rootless netns: move process to scope only with systemd When you run podman on a non systemd system we should not try to move the process under a new systemd scope. [NO NEW TESTS NEEDED] Ref #13703 Signed-off-by: Paul Holzinger --- libpod/networking_linux.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 29b9941fe..eb264379f 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -496,10 +496,13 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, err } - // move to systemd scope to prevent systemd from killing it - err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid) - if err != nil { - logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) + if utils.RunsOnSystemd() { + // move to systemd scope to prevent systemd from killing it + err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid) + if err != nil { + // only log this, it is not fatal but can lead to issues when running podman inside systemd units + logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) + } } // build a new resolv.conf file which uses the slirp4netns dns server address -- cgit v1.2.3-54-g00ecf From 997b57dccf82beb566366b81ac4242c88ef116ef Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 30 Mar 2022 15:49:04 +0200 Subject: systemd: enable all cgroups when running as a service enable cgroup delegation when running as a systemd service so all the available controllers are correctly detected. Closes: https://github.com/containers/podman/issues/13710 Signed-off-by: Giuseppe Scrivano --- contrib/systemd/system/podman.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/systemd/system/podman.service.in b/contrib/systemd/system/podman.service.in index 9a7e04fd4..c1a5952b5 100644 --- a/contrib/systemd/system/podman.service.in +++ b/contrib/systemd/system/podman.service.in @@ -6,6 +6,7 @@ Documentation=man:podman-system-service(1) StartLimitIntervalSec=0 [Service] +Delegate=true Type=exec KillMode=process Environment=LOGGING="--log-level=info" -- cgit v1.2.3-54-g00ecf From 5949238e58f3d3cf6cbc4f1f63a5a7f3a6b17799 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 30 Mar 2022 15:00:24 -0400 Subject: Update release notes for v4.0.3 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 4fcdf406d..30a7515b8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,45 @@ # Release Notes +## 4.0.3 +### Security +- This release fixes CVE-2022-27649, where containers run by Podman would have excess inheritable capabilities set. + +### Changes +- The `podman machine rm --force` command will now remove running machines as well (such machines are shut down first, then removed) ([#13448](https://github.com/containers/podman/issues/13448)). +- When a `podman machine` VM is started that is using a too-old VM image, it will now start in a reduced functionality mode, and provide instructions on how to recreate it (previously, VMs were effectively unusable) ([#13510](https://github.com/containers/podman/issues/13510)). + +### Bugfixes +- Fixed a bug where devices added to containers by the `--device` option to `podman run` and `podman create` would not be accessible within the container. +- Fixed a bug where Podman would refuse to create containers when the working directory in the container was a symlink ([#13346](https://github.com/containers/podman/issues/13346)). +- Fixed a bug where pods would be created with cgroups even if cgroups were disabled in `containers.conf` ([#13411](https://github.com/containers/podman/issues/13411)). +- Fixed a bug where the `podman play kube` command would produce confusing errors if invalid YAML with duplicated container named was passed ([#13332](https://github.com/containers/podman/issues/13332)). +- Fixed a bug where the `podman machine rm` command would not remove the Podman API socket on the host that was associated with the VM. +- Fixed a bug where the remote Podman client was unable to properly resize the TTYs of containers on non-Linux OSes. +- Fixed a bug where rootless Podman could hang indefinitely when starting containers on systems with IPv6 disabled ([#13388](https://github.com/containers/podman/issues/13388)). +- Fixed a bug where the `podman version` command could sometimes print excess blank lines as part of its output. +- Fixed a bug where the `podman generate systemd` command would sometimes generate systemd services with names beginning with a hyphen ([#13272](https://github.com/containers/podman/issues/13272)). +- Fixed a bug where locally building the pause image could fail if the current directory contained a `.dockerignore` file ([#13529](https://github.com/containers/podman/issues/13529)). +- Fixed a bug where root containers in VMs created by `podman machine` could not bind ports to specific IPs on the host ([#13543](https://github.com/containers/podman/issues/13543)). +- Fixed a bug where the storage utilization percentages displayed by `podman system df` were incorrect ([#13516](https://github.com/containers/podman/issues/13516)). +- Fixed a bug where the CPU utilization percentages displayed by `podman stats` were incorrect ([#13597](https://github.com/containers/podman/pull/13597)). +- Fixed a bug where containers created with the `--no-healthcheck` option would still display healthcheck status in `podman inspect` ([#13578](https://github.com/containers/podman/issues/13578)). +- Fixed a bug where the `podman pod rm` command could print a warning about a missing cgroup ([#13382](https://github.com/containers/podman/issues/13382)). +- Fixed a bug where the `podman exec` command could sometimes print a `timed out waiting for file` error after the process in the container exited ([#13227](https://github.com/containers/podman/issues/13227)). +- Fixed a bug where virtual machines created by `podman machine` were not tolerant of changes to the path to the qemu binary on the host ([#13394](https://github.com/containers/podman/issues/13394)). +- Fixed a bug where the remote Podman client's `podman build` command did not properly handle the context directory if a Containerfile was manually specified using `-f` ([#13293](https://github.com/containers/podman/issues/13293)). +- Fixed a bug where Podman would not properly detect the use of `systemd` as PID 1 in a container when the entrypoint was prefixed with `/bin/sh -c` ([#13324](https://github.com/containers/podman/issues/13324)). +- Fixed a bug where rootless Podman could, on systems that do not use `systemd` as init, print a warning message about the rootless network namespace ([#13703](https://github.com/containers/podman/issues/13703)). +- Fixed a bug where the default systemd unit file for `podman system service` did not delegate all cgroup controllers, resulting in `podman info` queries against the remote API returning incorrect cgroup controllers ([#13710](https://github.com/containers/podman/issues/13710)). + +### API +- Fixed a bug where the Compat Create API for containers did not properly handle permissions for tmpfs mounts ([#13108](https://github.com/containers/podman/issues/13108)). + +### Misc +- The static binary for Linux is now built with CGo disabled to avoid panics due to a Golang bug ([#13557](https://github.com/containers/podman/issues/13557)). +- Updated the containers/storage library to 1.38.3 +- Updated the containers/image library to 5.19.2 +- Updated the containers/common library to 0.47.5 + ## 4.0.2 ### Bugfixes - Revert "use GetRuntimeDir() from c/common" -- cgit v1.2.3-54-g00ecf From df6082fd6f2fa7f91a5263fa089e1294e813bb40 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 24 Mar 2022 15:35:16 +0100 Subject: fix slirp4netns port forwarding with ranges The slirp4netns port forwarder was not updated to make use of the new port format. This results in a problem when port ranges are used since it does not read the range field from the port. Update the logic to iterate through all ports with the range and protocols. Also added a system test for port ranges with slirp4netns, rootlesskit and the bridge network mode. Fixes #13643 [Fixed merge conflict] Signed-off-by: Paul Holzinger --- libpod/networking_slirp4netns.go | 107 ++++++++++++++++++++++----------------- test/system/500-networking.bats | 21 ++++++++ 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index cc44f78f7..e5cc95cda 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -614,60 +614,73 @@ func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd // for each port we want to add we need to open a connection to the slirp4netns control socket // and send the add_hostfwd command. - for _, i := range ctr.convertPortMappings() { - conn, err := net.Dial("unix", apiSocket) - if err != nil { - return errors.Wrapf(err, "cannot open connection to %s", apiSocket) - } - defer func() { - if err := conn.Close(); err != nil { - logrus.Errorf("Unable to close connection: %q", err) + for _, port := range ctr.convertPortMappings() { + protocols := strings.Split(port.Protocol, ",") + for _, protocol := range protocols { + hostIP := port.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + for i := uint16(0); i < port.Range; i++ { + if err := openSlirp4netnsPort(apiSocket, protocol, hostIP, port.HostPort+i, port.ContainerPort+i); err != nil { + return err + } } - }() - hostIP := i.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - apiCmd := slirp4netnsCmd{ - Execute: "add_hostfwd", - Args: slirp4netnsCmdArg{ - Proto: i.Protocol, - HostAddr: hostIP, - HostPort: i.HostPort, - GuestPort: i.ContainerPort, - }, - } - // create the JSON payload and send it. Mark the end of request shutting down writes - // to the socket, as requested by slirp4netns. - data, err := json.Marshal(&apiCmd) - if err != nil { - return errors.Wrapf(err, "cannot marshal JSON for slirp4netns") - } - if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil { - return errors.Wrapf(err, "cannot write to control socket %s", apiSocket) - } - if err := conn.(*net.UnixConn).CloseWrite(); err != nil { - return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket) - } - buf := make([]byte, 2048) - readLength, err := conn.Read(buf) - if err != nil { - return errors.Wrapf(err, "cannot read from control socket %s", apiSocket) - } - // if there is no 'error' key in the received JSON data, then the operation was - // successful. - var y map[string]interface{} - if err := json.Unmarshal(buf[0:readLength], &y); err != nil { - return errors.Wrapf(err, "error parsing error status from slirp4netns") - } - if e, found := y["error"]; found { - return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e) } } logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready") return nil } +// openSlirp4netnsPort sends the slirp4netns pai quey to the given socket +func openSlirp4netnsPort(apiSocket, proto, hostip string, hostport, guestport uint16) error { + conn, err := net.Dial("unix", apiSocket) + if err != nil { + return errors.Wrapf(err, "cannot open connection to %s", apiSocket) + } + defer func() { + if err := conn.Close(); err != nil { + logrus.Errorf("Unable to close slirp4netns connection: %q", err) + } + }() + apiCmd := slirp4netnsCmd{ + Execute: "add_hostfwd", + Args: slirp4netnsCmdArg{ + Proto: proto, + HostAddr: hostip, + HostPort: hostport, + GuestPort: guestport, + }, + } + // create the JSON payload and send it. Mark the end of request shutting down writes + // to the socket, as requested by slirp4netns. + data, err := json.Marshal(&apiCmd) + if err != nil { + return errors.Wrapf(err, "cannot marshal JSON for slirp4netns") + } + if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil { + return errors.Wrapf(err, "cannot write to control socket %s", apiSocket) + } + if err := conn.(*net.UnixConn).CloseWrite(); err != nil { + return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket) + } + buf := make([]byte, 2048) + readLength, err := conn.Read(buf) + if err != nil { + return errors.Wrapf(err, "cannot read from control socket %s", apiSocket) + } + // if there is no 'error' key in the received JSON data, then the operation was + // successful. + var y map[string]interface{} + if err := json.Unmarshal(buf[0:readLength], &y); err != nil { + return errors.Wrapf(err, "error parsing error status from slirp4netns") + } + if e, found := y["error"]; found { + return errors.Errorf("from slirp4netns while setting up port redirection: %v", e) + } + return nil +} + func getRootlessPortChildIP(c *Container, netStatus map[string]types.StatusBlock) string { if c.config.NetMode.IsSlirp4netns() { slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet) diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 4b1a22981..ae830291f 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -632,4 +632,25 @@ EOF is "$output" ".*nameserver $subnet.1.*" "integrated dns nameserver is set" } +@test "podman run port forward range" { + for netmode in bridge slirp4netns:port_handler=slirp4netns slirp4netns:port_handler=rootlesskit; do + local port=$(random_free_port) + local end_port=$(( $port + 2 )) + local range="$port-$end_port:$port-$end_port" + local random=$(random_string) + + run_podman run --network $netmode -p "$range" -d $IMAGE sleep inf + cid="$output" + for port in $(seq $port $end_port); do + run_podman exec -d $cid nc -l -p $port -e /bin/cat + # -w 1 adds a 1 second timeout, for some reason ubuntus ncat doesn't close the connection on EOF, + # other options to change this are not portable across distros but -w seems to work + run nc -w 1 127.0.0.1 $port <<<$random + is "$output" "$random" "ncat got data back (netmode=$netmode port=$port)" + done + + run_podman rm -f -t0 $cid + done +} + # vim: filetype=sh -- cgit v1.2.3-54-g00ecf From d30a1e9d087db9efd287e707eb41b88bfafad822 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 30 Mar 2022 15:54:50 -0400 Subject: Update release notes Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 30a7515b8..67759f84f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -30,6 +30,7 @@ - Fixed a bug where Podman would not properly detect the use of `systemd` as PID 1 in a container when the entrypoint was prefixed with `/bin/sh -c` ([#13324](https://github.com/containers/podman/issues/13324)). - Fixed a bug where rootless Podman could, on systems that do not use `systemd` as init, print a warning message about the rootless network namespace ([#13703](https://github.com/containers/podman/issues/13703)). - Fixed a bug where the default systemd unit file for `podman system service` did not delegate all cgroup controllers, resulting in `podman info` queries against the remote API returning incorrect cgroup controllers ([#13710](https://github.com/containers/podman/issues/13710)). +- Fixed a bug where the `slirp4netns` port forwarder for rootless Podman would only publish the first port of a range ([#13643](https://github.com/containers/podman/issues/13643)). ### API - Fixed a bug where the Compat Create API for containers did not properly handle permissions for tmpfs mounts ([#13108](https://github.com/containers/podman/issues/13108)). -- cgit v1.2.3-54-g00ecf