diff --git a/apis/swagger.yml b/apis/swagger.yml index 0fcb90631..2d5916e64 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -107,7 +107,7 @@ paths: description: | Stream real-time events from the server. Report various object events of pouchd when something happens to them. - Containers report these events: create`, `destroy`, `die`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + Containers report these events: create`, `destroy`, `die`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update` and `exec_die` Images report these events: `pull`, `untag` Volumes report these events: `create`, `destroy` Networks report these events: `create`, `connect`, `disconnect`, `destroy` diff --git a/ctrd/client.go b/ctrd/client.go index 93b8f6d50..d83fec8f9 100644 --- a/ctrd/client.go +++ b/ctrd/client.go @@ -231,9 +231,14 @@ func (c *Client) collectContainerdEvents() { logrus.Warnf("failed to parse %s event: %#v", TaskExitEventTopic, out) continue } - action = "die" + if exitEvent.ID == exitEvent.ContainerID { + action = "die" + } else { + action = "exec_die" + attributes["execID"] = exitEvent.ID + } containerID = exitEvent.ContainerID - attributes["exitcode"] = strconv.Itoa(int(exitEvent.ExitStatus)) + attributes["exitCode"] = strconv.Itoa(int(exitEvent.ExitStatus)) case TaskOOMEventTopic: oomEvent, ok := out.(*eventstypes.TaskOOM) if !ok { diff --git a/test/cli_events_test.go b/test/cli_events_test.go index e2a9b7c0a..99d694a6a 100644 --- a/test/cli_events_test.go +++ b/test/cli_events_test.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" "time" @@ -51,3 +52,95 @@ func (suite *PouchEventsSuite) TestEventsWorks(c *check.C) { c.Errorf("unexpected output %s: should contains create and start events\n", out) } } + +func delEmptyStrInSlice(strSlice []string) []string { + if len(strSlice) == 0 { + return strSlice + } + + newSlice := []string{} + for _, v := range strSlice { + if v != "" { + newSlice = append(newSlice, v) + } + } + + return newSlice +} + +// TestExecDieEventWorks tests exec_die event work. +func (suite *PouchEventsSuite) TestExecDieEventWorks(c *check.C) { + name := "test-exec-die-event-works" + + res := command.PouchRun("run", "-d", "--name", name, busyboxImage, "top") + defer DelContainerForceMultyTime(c, name) + res.Assert(c, icmd.Success) + + // only works when test case run on the same machine with pouchd + time.Sleep(1100 * time.Millisecond) + start := time.Now() + command.PouchRun("exec", name, "echo", "test").Assert(c, icmd.Success) + time.Sleep(1100 * time.Millisecond) + end := time.Now() + + since, until := start.Format(time.RFC3339), end.Format(time.RFC3339) + res = command.PouchRun("events", "--since", since, "--until", until) + output := res.Combined() + + // check output contains exec_die event + lines := delEmptyStrInSlice(strings.Split(output, "\n")) + if len(lines) != 1 { + c.Errorf("unexpected output %s: should just contains 1 line", output) + } + + if err := checkContainerEvent(lines[0], "exec_die"); err != nil { + c.Errorf("exec_die event check error: %v", err) + } +} + +// TestDieEventWorks tests container die event work. +func (suite *PouchEventsSuite) TestDieEventWorks(c *check.C) { + name := "test-die-event-works" + + res := command.PouchRun("run", "-d", "--name", name, busyboxImage, "top") + defer DelContainerForceMultyTime(c, name) + res.Assert(c, icmd.Success) + + // only works when test case run on the same machine with pouchd + time.Sleep(1100 * time.Millisecond) + start := time.Now() + command.PouchRun("stop", name).Assert(c, icmd.Success) + time.Sleep(1100 * time.Millisecond) + end := time.Now() + + since, until := start.Format(time.RFC3339), end.Format(time.RFC3339) + res = command.PouchRun("events", "--since", since, "--until", until) + output := res.Combined() + + // check events when stop a container + lines := delEmptyStrInSlice(strings.Split(output, "\n")) + if len(lines) != 2 { + c.Errorf("unexpected output %s: should contains 2 event line when stop a container", output) + } + + if err := checkContainerEvent(lines[0], "die"); err != nil { + c.Errorf("die event check error: %v", err) + } + + if err := checkContainerEvent(lines[1], "stop"); err != nil { + c.Errorf("exec_die event check error: %v", err) + } +} + +func checkContainerEvent(eventStr, eventType string) error { + strSlice := strings.Split(eventStr, " ") + if len(strSlice) < 4 { + return fmt.Errorf("unexpected output %s: output line may not be container event", eventStr) + } + + if strSlice[1] != "container" || strSlice[2] != eventType { + return fmt.Errorf("unexpected output %s: should be %s events", eventStr, eventType) + } + + return nil +}