Skip to content

Commit

Permalink
Added env-file flag to docker exec
Browse files Browse the repository at this point in the history
Signed-off-by: Brian Wieder <[email protected]>
  • Loading branch information
BrianWieder committed Jun 28, 2020
1 parent 44ff366 commit 27d2820
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 16 deletions.
46 changes: 31 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,36 @@ 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,
Env: execOpts.env.GetAll(),
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
}
68 changes: 67 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"
"runtime"
"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,70 @@ 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 TestParseExecError(t *testing.T) {
invalidFile := "no such file or directory"
if runtime.GOOS == "windows" {
invalidFile = "cannot find the file specified"
}

testcases := []struct {
options execOptions
configFile configfile.ConfigFile
expected string
}{
{
expected: invalidFile,
options: func() execOptions {
o := withDefaultOpts(execOptions{})
o.envFile.Set("invalid.env")
return o
}(),
},
}

for _, testcase := range testcases {
execConfig, err := parseExec(testcase.options, &testcase.configFile)
assert.ErrorContains(t, err, testcase.expected)
assert.Check(t, execConfig == nil)
}
}

func TestRunExec(t *testing.T) {
var testcases = []struct {
doc string
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 @@ -21,6 +21,7 @@ Options:
-t, --tty Allocate a pseudo-TTY
-u, --user Username or UID (format: <name|uid>[:<group|gid>])
-w, --workdir Working directory inside the container
--env-file Set environment variables from file
```

## Description
Expand Down

0 comments on commit 27d2820

Please sign in to comment.