github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/api/client/container/exec.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/docker/api/client"
    11  	"github.com/docker/docker/cli"
    12  	"github.com/docker/docker/pkg/promise"
    13  	"github.com/docker/engine-api/types"
    14  	"github.com/spf13/cobra"
    15  )
    16  
    17  type execOptions struct {
    18  	detachKeys  string
    19  	interactive bool
    20  	tty         bool
    21  	detach      bool
    22  	user        string
    23  	privileged  bool
    24  }
    25  
    26  // NewExecCommand creats a new cobra.Command for `docker exec`
    27  func NewExecCommand(dockerCli *client.DockerCli) *cobra.Command {
    28  	var opts execOptions
    29  
    30  	cmd := &cobra.Command{
    31  		Use:   "exec CONTAINER COMMAND [ARG...]",
    32  		Short: "Run a command in a running container",
    33  		Args:  cli.RequiresMinArgs(2),
    34  		RunE: func(cmd *cobra.Command, args []string) error {
    35  			container := args[0]
    36  			execCmd := args[1:]
    37  			return runExec(dockerCli, &opts, container, execCmd)
    38  		},
    39  	}
    40  
    41  	flags := cmd.Flags()
    42  	flags.SetInterspersed(false)
    43  
    44  	flags.StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container")
    45  	flags.BoolVarP(&opts.interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
    46  	flags.BoolVarP(&opts.tty, "tty", "t", false, "Allocate a pseudo-TTY")
    47  	flags.BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: run command in the background")
    48  	flags.StringVarP(&opts.user, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
    49  	flags.BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the command")
    50  
    51  	return cmd
    52  }
    53  
    54  func runExec(dockerCli *client.DockerCli, opts *execOptions, container string, execCmd []string) error {
    55  	execConfig, err := parseExec(opts, container, execCmd)
    56  	// just in case the ParseExec does not exit
    57  	if container == "" || err != nil {
    58  		return cli.StatusError{StatusCode: 1}
    59  	}
    60  
    61  	if opts.detachKeys != "" {
    62  		dockerCli.ConfigFile().DetachKeys = opts.detachKeys
    63  	}
    64  
    65  	// Send client escape keys
    66  	execConfig.DetachKeys = dockerCli.ConfigFile().DetachKeys
    67  
    68  	ctx := context.Background()
    69  
    70  	response, err := dockerCli.Client().ContainerExecCreate(ctx, container, *execConfig)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	execID := response.ID
    76  	if execID == "" {
    77  		fmt.Fprintf(dockerCli.Out(), "exec ID empty")
    78  		return nil
    79  	}
    80  
    81  	//Temp struct for execStart so that we don't need to transfer all the execConfig
    82  	if !execConfig.Detach {
    83  		if err := dockerCli.CheckTtyInput(execConfig.AttachStdin, execConfig.Tty); err != nil {
    84  			return err
    85  		}
    86  	} else {
    87  		execStartCheck := types.ExecStartCheck{
    88  			Detach: execConfig.Detach,
    89  			Tty:    execConfig.Tty,
    90  		}
    91  
    92  		if err := dockerCli.Client().ContainerExecStart(ctx, execID, execStartCheck); err != nil {
    93  			return err
    94  		}
    95  		// For now don't print this - wait for when we support exec wait()
    96  		// fmt.Fprintf(dockerCli.Out(), "%s\n", execID)
    97  		return nil
    98  	}
    99  
   100  	// Interactive exec requested.
   101  	var (
   102  		out, stderr io.Writer
   103  		in          io.ReadCloser
   104  		errCh       chan error
   105  	)
   106  
   107  	if execConfig.AttachStdin {
   108  		in = dockerCli.In()
   109  	}
   110  	if execConfig.AttachStdout {
   111  		out = dockerCli.Out()
   112  	}
   113  	if execConfig.AttachStderr {
   114  		if execConfig.Tty {
   115  			stderr = dockerCli.Out()
   116  		} else {
   117  			stderr = dockerCli.Err()
   118  		}
   119  	}
   120  
   121  	resp, err := dockerCli.Client().ContainerExecAttach(ctx, execID, *execConfig)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	defer resp.Close()
   126  	errCh = promise.Go(func() error {
   127  		return dockerCli.HoldHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
   128  	})
   129  
   130  	if execConfig.Tty && dockerCli.IsTerminalIn() {
   131  		if err := dockerCli.MonitorTtySize(ctx, execID, true); err != nil {
   132  			fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   133  		}
   134  	}
   135  
   136  	if err := <-errCh; err != nil {
   137  		logrus.Debugf("Error hijack: %s", err)
   138  		return err
   139  	}
   140  
   141  	var status int
   142  	if _, status, err = dockerCli.GetExecExitCode(ctx, execID); err != nil {
   143  		return err
   144  	}
   145  
   146  	if status != 0 {
   147  		return cli.StatusError{StatusCode: status}
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  // parseExec parses the specified args for the specified command and generates
   154  // an ExecConfig from it.
   155  func parseExec(opts *execOptions, container string, execCmd []string) (*types.ExecConfig, error) {
   156  	execConfig := &types.ExecConfig{
   157  		User:       opts.user,
   158  		Privileged: opts.privileged,
   159  		Tty:        opts.tty,
   160  		Cmd:        execCmd,
   161  		Detach:     opts.detach,
   162  		// container is not used here
   163  	}
   164  
   165  	// If -d is not set, attach to everything by default
   166  	if !opts.detach {
   167  		execConfig.AttachStdout = true
   168  		execConfig.AttachStderr = true
   169  		if opts.interactive {
   170  			execConfig.AttachStdin = true
   171  		}
   172  	}
   173  
   174  	return execConfig, nil
   175  }