X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions cli/command/container/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ type execOptions struct {
workdir string
container string
command []string
envFile opts.ListOpts
}

func newExecOptions() execOptions {
return execOptions{env: opts.NewListOpts(opts.ValidateEnv)}
return execOptions{
env: opts.NewListOpts(opts.ValidateEnv),
envFile: opts.NewListOpts(nil),
}
}

// NewExecCommand creates a new cobra.Command for `docker exec`
Expand Down Expand Up @@ -59,14 +63,20 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
flags.BoolVarP(&options.privileged, "privileged", "", false, "Give extended privileges to the command")
flags.VarP(&options.env, "env", "e", "Set environment variables")
flags.SetAnnotation("env", "version", []string{"1.25"})
flags.Var(&options.envFile, "env-file", "Read in a file of environment variables")
flags.SetAnnotation("env-file", "version", []string{"1.25"})
flags.StringVarP(&options.workdir, "workdir", "w", "", "Working directory inside the container")
flags.SetAnnotation("workdir", "version", []string{"1.35"})

return cmd
}

func runExec(dockerCli command.Cli, options execOptions) error {
execConfig := parseExec(options, dockerCli.ConfigFile())
execConfig, err := parseExec(options, dockerCli.ConfigFile())
if err != nil {
return err
}

ctx := context.Background()
client := dockerCli.Client()

Expand Down Expand Up @@ -185,30 +195,35 @@ func getExecExitStatus(ctx context.Context, client apiclient.ContainerAPIClient,

// parseExec parses the specified args for the specified command and generates
// an ExecConfig from it.
func parseExec(opts execOptions, configFile *configfile.ConfigFile) *types.ExecConfig {
func parseExec(execOpts execOptions, configFile *configfile.ConfigFile) (*types.ExecConfig, error) {
execConfig := &types.ExecConfig{
User: opts.user,
Privileged: opts.privileged,
Tty: opts.tty,
Cmd: opts.command,
Detach: opts.detach,
Env: opts.env.GetAll(),
WorkingDir: opts.workdir,
User: execOpts.user,
Privileged: execOpts.privileged,
Tty: execOpts.tty,
Cmd: execOpts.command,
Detach: execOpts.detach,
WorkingDir: execOpts.workdir,
}

// collect all the environment variables for the container
var err error
if execConfig.Env, err = opts.ReadKVEnvStrings(execOpts.envFile.GetAll(), execOpts.env.GetAll()); err != nil {
return nil, err
}

// If -d is not set, attach to everything by default
if !opts.detach {
if !execOpts.detach {
execConfig.AttachStdout = true
execConfig.AttachStderr = true
if opts.interactive {
if execOpts.interactive {
execConfig.AttachStdin = true
}
}

if opts.detachKeys != "" {
execConfig.DetachKeys = opts.detachKeys
if execOpts.detachKeys != "" {
execConfig.DetachKeys = execOpts.detachKeys
} else {
execConfig.DetachKeys = configFile.DetachKeys
}
return execConfig
return execConfig, nil
}
49 changes: 48 additions & 1 deletion cli/command/container/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package container
import (
"context"
"io/ioutil"
"os"
"testing"

"github.com/docker/cli/cli"
Expand All @@ -13,17 +14,26 @@ import (
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
)

func withDefaultOpts(options execOptions) execOptions {
options.env = opts.NewListOpts(opts.ValidateEnv)
options.envFile = opts.NewListOpts(nil)
if len(options.command) == 0 {
options.command = []string{"command"}
}
return options
}

func TestParseExec(t *testing.T) {
content := `ONE=1
TWO=2
`

tmpFile := fs.NewFile(t, t.Name(), fs.WithContent(content))
defer tmpFile.Remove()

testcases := []struct {
options execOptions
configFile configfile.ConfigFile
Expand Down Expand Up @@ -102,14 +112,51 @@ func TestParseExec(t *testing.T) {
Detach: true,
},
},
{
expected: types.ExecConfig{
Cmd: []string{"command"},
AttachStdout: true,
AttachStderr: true,
Env: []string{"ONE=1", "TWO=2"},
},
options: func() execOptions {
o := withDefaultOpts(execOptions{})
o.envFile.Set(tmpFile.Path())
return o
}(),
},
{
expected: types.ExecConfig{
Cmd: []string{"command"},
AttachStdout: true,
AttachStderr: true,
Env: []string{"ONE=1", "TWO=2", "ONE=override"},
},
options: func() execOptions {
o := withDefaultOpts(execOptions{})
o.envFile.Set(tmpFile.Path())
o.env.Set("ONE=override")
return o
}(),
},
}

for _, testcase := range testcases {
execConfig := parseExec(testcase.options, &testcase.configFile)
execConfig, err := parseExec(testcase.options, &testcase.configFile)
assert.NilError(t, err)
assert.Check(t, is.DeepEqual(testcase.expected, *execConfig))
}
}

func TestParseExecNoSuchFile(t *testing.T) {
execOpts := withDefaultOpts(execOptions{})
execOpts.envFile.Set("no-such-env-file")
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
assert.ErrorContains(t, err, "no-such-env-file")
assert.Check(t, os.IsNotExist(err))
assert.Check(t, execConfig == nil)
}

func TestRunExec(t *testing.T) {
var testcases = []struct {
doc string
Expand Down
6 changes: 5 additions & 1 deletion contrib/completion/bash/docker
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,10 @@ _docker_container_exec() {
__docker_nospace
return
;;
--env-file)
_filedir
return
;;
--user|-u)
__docker_complete_user_group
return
Expand All @@ -1615,7 +1619,7 @@ _docker_container_exec() {

case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--detach -d --detach-keys --env -e --help --interactive -i --privileged -t --tty -u --user --workdir -w" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--detach -d --detach-keys --env -e --env-file --help --interactive -i --privileged -t --tty -u --user --workdir -w" -- "$cur" ) )
;;
*)
__docker_complete_containers_running
Expand Down
1 change: 1 addition & 0 deletions contrib/completion/zsh/_docker
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ __docker_container_subcommand() {
$opts_attach_exec_run_start \
"($help -d --detach)"{-d,--detach}"[Detached mode: leave the container running in the background]" \
"($help)*"{-e=,--env=}"[Set environment variables]:environment variable: " \
"($help)*--env-file=[Read environment variables from a file]:environment file:_files" \
"($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]" \
Expand Down
1 change: 1 addition & 0 deletions docs/reference/commandline/exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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
--env-file Read in a file of environment variables
--help Print usage
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
Expand Down
X Tutup