mirror of https://github.com/docker/cli.git
publish RunExec for use by docker/compose
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
cf8c4bab64
commit
2d268392d1
|
@ -16,62 +16,65 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type execOptions struct {
|
// ExecOptions group options for `exec` command
|
||||||
detachKeys string
|
type ExecOptions struct {
|
||||||
interactive bool
|
DetachKeys string
|
||||||
tty bool
|
Interactive bool
|
||||||
detach bool
|
TTY bool
|
||||||
user string
|
Detach bool
|
||||||
privileged bool
|
User string
|
||||||
env opts.ListOpts
|
Privileged bool
|
||||||
workdir string
|
Env opts.ListOpts
|
||||||
container string
|
Workdir string
|
||||||
command []string
|
Container string
|
||||||
envFile opts.ListOpts
|
Command []string
|
||||||
|
EnvFile opts.ListOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExecOptions() execOptions {
|
// NewExecOptions creates a new ExecOptions
|
||||||
return execOptions{
|
func NewExecOptions() ExecOptions {
|
||||||
env: opts.NewListOpts(opts.ValidateEnv),
|
return ExecOptions{
|
||||||
envFile: opts.NewListOpts(nil),
|
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`
|
||||||
func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
options := newExecOptions()
|
options := NewExecOptions()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
|
Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
|
||||||
Short: "Run a command in a running container",
|
Short: "Run a command in a running container",
|
||||||
Args: cli.RequiresMinArgs(2),
|
Args: cli.RequiresMinArgs(2),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.container = args[0]
|
options.Container = args[0]
|
||||||
options.command = args[1:]
|
options.Command = args[1:]
|
||||||
return runExec(dockerCli, options)
|
return RunExec(dockerCli, options)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.SetInterspersed(false)
|
flags.SetInterspersed(false)
|
||||||
|
|
||||||
flags.StringVarP(&options.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container")
|
flags.StringVarP(&options.DetachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container")
|
||||||
flags.BoolVarP(&options.interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
|
flags.BoolVarP(&options.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
|
||||||
flags.BoolVarP(&options.tty, "tty", "t", false, "Allocate a pseudo-TTY")
|
flags.BoolVarP(&options.TTY, "tty", "t", false, "Allocate a pseudo-TTY")
|
||||||
flags.BoolVarP(&options.detach, "detach", "d", false, "Detached mode: run command in the background")
|
flags.BoolVarP(&options.Detach, "detach", "d", false, "Detached mode: run command in the background")
|
||||||
flags.StringVarP(&options.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
flags.StringVarP(&options.User, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
||||||
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.Var(&options.EnvFile, "env-file", "Read in a file of environment variables")
|
||||||
flags.SetAnnotation("env-file", "version", []string{"1.25"})
|
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"})
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExec(dockerCli command.Cli, options execOptions) error {
|
// RunExec executes an `exec` command
|
||||||
|
func RunExec(dockerCli command.Cli, options ExecOptions) error {
|
||||||
execConfig, err := parseExec(options, dockerCli.ConfigFile())
|
execConfig, err := parseExec(options, dockerCli.ConfigFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -84,7 +87,7 @@ func runExec(dockerCli command.Cli, options execOptions) error {
|
||||||
// otherwise if we error out we will leak execIDs on the server (and
|
// otherwise if we error out we will leak execIDs on the server (and
|
||||||
// there's no easy way to clean those up). But also in order to make "not
|
// there's no easy way to clean those up). But also in order to make "not
|
||||||
// exist" errors take precedence we do a dummy inspect first.
|
// exist" errors take precedence we do a dummy inspect first.
|
||||||
if _, err := client.ContainerInspect(ctx, options.container); err != nil {
|
if _, err := client.ContainerInspect(ctx, options.Container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !execConfig.Detach {
|
if !execConfig.Detach {
|
||||||
|
@ -93,7 +96,7 @@ func runExec(dockerCli command.Cli, options execOptions) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := client.ContainerExecCreate(ctx, options.container, *execConfig)
|
response, err := client.ContainerExecCreate(ctx, options.Container, *execConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -195,33 +198,33 @@ 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(execOpts execOptions, configFile *configfile.ConfigFile) (*types.ExecConfig, error) {
|
func parseExec(execOpts ExecOptions, configFile *configfile.ConfigFile) (*types.ExecConfig, error) {
|
||||||
execConfig := &types.ExecConfig{
|
execConfig := &types.ExecConfig{
|
||||||
User: execOpts.user,
|
User: execOpts.User,
|
||||||
Privileged: execOpts.privileged,
|
Privileged: execOpts.Privileged,
|
||||||
Tty: execOpts.tty,
|
Tty: execOpts.TTY,
|
||||||
Cmd: execOpts.command,
|
Cmd: execOpts.Command,
|
||||||
Detach: execOpts.detach,
|
Detach: execOpts.Detach,
|
||||||
WorkingDir: execOpts.workdir,
|
WorkingDir: execOpts.Workdir,
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the environment variables for the container
|
// collect all the environment variables for the container
|
||||||
var err error
|
var err error
|
||||||
if execConfig.Env, err = opts.ReadKVEnvStrings(execOpts.envFile.GetAll(), execOpts.env.GetAll()); err != nil {
|
if execConfig.Env, err = opts.ReadKVEnvStrings(execOpts.EnvFile.GetAll(), execOpts.Env.GetAll()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If -d is not set, attach to everything by default
|
// If -d is not set, attach to everything by default
|
||||||
if !execOpts.detach {
|
if !execOpts.Detach {
|
||||||
execConfig.AttachStdout = true
|
execConfig.AttachStdout = true
|
||||||
execConfig.AttachStderr = true
|
execConfig.AttachStderr = true
|
||||||
if execOpts.interactive {
|
if execOpts.Interactive {
|
||||||
execConfig.AttachStdin = true
|
execConfig.AttachStdin = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if execOpts.detachKeys != "" {
|
if execOpts.DetachKeys != "" {
|
||||||
execConfig.DetachKeys = execOpts.detachKeys
|
execConfig.DetachKeys = execOpts.DetachKeys
|
||||||
} else {
|
} else {
|
||||||
execConfig.DetachKeys = configFile.DetachKeys
|
execConfig.DetachKeys = configFile.DetachKeys
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ import (
|
||||||
"gotest.tools/v3/fs"
|
"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)
|
options.EnvFile = opts.NewListOpts(nil)
|
||||||
if len(options.command) == 0 {
|
if len(options.Command) == 0 {
|
||||||
options.command = []string{"command"}
|
options.Command = []string{"command"}
|
||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ TWO=2
|
||||||
defer tmpFile.Remove()
|
defer tmpFile.Remove()
|
||||||
|
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
options execOptions
|
options ExecOptions
|
||||||
configFile configfile.ConfigFile
|
configFile configfile.ConfigFile
|
||||||
expected types.ExecConfig
|
expected types.ExecConfig
|
||||||
}{
|
}{
|
||||||
|
@ -45,7 +45,7 @@ TWO=2
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
},
|
},
|
||||||
options: withDefaultOpts(execOptions{}),
|
options: withDefaultOpts(ExecOptions{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
|
@ -53,15 +53,15 @@ TWO=2
|
||||||
AttachStdout: true,
|
AttachStdout: true,
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
},
|
},
|
||||||
options: withDefaultOpts(execOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
command: []string{"command1", "command2"},
|
Command: []string{"command1", "command2"},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: withDefaultOpts(execOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
interactive: true,
|
Interactive: true,
|
||||||
tty: true,
|
TTY: true,
|
||||||
user: "uid",
|
User: "uid",
|
||||||
}),
|
}),
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
User: "uid",
|
User: "uid",
|
||||||
|
@ -73,17 +73,17 @@ TWO=2
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: withDefaultOpts(execOptions{detach: true}),
|
options: withDefaultOpts(ExecOptions{Detach: true}),
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
Detach: true,
|
Detach: true,
|
||||||
Cmd: []string{"command"},
|
Cmd: []string{"command"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: withDefaultOpts(execOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
tty: true,
|
TTY: true,
|
||||||
interactive: true,
|
Interactive: true,
|
||||||
detach: true,
|
Detach: true,
|
||||||
}),
|
}),
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
Detach: true,
|
Detach: true,
|
||||||
|
@ -92,7 +92,7 @@ TWO=2
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: withDefaultOpts(execOptions{detach: true}),
|
options: withDefaultOpts(ExecOptions{Detach: true}),
|
||||||
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
Cmd: []string{"command"},
|
Cmd: []string{"command"},
|
||||||
|
@ -101,9 +101,9 @@ TWO=2
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: withDefaultOpts(execOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
detach: true,
|
Detach: true,
|
||||||
detachKeys: "ab",
|
DetachKeys: "ab",
|
||||||
}),
|
}),
|
||||||
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
||||||
expected: types.ExecConfig{
|
expected: types.ExecConfig{
|
||||||
|
@ -119,9 +119,9 @@ TWO=2
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
Env: []string{"ONE=1", "TWO=2"},
|
Env: []string{"ONE=1", "TWO=2"},
|
||||||
},
|
},
|
||||||
options: func() execOptions {
|
options: func() ExecOptions {
|
||||||
o := withDefaultOpts(execOptions{})
|
o := withDefaultOpts(ExecOptions{})
|
||||||
o.envFile.Set(tmpFile.Path())
|
o.EnvFile.Set(tmpFile.Path())
|
||||||
return o
|
return o
|
||||||
}(),
|
}(),
|
||||||
},
|
},
|
||||||
|
@ -132,10 +132,10 @@ TWO=2
|
||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
Env: []string{"ONE=1", "TWO=2", "ONE=override"},
|
Env: []string{"ONE=1", "TWO=2", "ONE=override"},
|
||||||
},
|
},
|
||||||
options: func() execOptions {
|
options: func() ExecOptions {
|
||||||
o := withDefaultOpts(execOptions{})
|
o := withDefaultOpts(ExecOptions{})
|
||||||
o.envFile.Set(tmpFile.Path())
|
o.EnvFile.Set(tmpFile.Path())
|
||||||
o.env.Set("ONE=override")
|
o.Env.Set("ONE=override")
|
||||||
return o
|
return o
|
||||||
}(),
|
}(),
|
||||||
},
|
},
|
||||||
|
@ -149,8 +149,8 @@ TWO=2
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseExecNoSuchFile(t *testing.T) {
|
func TestParseExecNoSuchFile(t *testing.T) {
|
||||||
execOpts := withDefaultOpts(execOptions{})
|
execOpts := withDefaultOpts(ExecOptions{})
|
||||||
execOpts.envFile.Set("no-such-env-file")
|
execOpts.EnvFile.Set("no-such-env-file")
|
||||||
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
|
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
|
||||||
assert.ErrorContains(t, err, "no-such-env-file")
|
assert.ErrorContains(t, err, "no-such-env-file")
|
||||||
assert.Check(t, os.IsNotExist(err))
|
assert.Check(t, os.IsNotExist(err))
|
||||||
|
@ -160,7 +160,7 @@ func TestParseExecNoSuchFile(t *testing.T) {
|
||||||
func TestRunExec(t *testing.T) {
|
func TestRunExec(t *testing.T) {
|
||||||
var testcases = []struct {
|
var testcases = []struct {
|
||||||
doc string
|
doc string
|
||||||
options execOptions
|
options ExecOptions
|
||||||
client fakeClient
|
client fakeClient
|
||||||
expectedError string
|
expectedError string
|
||||||
expectedOut string
|
expectedOut string
|
||||||
|
@ -168,15 +168,15 @@ func TestRunExec(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
doc: "successful detach",
|
doc: "successful detach",
|
||||||
options: withDefaultOpts(execOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
container: "thecontainer",
|
Container: "thecontainer",
|
||||||
detach: true,
|
Detach: true,
|
||||||
}),
|
}),
|
||||||
client: fakeClient{execCreateFunc: execCreateWithID},
|
client: fakeClient{execCreateFunc: execCreateWithID},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
doc: "inspect error",
|
doc: "inspect error",
|
||||||
options: newExecOptions(),
|
options: NewExecOptions(),
|
||||||
client: fakeClient{
|
client: fakeClient{
|
||||||
inspectFunc: func(string) (types.ContainerJSON, error) {
|
inspectFunc: func(string) (types.ContainerJSON, error) {
|
||||||
return types.ContainerJSON{}, errors.New("failed inspect")
|
return types.ContainerJSON{}, errors.New("failed inspect")
|
||||||
|
@ -186,7 +186,7 @@ func TestRunExec(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
doc: "missing exec ID",
|
doc: "missing exec ID",
|
||||||
options: newExecOptions(),
|
options: NewExecOptions(),
|
||||||
expectedError: "exec ID empty",
|
expectedError: "exec ID empty",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ func TestRunExec(t *testing.T) {
|
||||||
t.Run(testcase.doc, func(t *testing.T) {
|
t.Run(testcase.doc, func(t *testing.T) {
|
||||||
cli := test.NewFakeCli(&testcase.client)
|
cli := test.NewFakeCli(&testcase.client)
|
||||||
|
|
||||||
err := runExec(cli, testcase.options)
|
err := RunExec(cli, testcase.options)
|
||||||
if testcase.expectedError != "" {
|
if testcase.expectedError != "" {
|
||||||
assert.ErrorContains(t, err, testcase.expectedError)
|
assert.ErrorContains(t, err, testcase.expectedError)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,58 +15,65 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type startOptions struct {
|
// StartOptions group options for `start` command
|
||||||
attach bool
|
type StartOptions struct {
|
||||||
openStdin bool
|
Attach bool
|
||||||
detachKeys string
|
OpenStdin bool
|
||||||
checkpoint string
|
DetachKeys string
|
||||||
checkpointDir string
|
Checkpoint string
|
||||||
|
CheckpointDir string
|
||||||
|
|
||||||
containers []string
|
Containers []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStartOptions creates a new StartOptions
|
||||||
|
func NewStartOptions() StartOptions {
|
||||||
|
return StartOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStartCommand creates a new cobra.Command for `docker start`
|
// NewStartCommand creates a new cobra.Command for `docker start`
|
||||||
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
var opts startOptions
|
var opts StartOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "start [OPTIONS] CONTAINER [CONTAINER...]",
|
Use: "start [OPTIONS] CONTAINER [CONTAINER...]",
|
||||||
Short: "Start one or more stopped containers",
|
Short: "Start one or more stopped containers",
|
||||||
Args: cli.RequiresMinArgs(1),
|
Args: cli.RequiresMinArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
opts.containers = args
|
opts.Containers = args
|
||||||
return runStart(dockerCli, &opts)
|
return RunStart(dockerCli, &opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")
|
flags.BoolVarP(&opts.Attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")
|
||||||
flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "Attach container's STDIN")
|
flags.BoolVarP(&opts.OpenStdin, "interactive", "i", false, "Attach container's STDIN")
|
||||||
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
flags.StringVar(&opts.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||||
|
|
||||||
flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint")
|
flags.StringVar(&opts.Checkpoint, "checkpoint", "", "Restore from this checkpoint")
|
||||||
flags.SetAnnotation("checkpoint", "experimental", nil)
|
flags.SetAnnotation("checkpoint", "experimental", nil)
|
||||||
flags.SetAnnotation("checkpoint", "ostype", []string{"linux"})
|
flags.SetAnnotation("checkpoint", "ostype", []string{"linux"})
|
||||||
flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
|
flags.StringVar(&opts.CheckpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
|
||||||
flags.SetAnnotation("checkpoint-dir", "experimental", nil)
|
flags.SetAnnotation("checkpoint-dir", "experimental", nil)
|
||||||
flags.SetAnnotation("checkpoint-dir", "ostype", []string{"linux"})
|
flags.SetAnnotation("checkpoint-dir", "ostype", []string{"linux"})
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunStart executes a `start` command
|
||||||
// nolint: gocyclo
|
// nolint: gocyclo
|
||||||
func runStart(dockerCli command.Cli, opts *startOptions) error {
|
func RunStart(dockerCli command.Cli, opts *StartOptions) error {
|
||||||
ctx, cancelFun := context.WithCancel(context.Background())
|
ctx, cancelFun := context.WithCancel(context.Background())
|
||||||
defer cancelFun()
|
defer cancelFun()
|
||||||
|
|
||||||
if opts.attach || opts.openStdin {
|
if opts.Attach || opts.OpenStdin {
|
||||||
// We're going to attach to a container.
|
// We're going to attach to a container.
|
||||||
// 1. Ensure we only have one container.
|
// 1. Ensure we only have one container.
|
||||||
if len(opts.containers) > 1 {
|
if len(opts.Containers) > 1 {
|
||||||
return errors.New("you cannot start and attach multiple containers at once")
|
return errors.New("you cannot start and attach multiple containers at once")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Attach to the container.
|
// 2. Attach to the container.
|
||||||
container := opts.containers[0]
|
container := opts.Containers[0]
|
||||||
c, err := dockerCli.Client().ContainerInspect(ctx, container)
|
c, err := dockerCli.Client().ContainerInspect(ctx, container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -79,13 +86,13 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.detachKeys != "" {
|
if opts.DetachKeys != "" {
|
||||||
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
|
dockerCli.ConfigFile().DetachKeys = opts.DetachKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
options := types.ContainerAttachOptions{
|
options := types.ContainerAttachOptions{
|
||||||
Stream: true,
|
Stream: true,
|
||||||
Stdin: opts.openStdin && c.Config.OpenStdin,
|
Stdin: opts.OpenStdin && c.Config.OpenStdin,
|
||||||
Stdout: true,
|
Stdout: true,
|
||||||
Stderr: true,
|
Stderr: true,
|
||||||
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
DetachKeys: dockerCli.ConfigFile().DetachKeys,
|
||||||
|
@ -129,8 +136,8 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||||
// no matter it's detached, removed on daemon side(--rm) or exit normally.
|
// no matter it's detached, removed on daemon side(--rm) or exit normally.
|
||||||
statusChan := waitExitOrRemoved(ctx, dockerCli, c.ID, c.HostConfig.AutoRemove)
|
statusChan := waitExitOrRemoved(ctx, dockerCli, c.ID, c.HostConfig.AutoRemove)
|
||||||
startOptions := types.ContainerStartOptions{
|
startOptions := types.ContainerStartOptions{
|
||||||
CheckpointID: opts.checkpoint,
|
CheckpointID: opts.Checkpoint,
|
||||||
CheckpointDir: opts.checkpointDir,
|
CheckpointDir: opts.CheckpointDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Start the container.
|
// 4. Start the container.
|
||||||
|
@ -161,21 +168,21 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||||
if status := <-statusChan; status != 0 {
|
if status := <-statusChan; status != 0 {
|
||||||
return cli.StatusError{StatusCode: status}
|
return cli.StatusError{StatusCode: status}
|
||||||
}
|
}
|
||||||
} else if opts.checkpoint != "" {
|
} else if opts.Checkpoint != "" {
|
||||||
if len(opts.containers) > 1 {
|
if len(opts.Containers) > 1 {
|
||||||
return errors.New("you cannot restore multiple containers at once")
|
return errors.New("you cannot restore multiple containers at once")
|
||||||
}
|
}
|
||||||
container := opts.containers[0]
|
container := opts.Containers[0]
|
||||||
startOptions := types.ContainerStartOptions{
|
startOptions := types.ContainerStartOptions{
|
||||||
CheckpointID: opts.checkpoint,
|
CheckpointID: opts.Checkpoint,
|
||||||
CheckpointDir: opts.checkpointDir,
|
CheckpointDir: opts.CheckpointDir,
|
||||||
}
|
}
|
||||||
return dockerCli.Client().ContainerStart(ctx, container, startOptions)
|
return dockerCli.Client().ContainerStart(ctx, container, startOptions)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We're not going to attach to anything.
|
// We're not going to attach to anything.
|
||||||
// Start as many containers as we want.
|
// Start as many containers as we want.
|
||||||
return startContainersWithoutAttachments(ctx, dockerCli, opts.containers)
|
return startContainersWithoutAttachments(ctx, dockerCli, opts.Containers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue