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
|
workdir string
|
||||||
container string
|
container string
|
||||||
command []string
|
command []string
|
||||||
|
envFile opts.ListOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExecOptions() execOptions {
|
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`
|
// 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.BoolVarP(&options.privileged, "privileged", "", false, "Give extended privileges to the command")
|
||||||
flags.VarP(&options.env, "env", "e", "Set environment variables")
|
flags.VarP(&options.env, "env", "e", "Set environment variables")
|
||||||
flags.SetAnnotation("env", "version", []string{"1.25"})
|
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.StringVarP(&options.workdir, "workdir", "w", "", "Working directory inside the container")
|
||||||
flags.SetAnnotation("workdir", "version", []string{"1.35"})
|
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 {
|
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()
|
ctx := context.Background()
|
||||||
client := dockerCli.Client()
|
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
|
// parseExec parses the specified args for the specified command and generates
|
||||||
// an ExecConfig from it.
|
// 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{
|
execConfig := &types.ExecConfig{
|
||||||
User: opts.user,
|
User: execOpts.user,
|
||||||
Privileged: opts.privileged,
|
Privileged: execOpts.privileged,
|
||||||
Tty: opts.tty,
|
Tty: execOpts.tty,
|
||||||
Cmd: opts.command,
|
Cmd: execOpts.command,
|
||||||
Detach: opts.detach,
|
Detach: execOpts.detach,
|
||||||
Env: opts.env.GetAll(),
|
WorkingDir: execOpts.workdir,
|
||||||
WorkingDir: opts.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 -d is not set, attach to everything by default
|
||||||
if !opts.detach {
|
if !execOpts.detach {
|
||||||
execConfig.AttachStdout = true
|
execConfig.AttachStdout = true
|
||||||
execConfig.AttachStderr = true
|
execConfig.AttachStderr = true
|
||||||
if opts.interactive {
|
if execOpts.interactive {
|
||||||
execConfig.AttachStdin = true
|
execConfig.AttachStdin = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.detachKeys != "" {
|
if execOpts.detachKeys != "" {
|
||||||
execConfig.DetachKeys = opts.detachKeys
|
execConfig.DetachKeys = execOpts.detachKeys
|
||||||
} else {
|
} else {
|
||||||
execConfig.DetachKeys = configFile.DetachKeys
|
execConfig.DetachKeys = configFile.DetachKeys
|
||||||
}
|
}
|
||||||
return execConfig
|
return execConfig, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package container
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
|
@ -13,10 +14,12 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
|
"gotest.tools/v3/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func withDefaultOpts(options execOptions) execOptions {
|
func withDefaultOpts(options execOptions) execOptions {
|
||||||
options.env = opts.NewListOpts(opts.ValidateEnv)
|
options.env = opts.NewListOpts(opts.ValidateEnv)
|
||||||
|
options.envFile = opts.NewListOpts(nil)
|
||||||
if len(options.command) == 0 {
|
if len(options.command) == 0 {
|
||||||
options.command = []string{"command"}
|
options.command = []string{"command"}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +27,13 @@ func withDefaultOpts(options execOptions) execOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseExec(t *testing.T) {
|
func TestParseExec(t *testing.T) {
|
||||||
|
content := `ONE=1
|
||||||
|
TWO=2
|
||||||
|
`
|
||||||
|
|
||||||
|
tmpFile := fs.NewFile(t, t.Name(), fs.WithContent(content))
|
||||||
|
defer tmpFile.Remove()
|
||||||
|
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
options execOptions
|
options execOptions
|
||||||
configFile configfile.ConfigFile
|
configFile configfile.ConfigFile
|
||||||
|
@ -102,14 +112,51 @@ func TestParseExec(t *testing.T) {
|
||||||
Detach: true,
|
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 {
|
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))
|
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) {
|
func TestRunExec(t *testing.T) {
|
||||||
var testcases = []struct {
|
var testcases = []struct {
|
||||||
doc string
|
doc string
|
||||||
|
|
|
@ -1604,6 +1604,10 @@ _docker_container_exec() {
|
||||||
__docker_nospace
|
__docker_nospace
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
--env-file)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
--user|-u)
|
--user|-u)
|
||||||
__docker_complete_user_group
|
__docker_complete_user_group
|
||||||
return
|
return
|
||||||
|
@ -1615,7 +1619,7 @@ _docker_container_exec() {
|
||||||
|
|
||||||
case "$cur" in
|
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
|
__docker_complete_containers_running
|
||||||
|
|
|
@ -750,6 +750,7 @@ __docker_container_subcommand() {
|
||||||
$opts_attach_exec_run_start \
|
$opts_attach_exec_run_start \
|
||||||
"($help -d --detach)"{-d,--detach}"[Detached mode: leave the container running in the background]" \
|
"($help -d --detach)"{-d,--detach}"[Detached mode: leave the container running in the background]" \
|
||||||
"($help)*"{-e=,--env=}"[Set environment variables]:environment variable: " \
|
"($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 -i --interactive)"{-i,--interactive}"[Keep stdin open even if not attached]" \
|
||||||
"($help)--privileged[Give extended Linux capabilities to the command]" \
|
"($help)--privileged[Give extended Linux capabilities to the command]" \
|
||||||
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]" \
|
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]" \
|
||||||
|
|
|
@ -15,6 +15,7 @@ Options:
|
||||||
-d, --detach Detached mode: run command in the background
|
-d, --detach Detached mode: run command in the background
|
||||||
--detach-keys Override the key sequence for detaching a container
|
--detach-keys Override the key sequence for detaching a container
|
||||||
-e, --env=[] Set environment variables
|
-e, --env=[] Set environment variables
|
||||||
|
--env-file Read in a file of environment variables
|
||||||
--help Print usage
|
--help Print usage
|
||||||
-i, --interactive Keep STDIN open even if not attached
|
-i, --interactive Keep STDIN open even if not attached
|
||||||
--privileged Give extended privileges to the command
|
--privileged Give extended privileges to the command
|
||||||
|
|
Loading…
Reference in New Issue