-
Notifications
You must be signed in to change notification settings - Fork 950
/
Copy pathrun.go
199 lines (169 loc) · 5.43 KB
/
run.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package main
import (
"context"
"fmt"
"io"
"os"
"strings"
"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/pkg/ioutils"
"github.com/spf13/cobra"
)
// runDescription is used to describe run command in detail and auto generate command doc.
var runDescription = "Create a container object in Pouchd, and start the container. " +
"This is useful when you just want to use one command to start a container. "
// RunCommand use to implement 'run' command, it creates and starts a container.
type RunCommand struct {
baseCommand
*container
detachKeys string
attach bool
stdin bool
detach bool
}
// Init initialize run command.
func (rc *RunCommand) Init(c *Cli) {
rc.cli = c
rc.cmd = &cobra.Command{
Use: "run [OPTIONS] IMAGE [ARG...]",
Short: "Create a new container and start it",
Long: runDescription,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return rc.runRun(args)
},
Example: runExample(),
}
rc.addFlags()
}
// addFlags adds flags for specific command.
func (rc *RunCommand) addFlags() {
flagSet := rc.cmd.Flags()
flagSet.SetInterspersed(false)
c := addCommonFlags(flagSet)
rc.container = c
flagSet.StringVar(&rc.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
flagSet.BoolVarP(&rc.attach, "attach", "a", false, "Attach container's STDOUT and STDERR")
flagSet.BoolVarP(&rc.stdin, "interactive", "i", false, "Attach container's STDIN")
flagSet.BoolVarP(&rc.detach, "detach", "d", false, "Run container in background and print container ID")
flagSet.BoolVar(&rc.rm, "rm", false, "Automatically remove the container after it exits")
}
// runRun is the entry of run command.
func (rc *RunCommand) runRun(args []string) error {
config, err := rc.config()
if err != nil {
return fmt.Errorf("failed to run container: %v", err)
}
//collect all the environment variables for the container
config.Env, err = readKVStrings(rc.envfile, rc.env)
if err != nil {
return nil
}
config.Image = args[0]
if len(args) > 1 {
config.Cmd = args[1:]
}
containerName := rc.name
config.ContainerConfig.OpenStdin = rc.stdin
ctx := context.Background()
apiClient := rc.cli.Client()
if err := pullMissingImage(ctx, apiClient, config.Image, false); err != nil {
return err
}
result, err := apiClient.ContainerCreate(ctx, config.ContainerConfig, config.HostConfig, config.NetworkingConfig, containerName)
if err != nil {
return fmt.Errorf("failed to run container: %v", err)
}
if len(result.Warnings) != 0 {
fmt.Printf("WARNING: %s \n", strings.Join(result.Warnings, "\n"))
}
// pouch run not specify --name
if containerName == "" {
containerName = result.Name
}
if (rc.attach || rc.stdin) && rc.detach {
return fmt.Errorf("Conflicting options: -a (or -i) and -d")
}
if rc.rm && rc.detach {
return fmt.Errorf("Conflicting options: --rm and -d")
}
// default attach container's stdout and stderr
if !rc.detach {
rc.attach = true
}
wait := make(chan struct{})
if err := checkTty(rc.stdin, rc.tty, os.Stdout.Fd()); err != nil {
return err
}
if rc.attach || rc.stdin {
if rc.tty {
in, out, err := setRawMode(rc.stdin, false)
if err != nil {
return fmt.Errorf("failed to set raw mode")
}
defer func() {
if err := restoreMode(in, out); err != nil {
fmt.Fprintf(os.Stderr, "failed to restore term mode")
}
}()
}
conn, br, err := apiClient.ContainerAttach(ctx, containerName, rc.stdin)
if err != nil {
return fmt.Errorf("failed to attach container: %v", err)
}
defer conn.Close()
go func() {
io.Copy(os.Stdout, br)
wait <- struct{}{}
}()
go func() {
io.Copy(conn, os.Stdin)
// close write if receive CTRL-D
if cw, ok := conn.(ioutils.CloseWriter); ok {
cw.CloseWrite()
}
}()
}
// start container
if err := apiClient.ContainerStart(ctx, containerName, types.ContainerStartOptions{
DetachKeys: rc.detachKeys,
}); err != nil {
return fmt.Errorf("failed to run container %s: %v", containerName, err)
}
// wait the io to finish
if rc.attach || rc.stdin {
<-wait
} else {
fmt.Fprintf(os.Stdout, "%s\n", result.ID)
}
info, err := apiClient.ContainerGet(ctx, containerName)
if err != nil {
return err
}
if rc.rm {
if err := apiClient.ContainerRemove(ctx, containerName, &types.ContainerRemoveOptions{Force: true}); err != nil {
return fmt.Errorf("failed to remove container %s: %v", containerName, err)
}
}
code := info.State.ExitCode
if code != 0 {
return ExitError{Code: int(code)}
}
return nil
}
// runExample shows examples in run command, and is used in auto-generated cli docs.
func runExample() string {
return `$ pouch run --name test registry.hub.docker.com/library/busybox:latest echo "hi"
hi
$ pouch ps -a
Name ID Status Image Runtime Created
test 23f852 stopped registry.hub.docker.com/library/busybox:latest runc 4 seconds ago
$ pouch run -d --name test registry.hub.docker.com/library/busybox:latest
90719b5f9a455b3314a49e72e3ecb9962f215e0f90153aa8911882acf2ba2c84
$ pouch ps -a
Name ID Status Image Runtime Created
test 90719b stopped registry.hub.docker.com/library/busybox:latest runc 5 seconds ago
$ pouch run --device /dev/zero:/dev/testDev:rwm --name test registry.hub.docker.com/library/busybox:latest ls -l /dev/testDev
crw-rw-rw- 1 root root 1, 3 Jan 8 09:40 /dev/testnull
`
}