diff --git a/api/swagger.yaml b/api/swagger.yaml index c5b79ed65c..8ff7415d62 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -6944,7 +6944,7 @@ paths: Various objects within Docker report events when something happens to them. - Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` diff --git a/daemon/exec.go b/daemon/exec.go index 6fe107987a..35c5831064 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -139,7 +139,10 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str d.registerExecCommand(cntr, execConfig) - d.LogContainerEvent(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) + attributes := map[string]string{ + "execID": execConfig.ID, + } + d.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes) return execConfig.ID, nil } @@ -174,7 +177,10 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R c := d.containers.Get(ec.ContainerID) logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID) - d.LogContainerEvent(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " ")) + attributes := map[string]string{ + "execID": ec.ID, + } + d.LogContainerEventWithAttributes(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " "), attributes) defer func() { if err != nil { @@ -270,7 +276,10 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R if _, ok := err.(term.EscapeError); !ok { return errdefs.System(errors.Wrap(err, "exec attach failed")) } - d.LogContainerEvent(c, "exec_detach") + attributes := map[string]string{ + "execID": ec.ID, + } + d.LogContainerEventWithAttributes(c, "exec_detach", attributes) } } return nil diff --git a/daemon/health.go b/daemon/health.go index 9acf19043b..ff3d54d45d 100644 --- a/daemon/health.go +++ b/daemon/health.go @@ -89,7 +89,10 @@ func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container execConfig.Env = container.ReplaceOrAppendEnvValues(cntr.CreateDaemonEnvironment(execConfig.Tty, linkedEnv), execConfig.Env) d.registerExecCommand(cntr, execConfig) - d.LogContainerEvent(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")) + attributes := map[string]string{ + "execID": execConfig.ID, + } + d.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes) output := &limitedBuffer{} err = d.ContainerExecStart(ctx, execConfig.ID, nil, output, output) diff --git a/daemon/monitor.go b/daemon/monitor.go index dd60239105..6f6928a4d9 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -128,6 +128,11 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc // remove the exec command from the container's store only and not the // daemon's store so that the exec command can be inspected. c.ExecCommands.Delete(execConfig.ID, execConfig.Pid) + attributes := map[string]string{ + "execID": execConfig.ID, + "exitCode": strconv.Itoa(ec), + } + daemon.LogContainerEventWithAttributes(c, "exec_die", attributes) } else { logrus.WithFields(logrus.Fields{ "container": c.ID, diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 0fdf4648dc..4eca7fbb11 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -17,6 +17,8 @@ keywords: "API, Docker, rcli, REST, documentation" [Docker Engine API v1.36](https://docs.docker.com/engine/api/v1.36/) documentation +* `Get /events` now return `exec_die` event when an exec process terminates. + ## v1.35 API changes diff --git a/integration/system/event_test.go b/integration/system/event_test.go new file mode 100644 index 0000000000..ef7eb1ff5b --- /dev/null +++ b/integration/system/event_test.go @@ -0,0 +1,74 @@ +package system + +import ( + "context" + "testing" + + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/strslice" + "github.com/docker/docker/integration/util/request" + "github.com/stretchr/testify/require" +) + +func TestEvents(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + container, err := client.ContainerCreate(ctx, + &container.Config{ + Image: "busybox", + Tty: true, + WorkingDir: "/root", + Cmd: strslice.StrSlice([]string{"top"}), + }, + &container.HostConfig{}, + &network.NetworkingConfig{}, + "foo", + ) + require.NoError(t, err) + err = client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}) + require.NoError(t, err) + + id, err := client.ContainerExecCreate(ctx, container.ID, + types.ExecConfig{ + Cmd: strslice.StrSlice([]string{"echo", "hello"}), + }, + ) + require.NoError(t, err) + + filters := filters.NewArgs( + filters.Arg("container", container.ID), + filters.Arg("event", "exec_die"), + ) + msg, errors := client.Events(ctx, types.EventsOptions{ + Filters: filters, + }) + + err = client.ContainerExecStart(ctx, id.ID, + types.ExecStartCheck{ + Detach: true, + Tty: false, + }, + ) + require.NoError(t, err) + + select { + case m := <-msg: + require.Equal(t, m.Type, "container") + require.Equal(t, m.Actor.ID, container.ID) + require.Equal(t, m.Action, "exec_die") + require.Equal(t, m.Actor.Attributes["execID"], id.ID) + require.Equal(t, m.Actor.Attributes["exitCode"], "0") + case err = <-errors: + t.Fatal(err) + case <-time.After(time.Second * 3): + t.Fatal("timeout hit") + } + +}