github.com/kobeld/docker@v1.12.0-rc1/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  		if !c.Config.Tty {
    67  			sigc := dockerCli.ForwardAllSignals(ctx, container)
    68  			defer signal.StopCatch(sigc)
    69  		}
    70  
    71  		if opts.detachKeys != "" {
    72  			dockerCli.ConfigFile().DetachKeys = opts.detachKeys
    73  		}
    74  
    75  		options := types.ContainerAttachOptions{
    76  			Stream:     true,
    77  			Stdin:      opts.openStdin && c.Config.OpenStdin,
    78  			Stdout:     true,
    79  			Stderr:     true,
    80  			DetachKeys: dockerCli.ConfigFile().DetachKeys,
    81  		}
    82  
    83  		var in io.ReadCloser
    84  
    85  		if options.Stdin {
    86  			in = dockerCli.In()
    87  		}
    88  
    89  		resp, errAttach := dockerCli.Client().ContainerAttach(ctx, container, options)
    90  		if errAttach != nil && errAttach != httputil.ErrPersistEOF {
    91  			// ContainerAttach return an ErrPersistEOF (connection closed)
    92  			// means server met an error and put it in Hijacked connection
    93  			// keep the error and read detailed error message from hijacked connection
    94  			return errAttach
    95  		}
    96  		defer resp.Close()
    97  		cErr := promise.Go(func() error {
    98  			errHijack := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
    99  			if errHijack == nil {
   100  				return errAttach
   101  			}
   102  			return errHijack
   103  		})
   104  
   105  		// 3. Start the container.
   106  		if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
   107  			cancelFun()
   108  			<-cErr
   109  			return err
   110  		}
   111  
   112  		// 4. Wait for attachment to break.
   113  		if c.Config.Tty && dockerCli.IsTerminalOut() {
   114  			if err := dockerCli.MonitorTtySize(ctx, container, false); err != nil {
   115  				fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err)
   116  			}
   117  		}
   118  		if attchErr := <-cErr; attchErr != nil {
   119  			return attchErr
   120  		}
   121  		_, status, err := getExitCode(dockerCli, ctx, container)
   122  		if err != nil {
   123  			return err
   124  		}
   125  		if status != 0 {
   126  			return cli.StatusError{StatusCode: status}
   127  		}
   128  	} else {
   129  		// We're not going to attach to anything.
   130  		// Start as many containers as we want.
   131  		return startContainersWithoutAttachments(dockerCli, ctx, opts.containers)
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func startContainersWithoutAttachments(dockerCli *client.DockerCli, ctx context.Context, containers []string) error {
   138  	var failedContainers []string
   139  	for _, container := range containers {
   140  		if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
   141  			fmt.Fprintf(dockerCli.Err(), "%s\n", err)
   142  			failedContainers = append(failedContainers, container)
   143  		} else {
   144  			fmt.Fprintf(dockerCli.Out(), "%s\n", container)
   145  		}
   146  	}
   147  
   148  	if len(failedContainers) > 0 {
   149  		return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
   150  	}
   151  	return nil
   152  }