/
start.go
152 lines (130 loc) · 4.29 KB
/
start.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package container
import (
"fmt"
"io"
"net/http/httputil"
"strings"
"golang.org/x/net/context"
"github.com/docker/docker/api/client"
"github.com/docker/docker/cli"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/signal"
"github.com/docker/engine-api/types"
"github.com/spf13/cobra"
)
type startOptions struct {
attach bool
openStdin bool
detachKeys string
containers []string
}
// NewStartCommand creats a new cobra.Command for `docker start`
func NewStartCommand(dockerCli *client.DockerCli) *cobra.Command {
var opts startOptions
cmd := &cobra.Command{
Use: "start [OPTIONS] CONTAINER [CONTAINER...]",
Short: "启动一个或多个停止的容器",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.containers = args
return runStart(dockerCli, &opts)
},
}
flags := cmd.Flags()
flags.BoolVarP(&opts.attach, "attach", "a", false, "附件标准输出/标准错误,同时转发信号")
flags.BoolVarP(&opts.openStdin, "interactive", "i", false, "附加容器的标准输入")
flags.StringVar(&opts.detachKeys, "detach-keys", "", "覆盖从一个容器退出附加操作时的按键顺序")
return cmd
}
func runStart(dockerCli *client.DockerCli, opts *startOptions) error {
ctx, cancelFun := context.WithCancel(context.Background())
if opts.attach || opts.openStdin {
// We're going to attach to a container.
// 1. Ensure we only have one container.
if len(opts.containers) > 1 {
return fmt.Errorf("您不能一次性启动和附加到多个容器。")
}
// 2. Attach to the container.
container := opts.containers[0]
c, err := dockerCli.Client().ContainerInspect(ctx, container)
if err != nil {
return err
}
// We always use c.ID instead of container to maintain consistency during `docker start`
if !c.Config.Tty {
sigc := dockerCli.ForwardAllSignals(ctx, c.ID)
defer signal.StopCatch(sigc)
}
if opts.detachKeys != "" {
dockerCli.ConfigFile().DetachKeys = opts.detachKeys
}
options := types.ContainerAttachOptions{
Stream: true,
Stdin: opts.openStdin && c.Config.OpenStdin,
Stdout: true,
Stderr: true,
DetachKeys: dockerCli.ConfigFile().DetachKeys,
}
var in io.ReadCloser
if options.Stdin {
in = dockerCli.In()
}
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach return an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
// keep the error and read detailed error message from hijacked connection
return errAttach
}
defer resp.Close()
cErr := promise.Go(func() error {
errHijack := dockerCli.HoldHijackedConnection(ctx, c.Config.Tty, in, dockerCli.Out(), dockerCli.Err(), resp)
if errHijack == nil {
return errAttach
}
return errHijack
})
// 3. Start the container.
if err := dockerCli.Client().ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil {
cancelFun()
<-cErr
return err
}
// 4. Wait for attachment to break.
if c.Config.Tty && dockerCli.IsTerminalOut() {
if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil {
fmt.Fprintf(dockerCli.Err(), "监视终端大小出错: %s\n", err)
}
}
if attchErr := <-cErr; attchErr != nil {
return attchErr
}
_, status, err := getExitCode(dockerCli, ctx, c.ID)
if err != nil {
return err
}
if status != 0 {
return cli.StatusError{StatusCode: status}
}
} else {
// We're not going to attach to anything.
// Start as many containers as we want.
return startContainersWithoutAttachments(dockerCli, ctx, opts.containers)
}
return nil
}
func startContainersWithoutAttachments(dockerCli *client.DockerCli, ctx context.Context, containers []string) error {
var failedContainers []string
for _, container := range containers {
if err := dockerCli.Client().ContainerStart(ctx, container, types.ContainerStartOptions{}); err != nil {
fmt.Fprintf(dockerCli.Err(), "%s\n", err)
failedContainers = append(failedContainers, container)
} else {
fmt.Fprintf(dockerCli.Out(), "%s\n", container)
}
}
if len(failedContainers) > 0 {
return fmt.Errorf("错误: 启动容器失败: %v", strings.Join(failedContainers, ", "))
}
return nil
}