diff options
-rw-r--r-- | docs/source/markdown/podman-generate-systemd.1.md | 2 | ||||
-rw-r--r-- | libpod/container_copy_linux.go | 25 | ||||
-rw-r--r-- | libpod/container_inspect.go | 23 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 15 | ||||
-rw-r--r-- | test/apiv2/26-containersWait.at | 4 | ||||
-rw-r--r-- | test/apiv2/python/rest_api/test_v2_0_0_container.py | 2 | ||||
-rw-r--r-- | test/e2e/cp_test.go | 44 | ||||
-rw-r--r-- | test/system/065-cp.bats | 16 |
9 files changed, 115 insertions, 18 deletions
diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index 4b77c5721..3565a42bc 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -10,7 +10,7 @@ podman\-generate\-systemd - Generate systemd unit file(s) for a container or pod **podman generate systemd** will create a systemd unit file that can be used to control a container or pod. By default, the command will print the content of the unit files to stdout. -_Note: If you use this command with the remote client, you would still have to place the generated units on the remote system._ +_Note: If you use this command with the remote client, you would still have to place the generated units on the remote system. Moreover, please make sure that the XDG_RUNTIME_DIR environment variable is set. If unset, you may set it via `export XDG_RUNTIME_DIR=/run/user/$(id -u)`._ ## OPTIONS diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index 5c275c641..0ab322829 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -237,21 +237,32 @@ func (c *Container) joinMountAndExec(ctx context.Context, f func() error) error } defer mountFD.Close() - pidFD, err := getFD(PIDNS) + inHostPidNS, err := c.inHostPidNS() if err != nil { - errChan <- err + errChan <- errors.Wrap(err, "checking inHostPidNS") return } - defer pidFD.Close() - if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { - errChan <- err - return + var pidFD *os.File + if !inHostPidNS { + pidFD, err = getFD(PIDNS) + if err != nil { + errChan <- err + return + } + defer pidFD.Close() } - if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil { + + if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { errChan <- err return } + if pidFD != nil { + if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil { + errChan <- err + return + } + } if err := unix.Setns(int(mountFD.Fd()), unix.CLONE_NEWNS); err != nil { errChan <- err return diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 4210bc581..638e0b756 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -892,3 +892,26 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named return hostConfig, nil } + +// Return true if the container is running in the host's PID NS. +func (c *Container) inHostPidNS() (bool, error) { + if c.config.PIDNsCtr != "" { + return false, nil + } + ctrSpec, err := c.specFromState() + if err != nil { + return false, err + } + if ctrSpec.Linux != nil { + // Locate the spec's PID namespace. + // If there is none, it's pid=host. + // If there is one and it has a path, it's "ns:". + // If there is no path, it's default - the empty string. + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == spec.PIDNamespace { + return false, nil + } + } + } + return true, nil +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 52d7633af..2ffd9b0cb 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -98,7 +98,7 @@ type BuildResult struct { type ContainerWaitOKBody struct { StatusCode int - Error struct { + Error *struct { Message string } } diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index 6c708f74e..fb1f8b7c1 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -75,18 +75,19 @@ func WaitContainerDocker(w http.ResponseWriter, r *http.Request) { } exitCode, err := waitDockerCondition(ctx, name, interval, condition) - msg := "" + var errStruct *struct{ Message string } if err != nil { logrus.Errorf("error while waiting on condition: %q", err) - msg = err.Error() + errStruct = &struct { + Message string + }{ + Message: err.Error(), + } } + responseData := handlers.ContainerWaitOKBody{ StatusCode: int(exitCode), - Error: struct { - Message string - }{ - Message: msg, - }, + Error: errStruct, } enc := json.NewEncoder(w) enc.SetEscapeHTML(true) diff --git a/test/apiv2/26-containersWait.at b/test/apiv2/26-containersWait.at index ec16c35df..55bcd4592 100644 --- a/test/apiv2/26-containersWait.at +++ b/test/apiv2/26-containersWait.at @@ -21,7 +21,9 @@ t POST "containers/${CTR}/wait?condition=non-existent-cond" 400 t POST "containers/${CTR}/wait?condition=not-running" 200 -t POST "containers/${CTR}/wait?condition=next-exit" 200 & +t POST "containers/${CTR}/wait?condition=next-exit" 200 \ + .StatusCode=0 \ + .Error=null & child_pid=$! podman start "${CTR}" wait "${child_pid}" diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index 70c07d47f..ad096ed38 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -99,7 +99,7 @@ class ContainerTestCase(APITestCase): r = requests.post(self.podman_url + f"/v1.40/containers/{create['Id']}/wait") self.assertEqual(r.status_code, 200, r.text) wait = r.json() - self.assertEqual(wait["StatusCode"], 0, wait["Error"]["Message"]) + self.assertEqual(wait["StatusCode"], 0, wait["Error"]) prune = requests.post(self.podman_url + "/v1.40/containers/prune") self.assertEqual(prune.status_code, 200, prune.status_code) diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index c0fb3f887..be4901833 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -91,6 +91,50 @@ var _ = Describe("Podman cp", func() { Expect(roundtripContent).To(Equal(originalContent)) }) + // Copy a file to the container, then back to the host in --pid=host + It("podman cp --pid=host file", func() { + SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") + srcFile, err := ioutil.TempFile("", "") + Expect(err).To(BeNil()) + defer srcFile.Close() + defer os.Remove(srcFile.Name()) + + originalContent := []byte("podman cp file test") + err = ioutil.WriteFile(srcFile.Name(), originalContent, 0644) + Expect(err).To(BeNil()) + + // Create a container. NOTE that container mustn't be running for copying. + session := podmanTest.Podman([]string{"create", "--pid=host", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + name := session.OutputToString() + + session = podmanTest.Podman([]string{"start", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // The file will now be created (and written to). + session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Copy FROM the container. + + destFile, err := ioutil.TempFile("", "") + Expect(err).To(BeNil()) + defer destFile.Close() + defer os.Remove(destFile.Name()) + + session = podmanTest.Podman([]string{"cp", name + ":foo", destFile.Name()}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + // Now make sure the content matches. + roundtripContent, err := ioutil.ReadFile(destFile.Name()) + Expect(err).To(BeNil()) + Expect(roundtripContent).To(Equal(originalContent)) + }) + // Create a symlink in the container, use it as a copy destination and // make sure that the link and the resolved path are accessible and // give the right content. diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 679cdc209..24ac8118e 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -130,6 +130,22 @@ load helpers } +@test "podman cp file from/to host while --pid=host" { + if is_rootless && ! is_cgroupsv2; then + skip "'podman cp --pid=host' (rootless) only works with cgroups v2" + fi + + srcdir=$PODMAN_TMPDIR/cp-pid-equals-host + mkdir -p $srcdir + touch $srcdir/hostfile + + run_podman run --pid=host -d --name cpcontainer $IMAGE sleep infinity + run_podman cp $srcdir/hostfile cpcontainer:/tmp/hostfile + run_podman cp cpcontainer:/tmp/hostfile $srcdir/hostfile1 + run_podman kill cpcontainer + run_podman rm -f cpcontainer +} + @test "podman cp file from container to host" { srcdir=$PODMAN_TMPDIR/cp-test-file-ctr-to-host mkdir -p $srcdir |