Merge pull request #35661 from ndeloof/8917
introduce `workingdir` option for docker exec
diff --git a/api/swagger.yaml b/api/swagger.yaml
index 96386e0..b7ed3b8 100644
--- a/api/swagger.yaml
+++ b/api/swagger.yaml
@@ -7259,6 +7259,9 @@
User:
type: "string"
description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`."
+ WorkingDir:
+ type: "string"
+ description: "The working directory for the exec process inside the container."
example:
AttachStdin: false
AttachStdout: true
diff --git a/api/types/configs.go b/api/types/configs.go
index 20c19f2..54d3e39 100644
--- a/api/types/configs.go
+++ b/api/types/configs.go
@@ -50,6 +50,7 @@
Detach bool // Execute in detach mode
DetachKeys string // Escape keys for detach
Env []string // Environment variables
+ WorkingDir string // Working directory
Cmd []string // Execution commands and args
}
diff --git a/daemon/exec.go b/daemon/exec.go
index 01670fa..83b7de2 100644
--- a/daemon/exec.go
+++ b/daemon/exec.go
@@ -122,6 +122,7 @@
execConfig.Tty = config.Tty
execConfig.Privileged = config.Privileged
execConfig.User = config.User
+ execConfig.WorkingDir = config.WorkingDir
linkedEnv, err := d.setupLinkedContainers(cntr)
if err != nil {
@@ -131,6 +132,9 @@
if len(execConfig.User) == 0 {
execConfig.User = cntr.Config.User
}
+ if len(execConfig.WorkingDir) == 0 {
+ execConfig.WorkingDir = cntr.Config.WorkingDir
+ }
d.registerExecCommand(cntr, execConfig)
@@ -211,7 +215,7 @@
Args: append([]string{ec.Entrypoint}, ec.Args...),
Env: ec.Env,
Terminal: ec.Tty,
- Cwd: c.Config.WorkingDir,
+ Cwd: ec.WorkingDir,
}
if p.Cwd == "" {
p.Cwd = "/"
diff --git a/daemon/exec/exec.go b/daemon/exec/exec.go
index 193d32f..370b403 100644
--- a/daemon/exec/exec.go
+++ b/daemon/exec/exec.go
@@ -31,6 +31,7 @@
Tty bool
Privileged bool
User string
+ WorkingDir string
Env []string
Pid int
}
diff --git a/integration/container/exec_test.go b/integration/container/exec_test.go
new file mode 100644
index 0000000..22d7ec0
--- /dev/null
+++ b/integration/container/exec_test.go
@@ -0,0 +1,60 @@
+package container
+
+import (
+ "context"
+ "io/ioutil"
+ "testing"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
+ "github.com/docker/docker/api/types/network"
+ "github.com/docker/docker/api/types/strslice"
+ "github.com/docker/docker/integration/util/request"
+ "github.com/stretchr/testify/require"
+)
+
+func TestExec(t *testing.T) {
+ defer setupTest(t)()
+ ctx := context.Background()
+ client := request.NewAPIClient(t)
+
+ container, err := client.ContainerCreate(ctx,
+ &container.Config{
+ Image: "busybox",
+ Tty: true,
+ WorkingDir: "/root",
+ Cmd: strslice.StrSlice([]string{"top"}),
+ },
+ &container.HostConfig{},
+ &network.NetworkingConfig{},
+ "foo",
+ )
+ require.NoError(t, err)
+ err = client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
+ require.NoError(t, err)
+
+ id, err := client.ContainerExecCreate(ctx, container.ID,
+ types.ExecConfig{
+ WorkingDir: "/tmp",
+ Env: strslice.StrSlice([]string{"FOO=BAR"}),
+ AttachStdout: true,
+ Cmd: strslice.StrSlice([]string{"sh", "-c", "env"}),
+ },
+ )
+ require.NoError(t, err)
+
+ resp, err := client.ContainerExecAttach(ctx, id.ID,
+ types.ExecStartCheck{
+ Detach: false,
+ Tty: false,
+ },
+ )
+ require.NoError(t, err)
+ defer resp.Close()
+ r, err := ioutil.ReadAll(resp.Reader)
+ require.NoError(t, err)
+ out := string(r)
+ require.NoError(t, err)
+ require.Contains(t, out, "PWD=/tmp", "exec command not running in expected /tmp working directory")
+ require.Contains(t, out, "FOO=BAR", "exec command not running with expected environment variable FOO")
+}