mirror of https://github.com/docker/cli.git
Merge pull request #2602 from BrianWieder/1681-docker-exec-env-file
Added env-file flag to docker exec
This commit is contained in:
commit
1c6dd42dab
|
@ -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`
|
||||
|
@ -59,6 +63,8 @@ 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"})
|
||||
|
||||
|
@ -66,7 +72,11 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
|||
}
|
||||
|
||||
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()
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package container
|
|||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
|
@ -13,10 +14,12 @@ 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"}
|
||||
}
|
||||
|
@ -24,6 +27,13 @@ func withDefaultOpts(options execOptions) execOptions {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
|
|
@ -1604,6 +1604,10 @@ _docker_container_exec() {
|
|||
__docker_nospace
|
||||
return
|
||||
;;
|
||||
--env-file)
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
--user|-u)
|
||||
__docker_complete_user_group
|
||||
return
|
||||
|
@ -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
|
||||
|
|
|
@ -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]" \
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue