mirror of https://github.com/docker/cli.git
Merge pull request #4637 from ndeloof/RunExecWithContextb
make runAttach public and allow passing context
This commit is contained in:
commit
ad861cdb39
|
@ -17,12 +17,11 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type attachOptions struct {
|
// AttachOptions group options for `attach` command
|
||||||
noStdin bool
|
type AttachOptions struct {
|
||||||
proxy bool
|
NoStdin bool
|
||||||
detachKeys string
|
Proxy bool
|
||||||
|
DetachKeys string
|
||||||
container string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func inspectContainerAndCheckState(ctx context.Context, cli client.APIClient, args string) (*types.ContainerJSON, error) {
|
func inspectContainerAndCheckState(ctx context.Context, cli client.APIClient, args string) (*types.ContainerJSON, error) {
|
||||||
|
@ -45,15 +44,16 @@ func inspectContainerAndCheckState(ctx context.Context, cli client.APIClient, ar
|
||||||
|
|
||||||
// NewAttachCommand creates a new cobra.Command for `docker attach`
|
// NewAttachCommand creates a new cobra.Command for `docker attach`
|
||||||
func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
|
func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
var opts attachOptions
|
var opts AttachOptions
|
||||||
|
var container string
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "attach [OPTIONS] CONTAINER",
|
Use: "attach [OPTIONS] CONTAINER",
|
||||||
Short: "Attach local standard input, output, and error streams to a running container",
|
Short: "Attach local standard input, output, and error streams to a running container",
|
||||||
Args: cli.ExactArgs(1),
|
Args: cli.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
opts.container = args[0]
|
container = args[0]
|
||||||
return runAttach(dockerCli, &opts)
|
return RunAttach(context.Background(), dockerCli, container, &opts)
|
||||||
},
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"aliases": "docker container attach, docker attach",
|
"aliases": "docker container attach, docker attach",
|
||||||
|
@ -64,36 +64,36 @@ func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
|
flags.BoolVar(&opts.NoStdin, "no-stdin", false, "Do not attach STDIN")
|
||||||
flags.BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
flags.BoolVar(&opts.Proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
||||||
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")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
// RunAttach executes an `attach` command
|
||||||
ctx := context.Background()
|
func RunAttach(ctx context.Context, dockerCli command.Cli, target string, opts *AttachOptions) error {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
|
|
||||||
// request channel to wait for client
|
// request channel to wait for client
|
||||||
resultC, errC := apiClient.ContainerWait(ctx, opts.container, "")
|
resultC, errC := apiClient.ContainerWait(ctx, target, "")
|
||||||
|
|
||||||
c, err := inspectContainerAndCheckState(ctx, apiClient, opts.container)
|
c, err := inspectContainerAndCheckState(ctx, apiClient, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dockerCli.In().CheckTty(!opts.noStdin, c.Config.Tty); err != nil {
|
if err := dockerCli.In().CheckTty(!opts.NoStdin, c.Config.Tty); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
detachKeys := dockerCli.ConfigFile().DetachKeys
|
detachKeys := dockerCli.ConfigFile().DetachKeys
|
||||||
if opts.detachKeys != "" {
|
if opts.DetachKeys != "" {
|
||||||
detachKeys = opts.detachKeys
|
detachKeys = opts.DetachKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
options := container.AttachOptions{
|
options := container.AttachOptions{
|
||||||
Stream: true,
|
Stream: true,
|
||||||
Stdin: !opts.noStdin && c.Config.OpenStdin,
|
Stdin: !opts.NoStdin && c.Config.OpenStdin,
|
||||||
Stdout: true,
|
Stdout: true,
|
||||||
Stderr: true,
|
Stderr: true,
|
||||||
DetachKeys: detachKeys,
|
DetachKeys: detachKeys,
|
||||||
|
@ -104,13 +104,13 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
||||||
in = dockerCli.In()
|
in = dockerCli.In()
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.proxy && !c.Config.Tty {
|
if opts.Proxy && !c.Config.Tty {
|
||||||
sigc := notifyAllSignals()
|
sigc := notifyAllSignals()
|
||||||
go ForwardAllSignals(ctx, dockerCli, opts.container, sigc)
|
go ForwardAllSignals(ctx, dockerCli, target, sigc)
|
||||||
defer signal.StopCatch(sigc)
|
defer signal.StopCatch(sigc)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errAttach := apiClient.ContainerAttach(ctx, opts.container, options)
|
resp, errAttach := apiClient.ContainerAttach(ctx, target, options)
|
||||||
if errAttach != nil {
|
if errAttach != nil {
|
||||||
return errAttach
|
return errAttach
|
||||||
}
|
}
|
||||||
|
@ -124,13 +124,13 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
||||||
// the container and not exit.
|
// the container and not exit.
|
||||||
//
|
//
|
||||||
// Recheck the container's state to avoid attach block.
|
// Recheck the container's state to avoid attach block.
|
||||||
_, err = inspectContainerAndCheckState(ctx, apiClient, opts.container)
|
_, err = inspectContainerAndCheckState(ctx, apiClient, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Config.Tty && dockerCli.Out().IsTerminal() {
|
if c.Config.Tty && dockerCli.Out().IsTerminal() {
|
||||||
resizeTTY(ctx, dockerCli, opts.container)
|
resizeTTY(ctx, dockerCli, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
streamer := hijackedIOStreamer{
|
streamer := hijackedIOStreamer{
|
||||||
|
|
|
@ -28,7 +28,6 @@ type ExecOptions struct {
|
||||||
Privileged bool
|
Privileged bool
|
||||||
Env opts.ListOpts
|
Env opts.ListOpts
|
||||||
Workdir string
|
Workdir string
|
||||||
Container string
|
|
||||||
Command []string
|
Command []string
|
||||||
EnvFile opts.ListOpts
|
EnvFile opts.ListOpts
|
||||||
}
|
}
|
||||||
|
@ -44,15 +43,16 @@ func NewExecOptions() ExecOptions {
|
||||||
// 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()
|
||||||
|
var container string
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
|
Use: "exec [OPTIONS] CONTAINER COMMAND [ARG...]",
|
||||||
Short: "Execute a command in a running container",
|
Short: "Execute 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]
|
container = args[0]
|
||||||
options.Command = args[1:]
|
options.Command = args[1:]
|
||||||
return RunExec(dockerCli, options)
|
return RunExec(context.Background(), dockerCli, container, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(container types.Container) bool {
|
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(container types.Container) bool {
|
||||||
return container.State != "paused"
|
return container.State != "paused"
|
||||||
|
@ -96,20 +96,19 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunExec executes an `exec` command
|
// RunExec executes an `exec` command
|
||||||
func RunExec(dockerCli command.Cli, options ExecOptions) error {
|
func RunExec(ctx context.Context, dockerCli command.Cli, container string, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
||||||
// We need to check the tty _before_ we do the ContainerExecCreate, because
|
// We need to check the tty _before_ we do the ContainerExecCreate, because
|
||||||
// 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, container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !execConfig.Detach {
|
if !execConfig.Detach {
|
||||||
|
@ -120,7 +119,7 @@ func RunExec(dockerCli command.Cli, options ExecOptions) error {
|
||||||
|
|
||||||
fillConsoleSize(execConfig, dockerCli)
|
fillConsoleSize(execConfig, dockerCli)
|
||||||
|
|
||||||
response, err := client.ContainerExecCreate(ctx, options.Container, *execConfig)
|
response, err := client.ContainerExecCreate(ctx, container, *execConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,7 @@ func TestRunExec(t *testing.T) {
|
||||||
{
|
{
|
||||||
doc: "successful detach",
|
doc: "successful detach",
|
||||||
options: withDefaultOpts(ExecOptions{
|
options: withDefaultOpts(ExecOptions{
|
||||||
Container: "thecontainer",
|
Detach: true,
|
||||||
Detach: true,
|
|
||||||
}),
|
}),
|
||||||
client: fakeClient{execCreateFunc: execCreateWithID},
|
client: fakeClient{execCreateFunc: execCreateWithID},
|
||||||
},
|
},
|
||||||
|
@ -195,7 +194,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(context.Background(), cli, "thecontainer", testcase.options)
|
||||||
if testcase.expectedError != "" {
|
if testcase.expectedError != "" {
|
||||||
assert.ErrorContains(t, err, testcase.expectedError)
|
assert.ErrorContains(t, err, testcase.expectedError)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue