github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/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/types"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/cli/command"
    14  	"github.com/docker/docker/pkg/promise"
    15  	"github.com/docker/docker/pkg/signal"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  type startOptions struct {
    20  	attach     bool
    21  	openStdin  bool
    22  	detachKeys string
    23  	checkpoint string
    24  
    25  	containers []string
    26  }
    27  
    28  // NewStartCommand creates a new cobra.Command for `docker start`
    29  func NewStartCommand(dockerCli *command.DockerCli) *cobra.Command {
    30  	var opts startOptions
    31  
    32  	cmd := &cobra.Command{
    33  		Use:   "start [OPTIONS] CONTAINER [CONTAINER...]",
    34  		Short: "Start one or more stopped containers",
    35  		Args:  cli.RequiresMinArgs(1),
    36  		RunE: func(cmd *cobra.Command, args []string) error {
    37  			opts.containers = args
    38  			return runStart(dockerCli, &opts)
    39  		},
    40  	}
    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  
    47  	addExperimentalStartFlags(flags, &opts)
    48  
    49  	return cmd
    50  }
    51  
    52  func runStart(dockerCli *command.DockerCli, opts *startOptions) error {
    53  	ctx, cancelFun := context.WithCancel(context.Background())
    54  
    55  	if opts.attach || opts.openStdin {
    56  		// We're going to attach to a container.
    57  		// 1. Ensure we only have one container.
    58  		if len(opts.containers) > 1 {
    59  			return fmt.Errorf("You cannot start and attach multiple containers at once.")
    60  		}
    61  
    62  		// 2. Attach to the container.
    63  		container := opts.containers[0]
    64  		c, err := dockerCli.Client().ContainerInspect(ctx, container)
    65  		if err != nil {
    66  			return err
    67  		}
    68  
    69  		// We always use c.ID instead of container to maintain consistency during `docker start`
    70  		if !c.Config.Tty {
    71  			sigc := ForwardAllSignals(ctx, dockerCli, c.ID)
    72  			defer signal.StopCatch(sigc)
    73  		}
    74  
    75  		if opts.detachKeys != "" {
    76  			dockerCli.ConfigFile().DetachKeys = opts.detachKeys
    77  		}
    78  
    79  		options := types.ContainerAttachOptions{
    80  			Stream:     true,
    81  			Stdin:      opts.openStdin && c.Config.OpenStdin,
    82  			Stdout:     true,
    83  			Stderr:     true,
    84  			DetachKeys: dockerCli.ConfigFile().DetachKeys,
    85  		}
    86  
    87  		var in io.ReadCloser
    88  
    89  		if options.Stdin {
    90  			in = dockerCli.In()
    91  		}
    92  
    93  		resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
    94  		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
    95  			// ContainerAttach return an ErrPersistEOF (connection closed)
    96  			// means server met an error and already put it in Hijacked connection,
    97  			// we would keep the error and read the detailed error message from hijacked connection
    98  			return errAttach
    99  		}
   100  		defer resp.Close()
   101  		cErr := promise.Go(func() error {
   102  			errHijack := holdHijackedConnection(ctx, dockerCli, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
   103  			if errHijack == nil {
   104  				return errAttach
   105  			}
   106  			return errHijack
   107  		})
   108  
   109  		// 3. We should open a channel for receiving status code of the container
   110  		// no matter it's detached, removed on daemon side(--rm) or exit normally.
   111  		statusChan := waitExitOrRemoved(dockerCli, ctx, c.ID, c.HostConfig.AutoRemove)
   112  		startOptions := types.ContainerStartOptions{
   113  			CheckpointID: opts.checkpoint,
   114  		}
   115  
   116  		// 4. Start the container.
   117  		if err := dockerCli.Client().ContainerStart(ctx, c.ID, startOptions); err != nil {
   118  			cancelFun()
   119  			<-cErr
   120  			if c.HostConfig.AutoRemove {
   121  				// wait container to be removed
   122  				<-statusChan
   123  			}
   124  			return err
   125  		}
   126  
   127  		// 5. Wait for attachment to break.
   128  		if c.Config.Tty && dockerCli.Out().IsTerminal() {
   129  			if err := MonitorTtySize(ctx, dockerCli, c.ID, false); err != nil {
   130  				fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   131  			}
   132  		}
   133  		if attchErr := <-cErr; attchErr != nil {
   134  			return attchErr
   135  		}
   136  
   137  		if status := <-statusChan; status != 0 {
   138  			return cli.StatusError{StatusCode: status}
   139  		}
   140  	} else if opts.checkpoint != "" {
   141  		if len(opts.containers) > 1 {
   142  			return fmt.Errorf("You cannot restore multiple containers at once.")
   143  		}
   144  		container := opts.containers[0]
   145  		startOptions := types.ContainerStartOptions{
   146  			CheckpointID: opts.checkpoint,
   147  		}
   148  		return dockerCli.Client().ContainerStart(ctx, container, startOptions)
   149  
   150  	} else {
   151  		// We're not going to attach to anything.
   152  		// Start as many containers as we want.
   153  		return startContainersWithoutAttachments(dockerCli, ctx, opts.containers)
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func startContainersWithoutAttachments(dockerCli *command.DockerCli, ctx context.Context, containers []string) error {
   160  	var failedContainers []string
   161  	for _, container := range containers {
   162  		if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
   163  			fmt.Fprintf(dockerCli.Err(), "%s\n", err)
   164  			failedContainers = append(failedContainers, container)
   165  		} else {
   166  			fmt.Fprintf(dockerCli.Out(), "%s\n", container)
   167  		}
   168  	}
   169  
   170  	if len(failedContainers) > 0 {
   171  		return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
   172  	}
   173  	return nil
   174  }