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