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