Browse code

Exec: Add ability to set environment variables

Keeping the current behavior for exec, i.e., inheriting
variables from main process. New variables will be added
to current ones. If there's already a variable with that
name it will be overwritten.

Example of usage: docker exec -it -e TERM=vt100 <container> top

Closes #24355.

Signed-off-by: Jonh Wendell <jonh.wendell@redhat.com>

Jonh Wendell authored on 2016/07/14 02:24:41
Showing 10 changed files
... ...
@@ -11,7 +11,9 @@ import (
11 11
 	"github.com/docker/docker/cli"
12 12
 	"github.com/docker/docker/cli/command"
13 13
 	apiclient "github.com/docker/docker/client"
14
+	options "github.com/docker/docker/opts"
14 15
 	"github.com/docker/docker/pkg/promise"
16
+	runconfigopts "github.com/docker/docker/runconfig/opts"
15 17
 	"github.com/spf13/cobra"
16 18
 )
17 19
 
... ...
@@ -22,11 +24,19 @@ type execOptions struct {
22 22
 	detach      bool
23 23
 	user        string
24 24
 	privileged  bool
25
+	env         *options.ListOpts
26
+}
27
+
28
+func newExecOptions() *execOptions {
29
+	var values []string
30
+	return &execOptions{
31
+		env: options.NewListOptsRef(&values, runconfigopts.ValidateEnv),
32
+	}
25 33
 }
26 34
 
27 35
 // NewExecCommand creats a new cobra.Command for `docker exec`
28 36
 func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command {
29
-	var opts execOptions
37
+	opts := newExecOptions()
30 38
 
31 39
 	cmd := &cobra.Command{
32 40
 		Use:   "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
... ...
@@ -35,7 +45,7 @@ func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command {
35 35
 		RunE: func(cmd *cobra.Command, args []string) error {
36 36
 			container := args[0]
37 37
 			execCmd := args[1:]
38
-			return runExec(dockerCli, &opts, container, execCmd)
38
+			return runExec(dockerCli, opts, container, execCmd)
39 39
 		},
40 40
 	}
41 41
 
... ...
@@ -48,6 +58,7 @@ func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command {
48 48
 	flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background")
49 49
 	flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
50 50
 	flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command")
51
+	flags.VarP(opts.env, "env", "e", "Set environment variables")
51 52
 
52 53
 	return cmd
53 54
 }
... ...
@@ -188,5 +199,9 @@ func parseExec(opts *execOptions, container string, execCmd []string) (*types.Ex
188 188
 		}
189 189
 	}
190 190
 
191
+	if opts.env != nil {
192
+		execConfig.Env = opts.env.GetAll()
193
+	}
194
+
191 195
 	return execConfig, nil
192 196
 }
... ...
@@ -1274,7 +1274,7 @@ _docker_exec() {
1274 1274
 
1275 1275
 	case "$cur" in
1276 1276
 		-*)
1277
-			COMPREPLY=( $( compgen -W "--detach -d --detach-keys --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
1277
+			COMPREPLY=( $( compgen -W "--detach -d --detach-keys -e --env --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
1278 1278
 			;;
1279 1279
 		*)
1280 1280
 			__docker_complete_containers_running
... ...
@@ -1679,6 +1679,7 @@ __docker_subcommand() {
1679 1679
                 $opts_help \
1680 1680
                 $opts_attach_exec_run_start \
1681 1681
                 "($help -d --detach)"{-d,--detach}"[Detached mode: leave the container running in the background]" \
1682
+                "($help -e --env)"{-e,--env}"[Set environment variables]" \
1682 1683
                 "($help -i --interactive)"{-i,--interactive}"[Keep stdin open even if not attached]" \
1683 1684
                 "($help)--privileged[Give extended Linux capabilities to the command]" \
1684 1685
                 "($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]" \
... ...
@@ -127,7 +127,7 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str
127 127
 	if err != nil {
128 128
 		return "", err
129 129
 	}
130
-	execConfig.Env = utils.ReplaceOrAppendEnvValues(container.CreateDaemonEnvironment(config.Tty, linkedEnv), execConfig.Env)
130
+	execConfig.Env = utils.ReplaceOrAppendEnvValues(container.CreateDaemonEnvironment(config.Tty, linkedEnv), config.Env)
131 131
 	if len(execConfig.User) == 0 {
132 132
 		execConfig.User = container.Config.User
133 133
 	}
... ...
@@ -131,6 +131,7 @@ This section lists each version from latest to oldest.  Each listing includes a
131 131
 * `POST /containers/create` now takes `StopTimeout` field.
132 132
 * `POST /services/create` and `POST /services/(id or name)/update` now accept `Monitor` and `MaxFailureRatio` parameters, which control the response to failures during service updates.
133 133
 * `GET /networks/(name)` now returns `Created`.
134
+* `POST /containers/(id or name)/exec` now accepts an `Env` field, which holds a list of environment variables to be set in the context of the command execution.
134 135
 
135 136
 ### v1.24 API changes
136 137
 
... ...
@@ -3151,6 +3151,10 @@ Sets up an exec instance in a running container `id`
3151 3151
       "AttachStderr": true,
3152 3152
       "Cmd": ["sh"],
3153 3153
       "DetachKeys": "ctrl-p,ctrl-q",
3154
+      "Env": [
3155
+        "FOO=bar",
3156
+        "BAZ=quux"
3157
+      ],
3154 3158
       "Privileged": true,
3155 3159
       "Tty": true,
3156 3160
       "User": "123:456"
... ...
@@ -3175,6 +3179,7 @@ Sets up an exec instance in a running container `id`
3175 3175
         container. Format is a single character `[a-Z]` or `ctrl-<value>`
3176 3176
         where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
3177 3177
 -   **Tty** - Boolean value to allocate a pseudo-TTY.
3178
+-   **Env** - A list of environment variables in the form of `["VAR=value", ...]`
3178 3179
 -   **Cmd** - Command to run specified as a string or an array of strings.
3179 3180
 -   **Privileged** - Boolean value, runs the exec process with extended privileges.
3180 3181
 -   **User** - A string value specifying the user, and optionally, group to run
... ...
@@ -14,6 +14,7 @@ Run a command in a running container
14 14
 Options:
15 15
   -d, --detach         Detached mode: run command in the background
16 16
       --detach-keys    Override the key sequence for detaching a container
17
+  -e, --env=[]         Set environment variables
17 18
       --help           Print usage
18 19
   -i, --interactive    Keep STDIN open even if not attached
19 20
       --privileged     Give extended privileges to the command
... ...
@@ -119,6 +119,17 @@ func (s *DockerSuite) TestExecEnv(c *check.C) {
119 119
 	c.Assert(out, checker.Contains, "HOME=/root")
120 120
 }
121 121
 
122
+func (s *DockerSuite) TestExecSetEnv(c *check.C) {
123
+	testRequires(c, DaemonIsLinux)
124
+	runSleepingContainer(c, "-e", "HOME=/root", "-d", "--name", "testing")
125
+	c.Assert(waitRun("testing"), check.IsNil)
126
+
127
+	out, _ := dockerCmd(c, "exec", "-e", "HOME=/another", "-e", "ABC=xyz", "testing", "env")
128
+	c.Assert(out, checker.Not(checker.Contains), "HOME=/root")
129
+	c.Assert(out, checker.Contains, "HOME=/another")
130
+	c.Assert(out, checker.Contains, "ABC=xyz")
131
+}
132
+
122 133
 func (s *DockerSuite) TestExecExitStatus(c *check.C) {
123 134
 	runSleepingContainer(c, "-d", "--name", "top")
124 135
 
... ...
@@ -45,7 +45,7 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly
45 45
 	sp := spec.Process
46 46
 	sp.Args = specp.Args
47 47
 	sp.Terminal = specp.Terminal
48
-	if specp.Env != nil {
48
+	if len(specp.Env) > 0 {
49 49
 		sp.Env = specp.Env
50 50
 	}
51 51
 	if specp.Cwd != nil {
... ...
@@ -8,6 +8,7 @@ docker-exec - Run a command in a running container
8 8
 **docker exec**
9 9
 [**-d**|**--detach**]
10 10
 [**--detach-keys**[=*[]*]]
11
+[**-e**|**--env**[=*[]*]]
11 12
 [**--help**]
12 13
 [**-i**|**--interactive**]
13 14
 [**--privileged**]
... ...
@@ -32,6 +33,12 @@ container is unpaused, and then run
32 32
 **--detach-keys**=""
33 33
   Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
34 34
 
35
+**-e**, **--env**=[]
36
+   Set environment variables
37
+
38
+   This option allows you to specify arbitrary environment variables that are
39
+available for the command to be executed.
40
+
35 41
 **--help**
36 42
   Print usage statement
37 43