From e03bf1221ee2c863f25a57af4d415e2d8ff4f26c Mon Sep 17 00:00:00 2001 From: Jonh Wendell Date: Wed, 13 Jul 2016 14:24:41 -0300 Subject: [PATCH] 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 top Closes #24355. Signed-off-by: Jonh Wendell --- cli/command/container/exec.go | 19 +++++++++++++++++-- contrib/completion/bash/docker | 2 +- contrib/completion/zsh/_docker | 1 + daemon/exec.go | 2 +- docs/reference/api/docker_remote_api.md | 1 + docs/reference/api/docker_remote_api_v1.25.md | 5 +++++ docs/reference/commandline/exec.md | 1 + integration-cli/docker_cli_exec_test.go | 11 +++++++++++ libcontainerd/client_linux.go | 2 +- man/docker-exec.1.md | 7 +++++++ 10 files changed, 46 insertions(+), 5 deletions(-) diff --git a/cli/command/container/exec.go b/cli/command/container/exec.go index 1682a7ca64..48964693b2 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -11,7 +11,9 @@ import ( "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" apiclient "github.com/docker/docker/client" + options "github.com/docker/docker/opts" "github.com/docker/docker/pkg/promise" + runconfigopts "github.com/docker/docker/runconfig/opts" "github.com/spf13/cobra" ) @@ -22,11 +24,19 @@ type execOptions struct { detach bool user string privileged bool + env *options.ListOpts +} + +func newExecOptions() *execOptions { + var values []string + return &execOptions{ + env: options.NewListOptsRef(&values, runconfigopts.ValidateEnv), + } } // NewExecCommand creats a new cobra.Command for `docker exec` func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command { - var opts execOptions + opts := newExecOptions() cmd := &cobra.Command{ Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]", @@ -35,7 +45,7 @@ func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { container := args[0] execCmd := args[1:] - return runExec(dockerCli, &opts, container, execCmd) + return runExec(dockerCli, opts, container, execCmd) }, } @@ -48,6 +58,7 @@ func NewExecCommand(dockerCli *command.DockerCli) *cobra.Command { flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background") flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: [:])") flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command") + flags.VarP(opts.env, "env", "e", "Set environment variables") return cmd } @@ -188,5 +199,9 @@ func parseExec(opts *execOptions, container string, execCmd []string) (*types.Ex } } + if opts.env != nil { + execConfig.Env = opts.env.GetAll() + } + return execConfig, nil } diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 012aba84f9..c6c9bbca8a 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1274,7 +1274,7 @@ _docker_exec() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--detach -d --detach-keys --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--detach -d --detach-keys -e --env --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) ) ;; *) __docker_complete_containers_running diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index cb73073905..0e57648522 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -1679,6 +1679,7 @@ __docker_subcommand() { $opts_help \ $opts_attach_exec_run_start \ "($help -d --detach)"{-d,--detach}"[Detached mode: leave the container running in the background]" \ + "($help -e --env)"{-e,--env}"[Set environment variables]" \ "($help -i --interactive)"{-i,--interactive}"[Keep stdin open even if not attached]" \ "($help)--privileged[Give extended Linux capabilities to the command]" \ "($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]" \ diff --git a/daemon/exec.go b/daemon/exec.go index 67631333bc..6c977dace3 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -127,7 +127,7 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str if err != nil { return "", err } - execConfig.Env = utils.ReplaceOrAppendEnvValues(container.CreateDaemonEnvironment(config.Tty, linkedEnv), execConfig.Env) + execConfig.Env = utils.ReplaceOrAppendEnvValues(container.CreateDaemonEnvironment(config.Tty, linkedEnv), config.Env) if len(execConfig.User) == 0 { execConfig.User = container.Config.User } diff --git a/docs/reference/api/docker_remote_api.md b/docs/reference/api/docker_remote_api.md index b383bc163a..b9fcabbddd 100644 --- a/docs/reference/api/docker_remote_api.md +++ b/docs/reference/api/docker_remote_api.md @@ -131,6 +131,7 @@ This section lists each version from latest to oldest. Each listing includes a * `POST /containers/create` now takes `StopTimeout` field. * `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. * `GET /networks/(name)` now returns `Created`. +* `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. ### v1.24 API changes diff --git a/docs/reference/api/docker_remote_api_v1.25.md b/docs/reference/api/docker_remote_api_v1.25.md index 1a8d3c21fb..f1a7bf9577 100644 --- a/docs/reference/api/docker_remote_api_v1.25.md +++ b/docs/reference/api/docker_remote_api_v1.25.md @@ -3151,6 +3151,10 @@ Sets up an exec instance in a running container `id` "AttachStderr": true, "Cmd": ["sh"], "DetachKeys": "ctrl-p,ctrl-q", + "Env": [ + "FOO=bar", + "BAZ=quux" + ], "Privileged": true, "Tty": true, "User": "123:456" @@ -3175,6 +3179,7 @@ Sets up an exec instance in a running container `id` container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. - **Tty** - Boolean value to allocate a pseudo-TTY. +- **Env** - A list of environment variables in the form of `["VAR=value", ...]` - **Cmd** - Command to run specified as a string or an array of strings. - **Privileged** - Boolean value, runs the exec process with extended privileges. - **User** - A string value specifying the user, and optionally, group to run diff --git a/docs/reference/commandline/exec.md b/docs/reference/commandline/exec.md index 9be6cbadd8..c6b7314dc7 100644 --- a/docs/reference/commandline/exec.md +++ b/docs/reference/commandline/exec.md @@ -14,6 +14,7 @@ Run a command in a running container Options: -d, --detach Detached mode: run command in the background --detach-keys Override the key sequence for detaching a container + -e, --env=[] Set environment variables --help Print usage -i, --interactive Keep STDIN open even if not attached --privileged Give extended privileges to the command diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go index da815335b0..d74e1a80a8 100644 --- a/integration-cli/docker_cli_exec_test.go +++ b/integration-cli/docker_cli_exec_test.go @@ -119,6 +119,17 @@ func (s *DockerSuite) TestExecEnv(c *check.C) { c.Assert(out, checker.Contains, "HOME=/root") } +func (s *DockerSuite) TestExecSetEnv(c *check.C) { + testRequires(c, DaemonIsLinux) + runSleepingContainer(c, "-e", "HOME=/root", "-d", "--name", "testing") + c.Assert(waitRun("testing"), check.IsNil) + + out, _ := dockerCmd(c, "exec", "-e", "HOME=/another", "-e", "ABC=xyz", "testing", "env") + c.Assert(out, checker.Not(checker.Contains), "HOME=/root") + c.Assert(out, checker.Contains, "HOME=/another") + c.Assert(out, checker.Contains, "ABC=xyz") +} + func (s *DockerSuite) TestExecExitStatus(c *check.C) { runSleepingContainer(c, "-d", "--name", "top") diff --git a/libcontainerd/client_linux.go b/libcontainerd/client_linux.go index c46a48a07a..abaadd68bf 100644 --- a/libcontainerd/client_linux.go +++ b/libcontainerd/client_linux.go @@ -45,7 +45,7 @@ func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendly sp := spec.Process sp.Args = specp.Args sp.Terminal = specp.Terminal - if specp.Env != nil { + if len(specp.Env) > 0 { sp.Env = specp.Env } if specp.Cwd != nil { diff --git a/man/docker-exec.1.md b/man/docker-exec.1.md index 16a061d069..fe9c279e7e 100644 --- a/man/docker-exec.1.md +++ b/man/docker-exec.1.md @@ -8,6 +8,7 @@ docker-exec - Run a command in a running container **docker exec** [**-d**|**--detach**] [**--detach-keys**[=*[]*]] +[**-e**|**--env**[=*[]*]] [**--help**] [**-i**|**--interactive**] [**--privileged**] @@ -32,6 +33,12 @@ container is unpaused, and then run **--detach-keys**="" Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. +**-e**, **--env**=[] + Set environment variables + + This option allows you to specify arbitrary environment variables that are +available for the command to be executed. + **--help** Print usage statement