Kaynağa Gözat

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 9 yıl önce
ebeveyn
işleme
e03bf1221e

+ 17 - 2
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: <name|uid>[:<group|gid>])")
 	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
 }

+ 1 - 1
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

+ 1 - 0
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]" \

+ 1 - 1
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
 	}

+ 1 - 0
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
 

+ 5 - 0
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-<value>`
         where `<value>` 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

+ 1 - 0
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

+ 11 - 0
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")
 

+ 1 - 1
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 {

+ 7 - 0
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-<value>` where `<value>` 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