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 }