From 867669374c3fdd39f2629e53cbe7430f1bc3e085 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <rishi@fedoraproject.org>
Date: Tue, 8 Jan 2019 12:53:50 +0100
Subject: [PATCH] Add a --workdir option to 'podman exec'

Signed-off-by: Debarshi Ray <rishi@fedoraproject.org>
---
 cmd/podman/common.go    |  9 +++++----
 cmd/podman/exec.go      |  3 ++-
 completions/bash/podman |  2 ++
 docs/podman-exec.1.md   |  8 ++++++++
 libpod/container_api.go |  4 ++--
 libpod/oci.go           |  6 ++++--
 test/e2e/exec_test.go   | 32 ++++++++++++++++++++++++++++++++
 7 files changed, 55 insertions(+), 9 deletions(-)

diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 0fc9a6accfd..d934c869946 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -28,6 +28,10 @@ var (
 		Name:  "latest, l",
 		Usage: "act on the latest pod podman is aware of",
 	}
+	WorkDirFlag = cli.StringFlag{
+		Name:  "workdir, w",
+		Usage: "Working directory inside the container",
+	}
 )
 
 const (
@@ -522,10 +526,7 @@ var createFlags = []cli.Flag{
 		Name:  "volumes-from",
 		Usage: "Mount volumes from the specified container(s) (default [])",
 	},
-	cli.StringFlag{
-		Name:  "workdir, w",
-		Usage: "Working `directory inside the container",
-	},
+	WorkDirFlag,
 }
 
 func getFormat(c *cli.Context) (string, error) {
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index c03834dea23..073e72e6404 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -34,6 +34,7 @@ var (
 			Usage: "Sets the username or UID used and optionally the groupname or GID for the specified command",
 		},
 		LatestFlag,
+		WorkDirFlag,
 	}
 	execDescription = `
 	podman exec
@@ -108,5 +109,5 @@ func execCmd(c *cli.Context) error {
 		envs = append(envs, fmt.Sprintf("%s=%s", k, v))
 	}
 
-	return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"))
+	return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir"))
 }
diff --git a/completions/bash/podman b/completions/bash/podman
index d65f54690e3..e23615d5256 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1111,6 +1111,8 @@ _podman_exec() {
     --env
     --user
     -u
+    --workdir
+    -w
      "
     local boolean_options="
     --latest
diff --git a/docs/podman-exec.1.md b/docs/podman-exec.1.md
index 284fa5a4a29..77317b0cabd 100644
--- a/docs/podman-exec.1.md
+++ b/docs/podman-exec.1.md
@@ -38,6 +38,14 @@ Sets the username or UID used and optionally the groupname or GID for the specif
 The following examples are all valid:
 --user [user | user:group | uid | uid:gid | user:gid | uid:group ]
 
+**--workdir**, **-w**=""
+
+Working directory inside the container
+
+The default working directory for running binaries within a container is the root directory (/).
+The image developer can set a different default with the WORKDIR instruction, which can be overridden
+when creating the container.
+
 ## SEE ALSO
 podman(1), podman-run(1)
 
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 09bc46905ae..4eaf737b09a 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -262,7 +262,7 @@ func (c *Container) Kill(signal uint) error {
 // Exec starts a new process inside the container
 // TODO allow specifying streams to attach to
 // TODO investigate allowing exec without attaching
-func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) error {
+func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string) error {
 	var capList []string
 
 	locked := false
@@ -324,7 +324,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
 
 	logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID)
 
-	execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, hostUser, sessionID)
+	execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID)
 	if err != nil {
 		return errors.Wrapf(err, "error exec %s", c.ID())
 	}
diff --git a/libpod/oci.go b/libpod/oci.go
index 093bfdd3573..31c1a7e8514 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -728,7 +728,7 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
 // TODO: Add --detach support
 // TODO: Convert to use conmon
 // TODO: add --pid-file and use that to generate exec session tracking
-func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, user, sessionID string) (*exec.Cmd, error) {
+func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string) (*exec.Cmd, error) {
 	if len(cmd) == 0 {
 		return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
 	}
@@ -749,7 +749,9 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
 
 	args = append(args, "exec")
 
-	args = append(args, "--cwd", c.config.Spec.Process.Cwd)
+	if cwd != "" {
+		args = append(args, "--cwd", cwd)
+	}
 
 	args = append(args, "--pid-file", c.execPidPath(sessionID))
 
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index fec80717fa2..a181501a5ff 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -127,4 +127,36 @@ var _ = Describe("Podman exec", func() {
 		Expect(session2.ExitCode()).To(Equal(0))
 		Expect(session2.OutputToString()).To(Equal(testUser))
 	})
+
+	It("podman exec simple working directory test", func() {
+		setup := podmanTest.RunTopContainer("test1")
+		setup.WaitWithDefaultTimeout()
+		Expect(setup.ExitCode()).To(Equal(0))
+
+		session := podmanTest.Podman([]string{"exec", "-l", "--workdir", "/tmp", "pwd"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+		match, _ := session.GrepString("/tmp")
+		Expect(match).Should(BeTrue())
+
+		session = podmanTest.Podman([]string{"exec", "-l", "-w", "/tmp", "pwd"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+		match, _ = session.GrepString("/tmp")
+		Expect(match).Should(BeTrue())
+	})
+
+	It("podman exec missing working directory test", func() {
+		setup := podmanTest.RunTopContainer("test1")
+		setup.WaitWithDefaultTimeout()
+		Expect(setup.ExitCode()).To(Equal(0))
+
+		session := podmanTest.Podman([]string{"exec", "-l", "--workdir", "/missing", "pwd"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(1))
+
+		session = podmanTest.Podman([]string{"exec", "-l", "-w", "/missing", "pwd"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(1))
+	})
 })
