Skip to content

Commit 6729e51

Browse files
authored
Merge pull request #1099 from Ace-Tang/exit_code
bugfix: make container exit with real exit code
2 parents 7a3a988 + 8219fd1 commit 6729e51

File tree

9 files changed

+102
-15
lines changed

9 files changed

+102
-15
lines changed

cli/cli.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,14 @@ func (c *Cli) Print(obj interface{}) {
140140

141141
display.Flush()
142142
}
143+
144+
// ExitError defines exit error produce by cli commands.
145+
type ExitError struct {
146+
Code int
147+
Status string
148+
}
149+
150+
// Error inplements error interface.
151+
func (e ExitError) Error() string {
152+
return fmt.Sprintf("Exit Code: %d, Status: %s", e.Code, e.Status)
153+
}

cli/main.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ func main() {
4747
cli.AddCommand(base, &GenDocCommand{})
4848

4949
if err := cli.Run(); err != nil {
50+
// deal with ExitError, which should be recognize as error, and should
51+
// not be exit with status 0.
52+
if exitErr, ok := err.(ExitError); ok {
53+
if exitErr.Status != "" {
54+
fmt.Fprintln(os.Stderr, exitErr.Status)
55+
}
56+
if exitErr.Code == 0 {
57+
// when get error with ExitError, code should not be 0.
58+
exitErr.Code = 1
59+
}
60+
os.Exit(exitErr.Code)
61+
}
62+
63+
// not ExitError, print error to os.Stderr, exit code 1.
5064
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
5165
os.Exit(1)
5266
}

cli/run.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ func (rc *RunCommand) runRun(args []string) error {
108108
if err != nil {
109109
return fmt.Errorf("failed to attach container: %v", err)
110110
}
111+
defer conn.Close()
111112

112113
go func() {
113114
io.Copy(os.Stdout, br)
114115
wait <- struct{}{}
115116
}()
116117
go func() {
117118
io.Copy(conn, os.Stdin)
118-
wait <- struct{}{}
119119
}()
120120
}
121121

@@ -130,6 +130,17 @@ func (rc *RunCommand) runRun(args []string) error {
130130
} else {
131131
fmt.Fprintf(os.Stdout, "%s\n", result.ID)
132132
}
133+
134+
info, err := apiClient.ContainerGet(ctx, containerName)
135+
if err != nil {
136+
137+
}
138+
139+
code := info.State.ExitCode
140+
if code != 0 {
141+
return ExitError{Code: int(code)}
142+
}
143+
133144
return nil
134145
}
135146

cli/start.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func (s *StartCommand) runStart(args []string) error {
7272
if err != nil {
7373
return fmt.Errorf("failed to attach container: %v", err)
7474
}
75+
defer conn.Close()
7576

7677
wait = make(chan struct{})
7778
go func() {
@@ -80,7 +81,6 @@ func (s *StartCommand) runStart(args []string) error {
8081
}()
8182
go func() {
8283
io.Copy(conn, os.Stdin)
83-
close(wait)
8484
}()
8585
}
8686

@@ -93,6 +93,17 @@ func (s *StartCommand) runStart(args []string) error {
9393
if s.attach || s.stdin {
9494
<-wait
9595
}
96+
97+
info, err := apiClient.ContainerGet(ctx, container)
98+
if err != nil {
99+
100+
}
101+
102+
code := info.State.ExitCode
103+
if code != 0 {
104+
return ExitError{Code: int(code)}
105+
}
106+
96107
return nil
97108
}
98109

daemon/mgr/container.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,7 @@ func (mgr *ContainerManager) createContainerdContainer(ctx context.Context, c *C
626626
return errors.Wrapf(err, "failed to get PID of container: %s", c.ID())
627627
}
628628
c.meta.State.Pid = int64(pid)
629+
c.meta.State.ExitCode = 0
629630
} else {
630631
c.meta.State.FinishedAt = time.Now().UTC().Format(utils.TimeLayout)
631632
c.meta.State.Error = err.Error()

test/cli_network_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func (suite *PouchNetworkSuite) TestNetworkBridgeWorks(c *check.C) {
139139
ExitCode: 1,
140140
Err: "has active endpoints",
141141
}
142-
command.PouchRun("run", "--name", funcname, "--net", funcname, busyboxImage, "top").Assert(c, icmd.Success)
142+
command.PouchRun("run", "-d", "--name", funcname, "--net", funcname, busyboxImage, "top").Assert(c, icmd.Success)
143143

144144
err := command.PouchRun("network", "remove", funcname).Compare(expct)
145145
c.Assert(err, check.IsNil)
@@ -181,7 +181,7 @@ func (suite *PouchNetworkSuite) TestNetworkBridgeWorks(c *check.C) {
181181
}
182182
{
183183
// running container is stopped, then the veth device should also been removed
184-
command.PouchRun("run", "--name", funcname, "--net", funcname, busyboxImage, "top").Assert(c, icmd.Success)
184+
command.PouchRun("run", "-d", "--name", funcname, "--net", funcname, busyboxImage, "top").Assert(c, icmd.Success)
185185
command.PouchRun("stop", funcname).Assert(c, icmd.Success)
186186

187187
// get the ID of bridge to construct the bridge name.

test/cli_run_test.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func (suite *PouchRunSuite) TestRunInWrongWay(c *check.C) {
185185
func (suite *PouchRunSuite) TestRunEnableLxcfs(c *check.C) {
186186
name := "test-run-lxcfs"
187187

188-
command.PouchRun("run", "--name", name, "-m", "512M", "--enableLxcfs=true",
188+
command.PouchRun("run", "-d", "--name", name, "-m", "512M", "--enableLxcfs=true",
189189
busyboxImage, "sleep", "10000").Assert(c, icmd.Success)
190190

191191
res := command.PouchRun("exec", name, "head", "-n", "5", "/proc/meminfo")
@@ -237,7 +237,7 @@ func (suite *PouchRunSuite) TestRunRestartPolicyNone(c *check.C) {
237237
func (suite *PouchRunSuite) TestRunWithIPCMode(c *check.C) {
238238
name := "test-run-with-ipc-mode"
239239

240-
res := command.PouchRun("run", "--name", name, "--ipc", "host", busyboxImage)
240+
res := command.PouchRun("run", "-d", "--name", name, "--ipc", "host", busyboxImage)
241241
res.Assert(c, icmd.Success)
242242
DelContainerForceMultyTime(c, name)
243243
}
@@ -247,7 +247,7 @@ func (suite *PouchRunSuite) TestRunWithIPCMode(c *check.C) {
247247
func (suite *PouchRunSuite) TestRunWithPIDMode(c *check.C) {
248248
name := "test-run-with-pid-mode"
249249

250-
res := command.PouchRun("run", "--name", name, "--pid", "host", busyboxImage)
250+
res := command.PouchRun("run", "-d", "--name", name, "--pid", "host", busyboxImage)
251251
res.Assert(c, icmd.Success)
252252
DelContainerForceMultyTime(c, name)
253253
}
@@ -256,7 +256,7 @@ func (suite *PouchRunSuite) TestRunWithPIDMode(c *check.C) {
256256
func (suite *PouchRunSuite) TestRunWithUTSMode(c *check.C) {
257257
name := "test-run-with-uts-mode"
258258

259-
res := command.PouchRun("run", "--name", name, "--uts", "host", busyboxImage)
259+
res := command.PouchRun("run", "-d", "--name", name, "--uts", "host", busyboxImage)
260260
res.Assert(c, icmd.Success)
261261
DelContainerForceMultyTime(c, name)
262262
}
@@ -266,7 +266,7 @@ func (suite *PouchRunSuite) TestRunWithSysctls(c *check.C) {
266266
sysctl := "net.ipv4.ip_forward=1"
267267
name := "run-sysctl"
268268

269-
res := command.PouchRun("run", "--name", name, "--sysctl", sysctl, busyboxImage)
269+
res := command.PouchRun("run", "-d", "--name", name, "--sysctl", sysctl, busyboxImage)
270270
res.Assert(c, icmd.Success)
271271

272272
output := command.PouchRun("exec", name, "cat", "/proc/sys/net/ipv4/ip_forward").Stdout()
@@ -281,7 +281,7 @@ func (suite *PouchRunSuite) TestRunWithUser(c *check.C) {
281281
user := "1001"
282282
name := "run-user"
283283

284-
res := command.PouchRun("run", "--name", name, "--user", user, busyboxImage)
284+
res := command.PouchRun("run", "-d", "--name", name, "--user", user, busyboxImage)
285285
res.Assert(c, icmd.Success)
286286

287287
output := command.PouchRun("exec", name, "id", "-u").Stdout()
@@ -304,7 +304,7 @@ func (suite *PouchRunSuite) TestRunWithAppArmor(c *check.C) {
304304
appArmor := "apparmor=unconfined"
305305
name := "run-apparmor"
306306

307-
res := command.PouchRun("run", "--name", name, "--security-opt", appArmor, busyboxImage)
307+
res := command.PouchRun("run", "-d", "--name", name, "--security-opt", appArmor, busyboxImage)
308308
res.Assert(c, icmd.Success)
309309

310310
// TODO: do the test more strictly with effective AppArmor profile.
@@ -317,7 +317,7 @@ func (suite *PouchRunSuite) TestRunWithSeccomp(c *check.C) {
317317
seccomp := "seccomp=unconfined"
318318
name := "run-seccomp"
319319

320-
res := command.PouchRun("run", "--name", name, "--security-opt", seccomp, busyboxImage)
320+
res := command.PouchRun("run", "-d", "--name", name, "--security-opt", seccomp, busyboxImage)
321321
res.Assert(c, icmd.Success)
322322

323323
// TODO: do the test more strictly with effective seccomp profile.
@@ -359,7 +359,7 @@ func (suite *PouchRunSuite) TestRunWithPrivilege(c *check.C) {
359359
func (suite *PouchRunSuite) TestRunWithBlkioWeight(c *check.C) {
360360
name := "test-run-with-blkio-weight"
361361

362-
res := command.PouchRun("run", "--name", name, "--blkio-weight", "500", busyboxImage)
362+
res := command.PouchRun("run", "-d", "--name", name, "--blkio-weight", "500", busyboxImage)
363363
res.Assert(c, icmd.Success)
364364
DelContainerForceMultyTime(c, name)
365365
}
@@ -817,7 +817,7 @@ func (suite *PouchRunSuite) TestRunWithDiskQuota(c *check.C) {
817817
// TestRunWithAnnotation is to verify the valid running container with annotation, and verify SpecAnnotation filed has been in inspect output.
818818
func (suite *PouchRunSuite) TestRunWithAnnotation(c *check.C) {
819819
cname := "TestRunWithAnnotation"
820-
command.PouchRun("run", "-d", "--annotation", "a=b", "--annotation", "foo=bar", "--name", cname, busyboxImage).Stdout()
820+
command.PouchRun("run", "-d", "--annotation", "a=b", "--annotation", "foo=bar", "--name", cname, busyboxImage).Assert(c, icmd.Success)
821821

822822
output := command.PouchRun("inspect", cname).Stdout()
823823
result := []types.ContainerJSON{}
@@ -835,3 +835,19 @@ func (suite *PouchRunSuite) TestRunWithAnnotation(c *check.C) {
835835
c.Assert(util.PartialEqual(annotationStr, "a=b"), check.IsNil)
836836
c.Assert(util.PartialEqual(annotationStr, "foo=bar"), check.IsNil)
837837
}
838+
839+
// TestRunWithExitCode is to verify the valid running container with exit code != 0.
840+
func (suite *PouchRunSuite) TestRunWithExitCode(c *check.C) {
841+
cname := "TestRunWithExitCode"
842+
ret := command.PouchRun("run", "--name", cname, busyboxImage, "sh", "-c", "exit 101")
843+
// test process exit code $? == 101
844+
ret.Assert(c, icmd.Expected{ExitCode: 101})
845+
846+
// test container ExitCode == 101
847+
output := command.PouchRun("inspect", cname).Stdout()
848+
result := []types.ContainerJSON{}
849+
if err := json.Unmarshal([]byte(output), &result); err != nil {
850+
c.Errorf("failed to decode inspect output: %v", err)
851+
}
852+
c.Assert(result[0].State.ExitCode, check.Equals, int64(101))
853+
}

test/cli_start_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package main
22

33
import (
44
"bufio"
5+
"encoding/json"
56
"os/exec"
67
"strings"
78

9+
"github.com/alibaba/pouch/apis/types"
810
"github.com/alibaba/pouch/test/command"
911
"github.com/alibaba/pouch/test/environment"
1012

@@ -244,3 +246,24 @@ func (suite *PouchStartSuite) TestStartWithAnnotation(c *check.C) {
244246

245247
command.PouchRun("start", name).Assert(c, icmd.Success)
246248
}
249+
250+
// TestStartWithExitCode starts a container with annotation.
251+
func (suite *PouchStartSuite) TestStartWithExitCode(c *check.C) {
252+
name := "start-exitcode"
253+
254+
res := command.PouchRun("create", "--name", name, busyboxImage, "sh", "-c", "exit 101")
255+
res.Assert(c, icmd.Success)
256+
defer DelContainerForceMultyTime(c, name)
257+
258+
// test process exit code $? == 101
259+
ret := command.PouchRun("start", "-a", name)
260+
ret.Assert(c, icmd.Expected{ExitCode: 101})
261+
262+
// test container ExitCode == 101
263+
output := command.PouchRun("inspect", name).Stdout()
264+
result := []types.ContainerJSON{}
265+
if err := json.Unmarshal([]byte(output), &result); err != nil {
266+
c.Errorf("failed to decode inspect output: %v", err)
267+
}
268+
c.Assert(result[0].State.ExitCode, check.Equals, int64(101))
269+
}

test/z_cli_daemon_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (suite *PouchDaemonSuite) TestDaemonCgroupParent(c *check.C) {
4848
}
4949
}
5050
{
51-
result := command.PouchRun("--host", daemon.Listen, "run", "--name", cname, busyboxImage)
51+
result := command.PouchRun("--host", daemon.Listen, "run", "-d", "--name", cname, busyboxImage)
5252
if result.ExitCode != 0 {
5353
dcfg.DumpLog()
5454
c.Fatalf("run container failed, err:%v", result)

0 commit comments

Comments
 (0)