github.com/DaoCloud/dao@v0.0.0-20161212064103-c3dbfd13ee36/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: "启动一个或多个停止的容器",
    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  
    41  	flags := cmd.Flags()
    42  	flags.BoolVarP(&opts.attach, "attach", "a", false, "附件标准输出/标准错误,同时转发信号")
    43  	flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "附加容器的标准输入")
    44  	flags.StringVar(&opts.detachKeys, "detach-keys", "", "覆盖从一个容器退出附加操作时的按键顺序")
    45  	return cmd
    46  }
    47  
    48  func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
    49  	ctx, cancelFun := context.WithCancel(context.Background())
    50  
    51  	if opts.attach || opts.openStdin {
    52  		// We're going to attach to a container.
    53  		// 1. Ensure we only have one container.
    54  		if len(opts.containers) > 1 {
    55  			return fmt.Errorf("您不能一次性启动和附加到多个容器。")
    56  		}
    57  
    58  		// 2. Attach to the container.
    59  		container := opts.containers[0]
    60  		c, err := dockerCli.Client().ContainerInspect(ctx, container)
    61  		if err != nil {
    62  			return err
    63  		}
    64  
    65  		// We always use c.ID instead of container to maintain consistency during `docker start`
    66  		if !c.Config.Tty {
    67  			sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
    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, c.ID, 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, c.ID, 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, c.ID, false); err != nil {
   115  				fmt.Fprintf(dockerCli.Err(), "监视终端大小出错: %s\n", err)
   116  			}
   117  		}
   118  		if attchErr := <-cErr; attchErr != nil {
   119  			return attchErr
   120  		}
   121  		_, status, err := getExitCode(dockerCli, ctx, c.ID)
   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("错误: 启动容器失败:  %v", strings.Join(failedContainers, ", "))
   150  	}
   151  	return nil
   152  }