github.com/portworx/docker@v1.12.1/api/client/container/start.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http/httputil"
     7  	"strings"
     8  
     9  	"golang.org/x/net/context"
    10  
    11  	"github.com/docker/docker/api/client"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/pkg/promise"
    14  	"github.com/docker/docker/pkg/signal"
    15  	"github.com/docker/engine-api/types"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  type startOptions struct {
    20  	attach     bool
    21  	openStdin  bool
    22  	detachKeys string
    23  
    24  	containers []string
    25  }
    26  
    27  // NewStartCommand creats a new cobra.Command for `docker start`
    28  func NewStartCommand(dockerCli *client.DockerCli) *cobra.Command {
    29  	var opts startOptions
    30  
    31  	cmd := &cobra.Command{
    32  		Use:   "start [OPTIONS] CONTAINER [CONTAINER...]",
    33  		Short: "Start one or more stopped containers",
    34  		Args:  cli.RequiresMinArgs(1),
    35  		RunE: func(cmd *cobra.Command, args []string) error {
    36  			opts.containers = args
    37  			return runStart(dockerCli, &opts)
    38  		},
    39  	}
    40  	cmd.SetFlagErrorFunc(flagErrorFunc)
    41  
    42  	flags := cmd.Flags()
    43  	flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")
    44  	flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "Attach container's STDIN")
    45  	flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
    46  	return cmd
    47  }
    48  
    49  func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
    50  	ctx, cancelFun := context.WithCancel(context.Background())
    51  
    52  	if opts.attach || opts.openStdin {
    53  		// We're going to attach to a container.
    54  		// 1. Ensure we only have one container.
    55  		if len(opts.containers) > 1 {
    56  			return fmt.Errorf("You cannot start and attach multiple containers at once.")
    57  		}
    58  
    59  		// 2. Attach to the container.
    60  		container := opts.containers[0]
    61  		c, err := dockerCli.Client().ContainerInspect(ctx, container)
    62  		if err != nil {
    63  			return err
    64  		}
    65  
    66  		// We always use c.ID instead of container to maintain consistency during `docker start`
    67  		if !c.Config.Tty {
    68  			sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
    69  			defer signal.StopCatch(sigc)
    70  		}
    71  
    72  		if opts.detachKeys != "" {
    73  			dockerCli.ConfigFile().DetachKeys = opts.detachKeys
    74  		}
    75  
    76  		options := types.ContainerAttachOptions{
    77  			Stream:     true,
    78  			Stdin:      opts.openStdin && c.Config.OpenStdin,
    79  			Stdout:     true,
    80  			Stderr:     true,
    81  			DetachKeys: dockerCli.ConfigFile().DetachKeys,
    82  		}
    83  
    84  		var in io.ReadCloser
    85  
    86  		if options.Stdin {
    87  			in = dockerCli.In()
    88  		}
    89  
    90  		resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
    91  		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
    92  			// ContainerAttach return an ErrPersistEOF (connection closed)
    93  			// means server met an error and put it in Hijacked connection
    94  			// keep the error and read detailed error message from hijacked connection
    95  			return errAttach
    96  		}
    97  		defer resp.Close()
    98  		cErr := promise.Go(func() error {
    99  			errHijack := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
   100  			if errHijack == nil {
   101  				return errAttach
   102  			}
   103  			return errHijack
   104  		})
   105  
   106  		// 3. Start the container.
   107  		if err := dockerCli.Client().ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil {
   108  			cancelFun()
   109  			<-cErr
   110  			return err
   111  		}
   112  
   113  		// 4. Wait for attachment to break.
   114  		if c.Config.Tty && dockerCli.IsTerminalOut() {
   115  			if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil {
   116  				fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   117  			}
   118  		}
   119  		if attchErr := <-cErr; attchErr != nil {
   120  			return attchErr
   121  		}
   122  		_, status, err := getExitCode(dockerCli, ctx, c.ID)
   123  		if err != nil {
   124  			return err
   125  		}
   126  		if status != 0 {
   127  			return cli.StatusError{StatusCode: status}
   128  		}
   129  	} else {
   130  		// We're not going to attach to anything.
   131  		// Start as many containers as we want.
   132  		return startContainersWithoutAttachments(dockerCli, ctx, opts.containers)
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func startContainersWithoutAttachments(dockerCli *client.DockerCli, ctx context.Context, containers []string) error {
   139  	var failedContainers []string
   140  	for _, container := range containers {
   141  		if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
   142  			fmt.Fprintf(dockerCli.Err(), "%s\n", err)
   143  			failedContainers = append(failedContainers, container)
   144  		} else {
   145  			fmt.Fprintf(dockerCli.Out(), "%s\n", container)
   146  		}
   147  	}
   148  
   149  	if len(failedContainers) > 0 {
   150  		return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
   151  	}
   152  	return nil
   153  }