瀏覽代碼

Merge pull request #46351 from thaJeztah/api_events_actions_enum

api/types/events: define "Action" type and consts
Sebastiaan van Stijn 1 年之前
父節點
當前提交
9c4e82435e
共有 70 個文件被更改,包括 299 次插入186 次删除
  1. 1 1
      api/server/backend/build/backend.go
  2. 81 1
      api/types/events/events.go
  3. 3 3
      client/events_test.go
  4. 8 9
      container/container.go
  5. 2 1
      container/container_unix.go
  6. 2 1
      container/container_windows.go
  7. 4 3
      daemon/archive_unix.go
  8. 4 3
      daemon/archive_windows.go
  9. 3 2
      daemon/attach.go
  10. 2 1
      daemon/checkpoint.go
  11. 5 5
      daemon/cluster/executor/container/controller.go
  12. 6 5
      daemon/cluster/executor/container/health_test.go
  13. 2 1
      daemon/commit.go
  14. 4 3
      daemon/container_operations.go
  15. 6 5
      daemon/containerd/image_delete.go
  16. 3 5
      daemon/containerd/image_events.go
  17. 2 1
      daemon/containerd/image_exporter.go
  18. 2 1
      daemon/containerd/image_import.go
  19. 3 2
      daemon/containerd/image_tag.go
  20. 2 1
      daemon/create.go
  21. 2 1
      daemon/delete.go
  22. 11 11
      daemon/events.go
  23. 3 3
      daemon/events/events.go
  24. 9 9
      daemon/events/events_test.go
  25. 2 2
      daemon/events/filter.go
  26. 1 1
      daemon/events/testutils/testutils.go
  27. 2 2
      daemon/events_test.go
  28. 7 10
      daemon/exec.go
  29. 2 1
      daemon/export.go
  30. 4 4
      daemon/health.go
  31. 7 7
      daemon/health_test.go
  32. 2 1
      daemon/image_service.go
  33. 9 11
      daemon/images/image_delete.go
  34. 3 5
      daemon/images/image_events.go
  35. 2 1
      daemon/images/image_import.go
  36. 1 1
      daemon/images/image_prune.go
  37. 2 1
      daemon/images/image_tag.go
  38. 2 1
      daemon/kill.go
  39. 8 8
      daemon/monitor.go
  40. 3 2
      daemon/network.go
  41. 2 1
      daemon/pause.go
  42. 2 2
      daemon/prune.go
  43. 2 1
      daemon/reload.go
  44. 3 2
      daemon/rename.go
  45. 3 3
      daemon/resize.go
  46. 2 1
      daemon/restart.go
  47. 2 1
      daemon/start.go
  48. 2 1
      daemon/stop.go
  49. 2 1
      daemon/top_unix.go
  50. 2 1
      daemon/unpause.go
  51. 2 1
      daemon/update.go
  52. 3 3
      daemon/volumes_unix.go
  53. 2 1
      distribution/config.go
  54. 2 1
      distribution/pull.go
  55. 2 1
      distribution/push.go
  56. 2 1
      image/tarexport/load.go
  57. 2 1
      image/tarexport/save.go
  58. 2 1
      image/tarexport/tarexport.go
  59. 1 1
      integration-cli/docker_cli_events_test.go
  60. 3 3
      integration/container/pause_test.go
  61. 2 2
      integration/plugin/authz/authz_plugin_test.go
  62. 2 2
      integration/system/event_test.go
  63. 7 6
      plugin/backend_linux.go
  64. 2 1
      plugin/manager.go
  65. 5 4
      plugin/manager_linux_test.go
  66. 1 1
      testutil/daemon/daemon.go
  67. 2 1
      testutil/fixtures/plugin/plugin.go
  68. 3 2
      volume/service/service.go
  69. 2 1
      volume/service/service_test.go
  70. 3 2
      volume/service/store.go

+ 1 - 1
api/server/backend/build/backend.go

@@ -104,7 +104,7 @@ func (b *Backend) PruneCache(ctx context.Context, opts types.BuildCachePruneOpti
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to prune build cache")
 	}
-	b.eventsService.Log("prune", events.BuilderEventType, events.Actor{
+	b.eventsService.Log(events.ActionPrune, events.BuilderEventType, events.Actor{
 		Attributes: map[string]string{
 			"reclaimed": strconv.FormatInt(buildCacheSize, 10),
 		},

+ 81 - 1
api/types/events/events.go

@@ -18,6 +18,86 @@ const (
 	VolumeEventType    Type = "volume"    // VolumeEventType is the event type that volumes generate.
 )
 
+// Action is used for event-actions.
+type Action string
+
+const (
+	ActionCreate       Action = "create"
+	ActionStart        Action = "start"
+	ActionRestart      Action = "restart"
+	ActionStop         Action = "stop"
+	ActionCheckpoint   Action = "checkpoint"
+	ActionPause        Action = "pause"
+	ActionUnPause      Action = "unpause"
+	ActionAttach       Action = "attach"
+	ActionDetach       Action = "detach"
+	ActionResize       Action = "resize"
+	ActionUpdate       Action = "update"
+	ActionRename       Action = "rename"
+	ActionKill         Action = "kill"
+	ActionDie          Action = "die"
+	ActionOOM          Action = "oom"
+	ActionDestroy      Action = "destroy"
+	ActionRemove       Action = "remove"
+	ActionCommit       Action = "commit"
+	ActionTop          Action = "top"
+	ActionCopy         Action = "copy"
+	ActionArchivePath  Action = "archive-path"
+	ActionExtractToDir Action = "extract-to-dir"
+	ActionExport       Action = "export"
+	ActionImport       Action = "import"
+	ActionSave         Action = "save"
+	ActionLoad         Action = "load"
+	ActionTag          Action = "tag"
+	ActionUnTag        Action = "untag"
+	ActionPush         Action = "push"
+	ActionPull         Action = "pull"
+	ActionPrune        Action = "prune"
+	ActionDelete       Action = "delete"
+	ActionEnable       Action = "enable"
+	ActionDisable      Action = "disable"
+	ActionConnect      Action = "connect"
+	ActionDisconnect   Action = "disconnect"
+	ActionReload       Action = "reload"
+	ActionMount        Action = "mount"
+	ActionUnmount      Action = "unmount"
+
+	// ActionExecCreate is the prefix used for exec_create events. These
+	// event-actions are commonly followed by a colon and space (": "),
+	// and the command that's defined for the exec, for example:
+	//
+	//	exec_create: /bin/sh -c 'echo hello'
+	//
+	// This is far from ideal; it's a compromise to allow filtering and
+	// to preserve backward-compatibility.
+	ActionExecCreate Action = "exec_create"
+	// ActionExecStart is the prefix used for exec_create events. These
+	// event-actions are commonly followed by a colon and space (": "),
+	// and the command that's defined for the exec, for example:
+	//
+	//	exec_start: /bin/sh -c 'echo hello'
+	//
+	// This is far from ideal; it's a compromise to allow filtering and
+	// to preserve backward-compatibility.
+	ActionExecStart  Action = "exec_start"
+	ActionExecDie    Action = "exec_die"
+	ActionExecDetach Action = "exec_detach"
+
+	// ActionHealthStatus is the prefix to use for health_status events.
+	//
+	// Health-status events can either have a pre-defined status, in which
+	// case the "health_status" action is followed by a colon, or can be
+	// "free-form", in which case they're followed by the output of the
+	// health-check output.
+	//
+	// This is far form ideal, and a compromise to allow filtering, and
+	// to preserve backward-compatibility.
+	ActionHealthStatus          Action = "health_status"
+	ActionHealthStatusRunning   Action = "health_status: running"
+	ActionHealthStatusHealthy   Action = "health_status: healthy"
+	ActionHealthStatusUnhealthy Action = "health_status: unhealthy"
+)
+
 // Actor describes something that generates events,
 // like a container, or a network, or a volume.
 // It has a defined name and a set of attributes.
@@ -37,7 +117,7 @@ type Message struct {
 	From   string `json:"from,omitempty"`   // Deprecated: use Actor.Attributes["image"] instead.
 
 	Type   Type
-	Action string
+	Action Action
 	Actor  Actor
 	// Engine events are local scope. Cluster events are swarm scope.
 	Scope string `json:"scope,omitempty"`

+ 3 - 3
client/events_test.go

@@ -90,17 +90,17 @@ func TestEvents(t *testing.T) {
 				{
 					Type:   events.BuilderEventType,
 					Actor:  events.Actor{ID: "1"},
-					Action: "create",
+					Action: events.ActionCreate,
 				},
 				{
 					Type:   events.BuilderEventType,
 					Actor:  events.Actor{ID: "1"},
-					Action: "die",
+					Action: events.ActionDie,
 				},
 				{
 					Type:   events.BuilderEventType,
 					Actor:  events.Actor{ID: "1"},
-					Action: "create",
+					Action: events.ActionCreate,
 				},
 			},
 			expectedEvents: map[string]bool{

+ 8 - 9
container/container.go

@@ -17,6 +17,7 @@ import (
 	"github.com/containerd/containerd/cio"
 	"github.com/containerd/containerd/log"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/container/stream"
@@ -488,26 +489,24 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
 }
 
 // UnmountVolumes unmounts all volumes
-func (container *Container) UnmountVolumes(volumeEventLog func(name, action string, attributes map[string]string)) error {
-	var errors []string
+func (container *Container) UnmountVolumes(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
+	var errs []string
 	for _, volumeMount := range container.MountPoints {
 		if volumeMount.Volume == nil {
 			continue
 		}
 
 		if err := volumeMount.Cleanup(); err != nil {
-			errors = append(errors, err.Error())
+			errs = append(errs, err.Error())
 			continue
 		}
-
-		attributes := map[string]string{
+		volumeEventLog(volumeMount.Volume.Name(), events.ActionUnmount, map[string]string{
 			"driver":    volumeMount.Volume.DriverName(),
 			"container": container.ID,
-		}
-		volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes)
+		})
 	}
-	if len(errors) > 0 {
-		return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errors, "; "))
+	if len(errs) > 0 {
+		return fmt.Errorf("error while unmounting volumes for container %s: %s", container.ID, strings.Join(errs, "; "))
 	}
 	return nil
 }

+ 2 - 1
container/container_unix.go

@@ -12,6 +12,7 @@ import (
 	"github.com/containerd/continuity/fs"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/pkg/stringid"
@@ -365,7 +366,7 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi
 // DetachAndUnmount uses a detached mount on all mount destinations, then
 // unmounts each volume normally.
 // This is used from daemon/archive for `docker cp`
-func (container *Container) DetachAndUnmount(volumeEventLog func(name, action string, attributes map[string]string)) error {
+func (container *Container) DetachAndUnmount(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
 	ctx := context.TODO()
 
 	networkMounts := container.NetworkMounts()

+ 2 - 1
container/container_windows.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/pkg/system"
 )
@@ -126,7 +127,7 @@ func (container *Container) ConfigMounts() []Mount {
 // DetachAndUnmount unmounts all volumes.
 // On Windows it only delegates to `UnmountVolumes` since there is nothing to
 // force unmount.
-func (container *Container) DetachAndUnmount(volumeEventLog func(name, action string, attributes map[string]string)) error {
+func (container *Container) DetachAndUnmount(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
 	return container.UnmountVolumes(volumeEventLog)
 }
 

+ 4 - 3
daemon/archive_unix.go

@@ -9,6 +9,7 @@ import (
 	"path/filepath"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/archive"
@@ -85,7 +86,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
 		return err
 	})
 
-	daemon.LogContainerEvent(container, "archive-path")
+	daemon.LogContainerEvent(container, events.ActionArchivePath)
 
 	return content, stat, nil
 }
@@ -155,7 +156,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
 		return err
 	}
 
-	daemon.LogContainerEvent(container, "extract-to-dir")
+	daemon.LogContainerEvent(container, events.ActionExtractToDir)
 
 	return nil
 }
@@ -205,7 +206,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
 		container.Unlock()
 		return err
 	})
-	daemon.LogContainerEvent(container, "copy")
+	daemon.LogContainerEvent(container, events.ActionCopy)
 	return reader, nil
 }
 

+ 4 - 3
daemon/archive_windows.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/archive"
@@ -136,7 +137,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
 		return err
 	})
 
-	daemon.LogContainerEvent(container, "archive-path")
+	daemon.LogContainerEvent(container, events.ActionArchivePath)
 
 	return content, stat, nil
 }
@@ -254,7 +255,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
 		return err
 	}
 
-	daemon.LogContainerEvent(container, "extract-to-dir")
+	daemon.LogContainerEvent(container, events.ActionExtractToDir)
 
 	return nil
 }
@@ -328,7 +329,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
 		container.Unlock()
 		return err
 	})
-	daemon.LogContainerEvent(container, "copy")
+	daemon.LogContainerEvent(container, events.ActionCopy)
 	return reader, nil
 }
 

+ 3 - 2
daemon/attach.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types/backend"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container/stream"
 	"github.com/docker/docker/daemon/logger"
@@ -146,7 +147,7 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.Attach
 		}
 	}
 
-	daemon.LogContainerEvent(c, "attach")
+	daemon.LogContainerEvent(c, events.ActionAttach)
 
 	if !doStream {
 		return nil
@@ -179,7 +180,7 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.Attach
 	if err != nil {
 		var ierr term.EscapeError
 		if errors.Is(err, context.Canceled) || errors.As(err, &ierr) {
-			daemon.LogContainerEvent(c, "detach")
+			daemon.LogContainerEvent(c, events.ActionDetach)
 		} else {
 			log.G(ctx).Errorf("attach failed with error: %v", err)
 		}

+ 2 - 1
daemon/checkpoint.go

@@ -7,6 +7,7 @@ import (
 	"path/filepath"
 
 	"github.com/docker/docker/api/types/checkpoint"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/daemon/names"
 )
 
@@ -79,7 +80,7 @@ func (daemon *Daemon) CheckpointCreate(name string, config checkpoint.CreateOpti
 		return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
 	}
 
-	daemon.LogContainerEvent(container, "checkpoint")
+	daemon.LogContainerEvent(container, events.ActionCheckpoint)
 
 	return nil
 }

+ 5 - 5
daemon/cluster/executor/container/controller.go

@@ -254,7 +254,7 @@ func (r *controller) Start(ctx context.Context) error {
 			}
 
 			switch event.Action {
-			case "die": // exit on terminal events
+			case events.ActionDie: // exit on terminal events
 				ctnr, err := r.adapter.inspect(ctx)
 				if err != nil {
 					return errors.Wrap(err, "die event received")
@@ -263,18 +263,18 @@ func (r *controller) Start(ctx context.Context) error {
 				}
 
 				return nil
-			case "destroy":
+			case events.ActionDestroy:
 				// If we get here, something has gone wrong but we want to exit
 				// and report anyways.
 				return ErrContainerDestroyed
-			case "health_status: unhealthy":
+			case events.ActionHealthStatusUnhealthy:
 				// in this case, we stop the container and report unhealthy status
 				if err := r.Shutdown(ctx); err != nil {
 					return errors.Wrap(err, "unhealthy container shutdown failed")
 				}
 				// set health check error, and wait for container to fully exit ("die" event)
 				healthErr = ErrContainerUnhealthy
-			case "health_status: healthy":
+			case events.ActionHealthStatusHealthy:
 				if err := r.adapter.activateServiceBinding(); err != nil {
 					log.G(ctx).WithError(err).Errorf("failed to activate service binding for container %s after healthy event", r.adapter.container.name())
 					return err
@@ -716,7 +716,7 @@ func (r *controller) checkHealth(ctx context.Context) error {
 			}
 
 			switch event.Action {
-			case "health_status: unhealthy":
+			case events.ActionHealthStatusUnhealthy:
 				return ErrContainerUnhealthy
 			}
 		}

+ 6 - 5
daemon/cluster/executor/container/health_test.go

@@ -8,6 +8,7 @@ import (
 	"time"
 
 	containertypes "github.com/docker/docker/api/types/container"
+	eventtypes "github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/events"
@@ -71,7 +72,7 @@ func TestHealthStates(t *testing.T) {
 
 	// send an event and expect to get expectedErr
 	// if expectedErr is nil, shouldn't get any error
-	logAndExpect := func(msg string, expectedErr error) {
+	logAndExpect := func(msg eventtypes.Action, expectedErr error) {
 		daemon.LogContainerEvent(c, msg)
 
 		timer := time.NewTimer(1 * time.Second)
@@ -90,10 +91,10 @@ func TestHealthStates(t *testing.T) {
 	}
 
 	// events that are ignored by checkHealth
-	logAndExpect("health_status: running", nil)
-	logAndExpect("health_status: healthy", nil)
-	logAndExpect("die", nil)
+	logAndExpect(eventtypes.ActionHealthStatusRunning, nil)
+	logAndExpect(eventtypes.ActionHealthStatusHealthy, nil)
+	logAndExpect(eventtypes.ActionDie, nil)
 
 	// unhealthy event will be caught by checkHealth
-	logAndExpect("health_status: unhealthy", ErrContainerUnhealthy)
+	logAndExpect(eventtypes.ActionHealthStatusUnhealthy, ErrContainerUnhealthy)
 }

+ 2 - 1
daemon/commit.go

@@ -10,6 +10,7 @@ import (
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/backend"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
@@ -180,7 +181,7 @@ func (daemon *Daemon) CreateImageFromContainer(ctx context.Context, name string,
 		}
 		imageRef = reference.FamiliarString(c.Tag)
 	}
-	daemon.LogContainerEventWithAttributes(container, "commit", map[string]string{
+	daemon.LogContainerEventWithAttributes(container, events.ActionCommit, map[string]string{
 		"comment":  c.Comment,
 		"imageID":  id.String(),
 		"imageRef": imageRef,

+ 4 - 3
daemon/container_operations.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/config"
@@ -772,7 +773,7 @@ func (daemon *Daemon) connectToNetwork(cfg *config.Config, container *container.
 
 	container.NetworkSettings.Ports = getPortMapInfo(sb)
 
-	daemon.LogNetworkEventWithAttributes(n, "connect", map[string]string{"container": container.ID})
+	daemon.LogNetworkEventWithAttributes(n, events.ActionConnect, map[string]string{"container": container.ID})
 	networkActions.WithValues("connect").UpdateSince(start)
 	return nil
 }
@@ -880,7 +881,7 @@ func (daemon *Daemon) tryDetachContainerFromClusterNetwork(network *libnetwork.N
 			}
 		}
 	}
-	daemon.LogNetworkEventWithAttributes(network, "disconnect", map[string]string{
+	daemon.LogNetworkEventWithAttributes(network, events.ActionDisconnect, map[string]string{
 		"container": container.ID,
 	})
 }
@@ -1051,7 +1052,7 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, netw
 	}
 
 	if n != nil {
-		daemon.LogNetworkEventWithAttributes(n, "disconnect", map[string]string{
+		daemon.LogNetworkEventWithAttributes(n, events.ActionDisconnect, map[string]string{
 			"container": container.ID,
 		})
 	}

+ 6 - 5
daemon/containerd/image_delete.go

@@ -10,6 +10,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/stringid"
@@ -76,7 +77,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
 		if err != nil {
 			return nil, err
 		}
-		i.LogImageEvent(imgID.String(), imgID.String(), "untag")
+		i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
 		records := []types.ImageDeleteResponseItem{{Untagged: reference.FamiliarString(reference.TagNameOnly(parsedRef))}}
 		return records, nil
 	}
@@ -107,7 +108,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
 			return nil, err
 		}
 
-		i.LogImageEvent(imgID.String(), imgID.String(), "untag")
+		i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
 		records := []types.ImageDeleteResponseItem{{Untagged: reference.FamiliarString(reference.TagNameOnly(parsedRef))}}
 		return records, nil
 	}
@@ -159,7 +160,7 @@ func (i *ImageService) deleteAll(ctx context.Context, img images.Image, force, p
 			return records, err
 		}
 	}
-	i.LogImageEvent(imgID, imgID, "delete")
+	i.LogImageEvent(imgID, imgID, events.ActionDelete)
 	records = append(records, types.ImageDeleteResponseItem{Deleted: imgID})
 
 	for _, parent := range parents {
@@ -172,7 +173,7 @@ func (i *ImageService) deleteAll(ctx context.Context, img images.Image, force, p
 			break
 		}
 		parentID := parent.img.Target.Digest.String()
-		i.LogImageEvent(parentID, parentID, "delete")
+		i.LogImageEvent(parentID, parentID, events.ActionDelete)
 		records = append(records, types.ImageDeleteResponseItem{Deleted: parentID})
 	}
 
@@ -259,7 +260,7 @@ func (i *ImageService) imageDeleteHelper(ctx context.Context, img images.Image,
 		return err
 	}
 
-	i.LogImageEvent(imgID.String(), imgID.String(), "untag")
+	i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
 	*records = append(*records, types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(untaggedRef)})
 
 	return nil

+ 3 - 5
daemon/containerd/image_events.go

@@ -8,7 +8,7 @@ import (
 )
 
 // LogImageEvent generates an event related to an image with only the default attributes.
-func (i *ImageService) LogImageEvent(imageID, refName, action string) {
+func (i *ImageService) LogImageEvent(imageID, refName string, action events.Action) {
 	ctx := context.TODO()
 	attributes := map[string]string{}
 
@@ -21,12 +21,10 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) {
 	if refName != "" {
 		attributes["name"] = refName
 	}
-	actor := events.Actor{
+	i.eventsService.Log(action, events.ImageEventType, events.Actor{
 		ID:         imageID,
 		Attributes: attributes,
-	}
-
-	i.eventsService.Log(action, events.ImageEventType, actor)
+	})
 }
 
 // copyAttributes guarantees that labels are not mutated by event triggers.

+ 2 - 1
daemon/containerd/image_exporter.go

@@ -14,6 +14,7 @@ import (
 	"github.com/containerd/containerd/log"
 	cplatforms "github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/platforms"
@@ -216,7 +217,7 @@ func (i *ImageService) LoadImage(ctx context.Context, inTar io.ReadCloser, outSt
 		}
 
 		fmt.Fprintf(progress, "%s: %s\n", loadedMsg, name)
-		i.LogImageEvent(img.Target.Digest.String(), img.Target.Digest.String(), "load")
+		i.LogImageEvent(img.Target.Digest.String(), img.Target.Digest.String(), events.ActionLoad)
 	}
 
 	return nil

+ 2 - 1
daemon/containerd/image_import.go

@@ -16,6 +16,7 @@ import (
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
@@ -153,7 +154,7 @@ func (i *ImageService) ImportImage(ctx context.Context, ref reference.Named, pla
 	if err != nil {
 		logger.WithError(err).Debug("failed to unpack image")
 	} else {
-		i.LogImageEvent(id.String(), id.String(), "import")
+		i.LogImageEvent(id.String(), id.String(), events.ActionImport)
 	}
 
 	return id, err

+ 3 - 2
daemon/containerd/image_tag.go

@@ -7,6 +7,7 @@ import (
 	containerdimages "github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/log"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/pkg/errors"
@@ -39,7 +40,7 @@ func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag re
 		// Check if image we would replace already resolves to the same target.
 		// No need to do anything.
 		if replacedImg.Target.Digest == target.Digest {
-			i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
+			i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), events.ActionTag)
 			return nil
 		}
 
@@ -60,7 +61,7 @@ func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag re
 	})
 	logger.Info("image created")
 
-	defer i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
+	defer i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), events.ActionTag)
 
 	// The tag succeeded, check if the source image is dangling
 	sourceDanglingImg, err := is.Get(context.Background(), danglingImageName(target.Digest))

+ 2 - 1
daemon/create.go

@@ -13,6 +13,7 @@ import (
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	imagetypes "github.com/docker/docker/api/types/image"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
@@ -235,7 +236,7 @@ func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts
 		return nil, err
 	}
 	stateCtr.set(ctr.ID, "stopped")
-	daemon.LogContainerEvent(ctr, "create")
+	daemon.LogContainerEvent(ctr, events.ActionCreate)
 	return ctr, nil
 }
 

+ 2 - 1
daemon/delete.go

@@ -13,6 +13,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
@@ -182,6 +183,6 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, config ty
 	container.SetRemoved()
 	stateCtr.del(container.ID)
 
-	daemon.LogContainerEvent(container, "destroy")
+	daemon.LogContainerEvent(container, events.ActionDestroy)
 	return nil
 }

+ 11 - 11
daemon/events.go

@@ -17,12 +17,12 @@ import (
 )
 
 // LogContainerEvent generates an event related to a container with only the default attributes.
-func (daemon *Daemon) LogContainerEvent(container *container.Container, action string) {
+func (daemon *Daemon) LogContainerEvent(container *container.Container, action events.Action) {
 	daemon.LogContainerEventWithAttributes(container, action, map[string]string{})
 }
 
 // LogContainerEventWithAttributes generates an event related to a container with specific given attributes.
-func (daemon *Daemon) LogContainerEventWithAttributes(container *container.Container, action string, attributes map[string]string) {
+func (daemon *Daemon) LogContainerEventWithAttributes(container *container.Container, action events.Action, attributes map[string]string) {
 	copyAttributes(attributes, container.Config.Labels)
 	if container.Config.Image != "" {
 		attributes["image"] = container.Config.Image
@@ -35,7 +35,7 @@ func (daemon *Daemon) LogContainerEventWithAttributes(container *container.Conta
 }
 
 // LogPluginEvent generates an event related to a plugin with only the default attributes.
-func (daemon *Daemon) LogPluginEvent(pluginID, refName, action string) {
+func (daemon *Daemon) LogPluginEvent(pluginID, refName string, action events.Action) {
 	daemon.EventsService.Log(action, events.PluginEventType, events.Actor{
 		ID:         pluginID,
 		Attributes: map[string]string{"name": refName},
@@ -43,7 +43,7 @@ func (daemon *Daemon) LogPluginEvent(pluginID, refName, action string) {
 }
 
 // LogVolumeEvent generates an event related to a volume.
-func (daemon *Daemon) LogVolumeEvent(volumeID, action string, attributes map[string]string) {
+func (daemon *Daemon) LogVolumeEvent(volumeID string, action events.Action, attributes map[string]string) {
 	daemon.EventsService.Log(action, events.VolumeEventType, events.Actor{
 		ID:         volumeID,
 		Attributes: attributes,
@@ -51,12 +51,12 @@ func (daemon *Daemon) LogVolumeEvent(volumeID, action string, attributes map[str
 }
 
 // LogNetworkEvent generates an event related to a network with only the default attributes.
-func (daemon *Daemon) LogNetworkEvent(nw *libnetwork.Network, action string) {
+func (daemon *Daemon) LogNetworkEvent(nw *libnetwork.Network, action events.Action) {
 	daemon.LogNetworkEventWithAttributes(nw, action, map[string]string{})
 }
 
 // LogNetworkEventWithAttributes generates an event related to a network with specific given attributes.
-func (daemon *Daemon) LogNetworkEventWithAttributes(nw *libnetwork.Network, action string, attributes map[string]string) {
+func (daemon *Daemon) LogNetworkEventWithAttributes(nw *libnetwork.Network, action events.Action, attributes map[string]string) {
 	attributes["name"] = nw.Name()
 	attributes["type"] = nw.Type()
 	daemon.EventsService.Log(action, events.NetworkEventType, events.Actor{
@@ -66,7 +66,7 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw *libnetwork.Network, acti
 }
 
 // LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
-func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
+func (daemon *Daemon) LogDaemonEventWithAttributes(action events.Action, attributes map[string]string) {
 	if daemon.EventsService != nil {
 		if name := hostName(); name != "" {
 			attributes["name"] = name
@@ -248,10 +248,10 @@ func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *
 	daemon.logClusterEvent(action, service.ID, events.ServiceEventType, eventTime, attributes)
 }
 
-var clusterEventAction = map[swarmapi.WatchActionKind]string{
-	swarmapi.WatchActionKindCreate: "create",
-	swarmapi.WatchActionKindUpdate: "update",
-	swarmapi.WatchActionKindRemove: "remove",
+var clusterEventAction = map[swarmapi.WatchActionKind]events.Action{
+	swarmapi.WatchActionKindCreate: events.ActionCreate,
+	swarmapi.WatchActionKindUpdate: events.ActionUpdate,
+	swarmapi.WatchActionKindRemove: events.ActionRemove,
 }
 
 func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id string, eventType events.Type, eventTime time.Time, attributes map[string]string) {

+ 3 - 3
daemon/events/events.go

@@ -79,7 +79,7 @@ func (e *Events) Evict(l chan interface{}) {
 }
 
 // Log creates a local scope message and publishes it
-func (e *Events) Log(action string, eventType eventtypes.Type, actor eventtypes.Actor) {
+func (e *Events) Log(action eventtypes.Action, eventType eventtypes.Type, actor eventtypes.Actor) {
 	now := time.Now().UTC()
 	jm := eventtypes.Message{
 		Action:   action,
@@ -94,11 +94,11 @@ func (e *Events) Log(action string, eventType eventtypes.Type, actor eventtypes.
 	switch eventType {
 	case eventtypes.ContainerEventType:
 		jm.ID = actor.ID
-		jm.Status = action
+		jm.Status = string(action)
 		jm.From = actor.Attributes["image"]
 	case eventtypes.ImageEventType:
 		jm.ID = actor.ID
-		jm.Status = action
+		jm.Status = string(action)
 	}
 
 	e.PublishMessage(jm)

+ 9 - 9
daemon/events/events_test.go

@@ -20,7 +20,7 @@ import (
 // TODO remove this once we removed the deprecated `ID`, `Status`, and `From` fields.
 func validateLegacyFields(t *testing.T, msg events.Message) {
 	t.Helper()
-	assert.Check(t, is.Equal(msg.Status, msg.Action), "Legacy Status field does not match Action")
+	assert.Check(t, is.Equal(msg.Status, string(msg.Action)), "Legacy Status field does not match Action")
 	assert.Check(t, is.Equal(msg.ID, msg.Actor.ID), "Legacy ID field does not match Actor.ID")
 	assert.Check(t, is.Equal(msg.From, msg.Actor.Attributes["image"]), "Legacy From field does not match Actor.Attributes.image")
 }
@@ -45,7 +45,7 @@ func TestEventsLog(t *testing.T) {
 		jmsg, ok := msg.(events.Message)
 		assert.Assert(t, ok, "unexpected type: %T", msg)
 		validateLegacyFields(t, jmsg)
-		assert.Check(t, is.Equal(jmsg.Action, "test"))
+		assert.Check(t, is.Equal(jmsg.Action, events.Action("test")))
 		assert.Check(t, is.Equal(jmsg.Actor.ID, "cont"))
 		assert.Check(t, is.Equal(jmsg.Actor.Attributes["image"], "image"))
 	case <-time.After(1 * time.Second):
@@ -58,7 +58,7 @@ func TestEventsLog(t *testing.T) {
 		jmsg, ok := msg.(events.Message)
 		assert.Assert(t, ok, "unexpected type: %T", msg)
 		validateLegacyFields(t, jmsg)
-		assert.Check(t, is.Equal(jmsg.Action, "test"))
+		assert.Check(t, is.Equal(jmsg.Action, events.Action("test")))
 		assert.Check(t, is.Equal(jmsg.Actor.ID, "cont"))
 		assert.Check(t, is.Equal(jmsg.Actor.Attributes["image"], "image"))
 	case <-time.After(1 * time.Second):
@@ -91,7 +91,7 @@ func TestLogEvents(t *testing.T) {
 
 	for i := 0; i < eventsLimit+16; i++ {
 		num := strconv.Itoa(i)
-		e.Log("action_"+num, events.ContainerEventType, events.Actor{
+		e.Log(events.Action("action_"+num), events.ContainerEventType, events.Actor{
 			ID:         "cont_" + num,
 			Attributes: map[string]string{"image": "image_" + num},
 		})
@@ -100,7 +100,7 @@ func TestLogEvents(t *testing.T) {
 	current, l, _ := e.Subscribe()
 	for i := 0; i < 10; i++ {
 		num := strconv.Itoa(i + eventsLimit + 16)
-		e.Log("action_"+num, events.ContainerEventType, events.Actor{
+		e.Log(events.Action("action_"+num), events.ContainerEventType, events.Actor{
 			ID:         "cont_" + num,
 			Attributes: map[string]string{"image": "image_" + num},
 		})
@@ -121,16 +121,16 @@ func TestLogEvents(t *testing.T) {
 
 	first := current[0]
 	validateLegacyFields(t, first)
-	assert.Check(t, is.Equal(first.Action, "action_16"))
+	assert.Check(t, is.Equal(first.Action, events.Action("action_16")))
 
 	last := current[len(current)-1]
-	assert.Check(t, is.Equal(last.Action, "action_271"))
+	assert.Check(t, is.Equal(last.Action, events.Action("action_271")))
 
 	firstC := msgs[0]
-	assert.Check(t, is.Equal(firstC.Action, "action_272"))
+	assert.Check(t, is.Equal(firstC.Action, events.Action("action_272")))
 
 	lastC := msgs[len(msgs)-1]
-	assert.Check(t, is.Equal(lastC.Action, "action_281"))
+	assert.Check(t, is.Equal(lastC.Action, events.Action("action_281")))
 }
 
 // Regression-test for https://github.com/moby/moby/issues/20999

+ 2 - 2
daemon/events/filter.go

@@ -38,9 +38,9 @@ func (ef *Filter) matchEvent(ev events.Message) bool {
 	// #25798 if an event filter contains either health_status, exec_create or exec_start without a colon
 	// Let's to a FuzzyMatch instead of an ExactMatch.
 	if ef.filterContains("event", map[string]struct{}{"health_status": {}, "exec_create": {}, "exec_start": {}}) {
-		return ef.filter.FuzzyMatch("event", ev.Action)
+		return ef.filter.FuzzyMatch("event", string(ev.Action))
 	}
-	return ef.filter.ExactMatch("event", ev.Action)
+	return ef.filter.ExactMatch("event", string(ev.Action))
 }
 
 func (ef *Filter) filterContains(field string, values map[string]struct{}) bool {

+ 1 - 1
daemon/events/testutils/testutils.go

@@ -66,7 +66,7 @@ func Scan(text string) (*events.Message, error) {
 		Time:     t,
 		TimeNano: time.Unix(t, tn).UnixNano(),
 		Type:     events.Type(md["eventType"]),
-		Action:   md["action"],
+		Action:   events.Action(md["action"]),
 		Actor: events.Actor{
 			ID:         md["id"],
 			Attributes: attrs,

+ 2 - 2
daemon/events_test.go

@@ -29,7 +29,7 @@ func TestLogContainerEventCopyLabels(t *testing.T) {
 	daemon := &Daemon{
 		EventsService: e,
 	}
-	daemon.LogContainerEvent(ctr, "create")
+	daemon.LogContainerEvent(ctr, eventtypes.ActionCreate)
 
 	if _, mutated := ctr.Config.Labels["image"]; mutated {
 		t.Fatalf("Expected to not mutate the container labels, got %q", ctr.Config.Labels)
@@ -59,7 +59,7 @@ func TestLogContainerEventWithAttributes(t *testing.T) {
 	daemon := &Daemon{
 		EventsService: e,
 	}
-	daemon.LogContainerEventWithAttributes(ctr, "create", map[string]string{
+	daemon.LogContainerEventWithAttributes(ctr, eventtypes.ActionCreate, map[string]string{
 		"node": "2",
 		"foo":  "bar",
 	})

+ 7 - 10
daemon/exec.go

@@ -13,6 +13,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/container/stream"
@@ -137,11 +138,9 @@ func (daemon *Daemon) ContainerExecCreate(name string, config *types.ExecConfig)
 	}
 
 	daemon.registerExecCommand(cntr, execConfig)
-
-	attributes := map[string]string{
+	daemon.LogContainerEventWithAttributes(cntr, events.Action(string(events.ActionExecCreate)+": "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")), map[string]string{
 		"execID": execConfig.ID,
-	}
-	daemon.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes)
+	})
 
 	return execConfig.ID, nil
 }
@@ -175,10 +174,9 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
 	ec.Unlock()
 
 	log.G(ctx).Debugf("starting exec command %s in container %s", ec.ID, ec.Container.ID)
-	attributes := map[string]string{
+	daemon.LogContainerEventWithAttributes(ec.Container, events.Action(string(events.ActionExecStart)+": "+ec.Entrypoint+" "+strings.Join(ec.Args, " ")), map[string]string{
 		"execID": ec.ID,
-	}
-	daemon.LogContainerEventWithAttributes(ec.Container, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " "), attributes)
+	})
 
 	defer func() {
 		if err != nil {
@@ -311,10 +309,9 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio
 			if _, ok := err.(term.EscapeError); !ok {
 				return errdefs.System(errors.Wrap(err, "exec attach failed"))
 			}
-			attributes := map[string]string{
+			daemon.LogContainerEventWithAttributes(ec.Container, events.ActionExecDetach, map[string]string{
 				"execID": ec.ID,
-			}
-			daemon.LogContainerEventWithAttributes(ec.Container, "exec_detach", attributes)
+			})
 		}
 	}
 	return nil

+ 2 - 1
daemon/export.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"io"
 
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/archive"
@@ -58,6 +59,6 @@ func (daemon *Daemon) containerExport(ctx context.Context, container *container.
 	if err != nil {
 		return err
 	}
-	daemon.LogContainerEvent(container, "export")
+	daemon.LogContainerEvent(container, events.ActionExport)
 	return nil
 }

+ 4 - 4
daemon/health.go

@@ -12,6 +12,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/container"
 )
@@ -87,10 +88,9 @@ 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)
-	attributes := map[string]string{
+	d.LogContainerEventWithAttributes(cntr, events.Action(string(events.ActionExecCreate)+": "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " ")), map[string]string{
 		"execID": execConfig.ID,
-	}
-	d.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes)
+	})
 
 	output := &limitedBuffer{}
 	probeCtx, cancelProbe := context.WithCancel(ctx)
@@ -240,7 +240,7 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
 
 	current := h.Status()
 	if oldStatus != current {
-		d.LogContainerEvent(c, "health_status: "+current)
+		d.LogContainerEvent(c, events.Action(string(events.ActionHealthStatus)+": "+current))
 	}
 }
 

+ 7 - 7
daemon/health_test.go

@@ -49,7 +49,7 @@ func TestHealthStates(t *testing.T) {
 	_, l, _ := e.Subscribe()
 	defer e.Evict(l)
 
-	expect := func(expected string) {
+	expect := func(expected eventtypes.Action) {
 		select {
 		case event := <-l:
 			ev := event.(eventtypes.Message)
@@ -97,13 +97,13 @@ func TestHealthStates(t *testing.T) {
 	// starting -> failed -> success -> failed
 
 	handleResult(c.State.StartedAt.Add(1*time.Second), 1)
-	expect("health_status: unhealthy")
+	expect(eventtypes.ActionHealthStatusUnhealthy)
 
 	handleResult(c.State.StartedAt.Add(2*time.Second), 0)
-	expect("health_status: healthy")
+	expect(eventtypes.ActionHealthStatusHealthy)
 
 	handleResult(c.State.StartedAt.Add(3*time.Second), 1)
-	expect("health_status: unhealthy")
+	expect(eventtypes.ActionHealthStatusUnhealthy)
 
 	// Test retries
 
@@ -119,10 +119,10 @@ func TestHealthStates(t *testing.T) {
 		t.Errorf("Expecting FailingStreak=2, but got %d\n", c.State.Health.FailingStreak)
 	}
 	handleResult(c.State.StartedAt.Add(60*time.Second), 1)
-	expect("health_status: unhealthy")
+	expect(eventtypes.ActionHealthStatusUnhealthy)
 
 	handleResult(c.State.StartedAt.Add(80*time.Second), 0)
-	expect("health_status: healthy")
+	expect(eventtypes.ActionHealthStatusHealthy)
 	if c.State.Health.FailingStreak != 0 {
 		t.Errorf("Expecting FailingStreak=0, but got %d\n", c.State.Health.FailingStreak)
 	}
@@ -148,7 +148,7 @@ func TestHealthStates(t *testing.T) {
 		t.Errorf("Expecting FailingStreak=1, but got %d\n", c.State.Health.FailingStreak)
 	}
 	handleResult(c.State.StartedAt.Add(80*time.Second), 0)
-	expect("health_status: healthy")
+	expect(eventtypes.ActionHealthStatusHealthy)
 	if c.State.Health.FailingStreak != 0 {
 		t.Errorf("Expecting FailingStreak=0, but got %d\n", c.State.Health.FailingStreak)
 	}

+ 2 - 1
daemon/image_service.go

@@ -7,6 +7,7 @@ import (
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/backend"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	imagetype "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/api/types/registry"
@@ -34,7 +35,7 @@ type ImageService interface {
 	PerformWithBaseFS(ctx context.Context, c *container.Container, fn func(string) error) error
 	LoadImage(ctx context.Context, inTar io.ReadCloser, outStream io.Writer, quiet bool) error
 	Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error)
-	LogImageEvent(imageID, refName, action string)
+	LogImageEvent(imageID, refName string, action events.Action)
 	CountImages() int
 	ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
 	ImportImage(ctx context.Context, ref reference.Named, platform *ocispec.Platform, msg string, layerReader io.Reader, changes []string) (image.ID, error)

+ 9 - 11
daemon/images/image_delete.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	imagetypes "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
@@ -105,7 +106,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
 
 		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
-		i.LogImageEvent(imgID.String(), imgID.String(), "untag")
+		i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
 		records = append(records, untaggedRecord)
 
 		repoRefs = i.referenceStore.References(imgID.Digest())
@@ -167,7 +168,7 @@ func (i *ImageService) ImageDelete(ctx context.Context, imageRef string, force,
 
 				untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
 
-				i.LogImageEvent(imgID.String(), imgID.String(), "untag")
+				i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
 				records = append(records, untaggedRecord)
 			}
 		}
@@ -243,18 +244,15 @@ func (i *ImageService) removeImageRef(ref reference.Named) (reference.Named, err
 // daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the
 // given list of records.
 func (i *ImageService) removeAllReferencesToImageID(imgID image.ID, records *[]types.ImageDeleteResponseItem) error {
-	imageRefs := i.referenceStore.References(imgID.Digest())
-
-	for _, imageRef := range imageRefs {
+	for _, imageRef := range i.referenceStore.References(imgID.Digest()) {
 		parsedRef, err := i.removeImageRef(imageRef)
 		if err != nil {
 			return err
 		}
-
-		untaggedRecord := types.ImageDeleteResponseItem{Untagged: reference.FamiliarString(parsedRef)}
-
-		i.LogImageEvent(imgID.String(), imgID.String(), "untag")
-		*records = append(*records, untaggedRecord)
+		i.LogImageEvent(imgID.String(), imgID.String(), events.ActionUnTag)
+		*records = append(*records, types.ImageDeleteResponseItem{
+			Untagged: reference.FamiliarString(parsedRef),
+		})
 	}
 
 	return nil
@@ -328,7 +326,7 @@ func (i *ImageService) imageDeleteHelper(imgID image.ID, records *[]types.ImageD
 		return err
 	}
 
-	i.LogImageEvent(imgID.String(), imgID.String(), "delete")
+	i.LogImageEvent(imgID.String(), imgID.String(), events.ActionDelete)
 	*records = append(*records, types.ImageDeleteResponseItem{Deleted: imgID.String()})
 	for _, removedLayer := range removedLayers {
 		*records = append(*records, types.ImageDeleteResponseItem{Deleted: removedLayer.ChainID.String()})

+ 3 - 5
daemon/images/image_events.go

@@ -8,7 +8,7 @@ import (
 )
 
 // LogImageEvent generates an event related to an image with only the default attributes.
-func (i *ImageService) LogImageEvent(imageID, refName, action string) {
+func (i *ImageService) LogImageEvent(imageID, refName string, action events.Action) {
 	ctx := context.TODO()
 	attributes := map[string]string{}
 
@@ -21,12 +21,10 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) {
 	if refName != "" {
 		attributes["name"] = refName
 	}
-	actor := events.Actor{
+	i.eventsService.Log(action, events.ImageEventType, events.Actor{
 		ID:         imageID,
 		Attributes: attributes,
-	}
-
-	i.eventsService.Log(action, events.ImageEventType, actor)
+	})
 }
 
 // copyAttributes guarantees that labels are not mutated by event triggers.

+ 2 - 1
daemon/images/image_import.go

@@ -9,6 +9,7 @@ import (
 	"github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/builder/dockerfile"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/errdefs"
@@ -85,6 +86,6 @@ func (i *ImageService) ImportImage(ctx context.Context, newRef reference.Named,
 		}
 	}
 
-	i.LogImageEvent(id.String(), id.String(), "import")
+	i.LogImageEvent(id.String(), id.String(), events.ActionImport)
 	return id, nil
 }

+ 1 - 1
daemon/images/image_prune.go

@@ -147,7 +147,7 @@ deleteImagesLoop:
 	if canceled {
 		log.G(ctx).Debugf("ImagesPrune operation cancelled: %#v", *rep)
 	}
-	i.eventsService.Log("prune", events.ImageEventType, events.Actor{
+	i.eventsService.Log(events.ActionPrune, events.ImageEventType, events.Actor{
 		Attributes: map[string]string{
 			"reclaimed": strconv.FormatUint(rep.SpaceReclaimed, 10),
 		},

+ 2 - 1
daemon/images/image_tag.go

@@ -4,6 +4,7 @@ import (
 	"context"
 
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/image"
 )
 
@@ -16,6 +17,6 @@ func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag re
 	if err := i.imageStore.SetLastUpdated(imageID); err != nil {
 		return err
 	}
-	i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")
+	i.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), events.ActionTag)
 	return nil
 }

+ 2 - 1
daemon/kill.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	containerpkg "github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/moby/sys/signal"
@@ -137,7 +138,7 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, stopSign
 		}
 	}
 
-	daemon.LogContainerEventWithAttributes(container, "kill", map[string]string{
+	daemon.LogContainerEventWithAttributes(container, events.ActionKill, map[string]string{
 		"signal": strconv.Itoa(int(stopSignal)),
 	})
 	return nil

+ 8 - 8
daemon/monitor.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/daemon/config"
 	"github.com/docker/docker/errdefs"
@@ -110,7 +111,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
 	daemon.setStateCounter(c)
 	checkpointErr := c.CheckpointTo(daemon.containersReplica)
 
-	daemon.LogContainerEventWithAttributes(c, "die", attributes)
+	daemon.LogContainerEventWithAttributes(c, events.ActionDie, attributes)
 
 	if restart {
 		go func() {
@@ -168,7 +169,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 			return err
 		}
 
-		daemon.LogContainerEvent(c, "oom")
+		daemon.LogContainerEvent(c, events.ActionOOM)
 	case libcontainerdtypes.EventExit:
 		if ei.ProcessID == ei.ContainerID {
 			return daemon.handleContainerExit(c, &ei)
@@ -219,11 +220,10 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 				}()
 			}
 		}
-		attributes := map[string]string{
+		daemon.LogContainerEventWithAttributes(c, events.ActionExecDie, map[string]string{
 			"execID":   ei.ProcessID,
 			"exitCode": strconv.Itoa(exitCode),
-		}
-		daemon.LogContainerEventWithAttributes(c, "exec_die", attributes)
+		})
 	case libcontainerdtypes.EventStart:
 		c.Lock()
 		defer c.Unlock()
@@ -264,7 +264,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
-			daemon.LogContainerEvent(c, "start")
+			daemon.LogContainerEvent(c, events.ActionStart)
 		}
 
 	case libcontainerdtypes.EventPaused:
@@ -278,7 +278,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
-			daemon.LogContainerEvent(c, "pause")
+			daemon.LogContainerEvent(c, events.ActionPause)
 		}
 	case libcontainerdtypes.EventResumed:
 		c.Lock()
@@ -292,7 +292,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
-			daemon.LogContainerEvent(c, "unpause")
+			daemon.LogContainerEvent(c, events.ActionUnPause)
 		}
 	}
 	return nil

+ 3 - 2
daemon/network.go

@@ -13,6 +13,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
@@ -388,7 +389,7 @@ func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCrea
 	if create.IPAM != nil {
 		daemon.pluginRefCount(create.IPAM.Driver, ipamapi.PluginEndpointType, plugingetter.Acquire)
 	}
-	daemon.LogNetworkEvent(n, "create")
+	daemon.LogNetworkEvent(n, events.ActionCreate)
 
 	return &types.NetworkCreateResponse{
 		ID:      n.ID(),
@@ -555,7 +556,7 @@ func (daemon *Daemon) deleteNetwork(nw *libnetwork.Network, dynamic bool) error
 		daemon.pluginRefCount(nw.Type(), driverapi.NetworkPluginEndpointType, plugingetter.Release)
 		ipamType, _, _, _ := nw.IpamConfig()
 		daemon.pluginRefCount(ipamType, ipamapi.PluginEndpointType, plugingetter.Release)
-		daemon.LogNetworkEvent(nw, "destroy")
+		daemon.LogNetworkEvent(nw, events.ActionDestroy)
 	}
 
 	return nil

+ 2 - 1
daemon/pause.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 )
 
@@ -46,7 +47,7 @@ func (daemon *Daemon) containerPause(container *container.Container) error {
 	container.Paused = true
 	daemon.setStateCounter(container)
 	daemon.updateHealthMonitor(container)
-	daemon.LogContainerEvent(container, "pause")
+	daemon.LogContainerEvent(container, events.ActionPause)
 
 	if err := container.CheckpointTo(daemon.containersReplica); err != nil {
 		log.G(context.TODO()).WithError(err).Warn("could not save container to disk")

+ 2 - 2
daemon/prune.go

@@ -89,7 +89,7 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.
 			rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
 		}
 	}
-	daemon.EventsService.Log("prune", events.ContainerEventType, events.Actor{
+	daemon.EventsService.Log(events.ActionPrune, events.ContainerEventType, events.Actor{
 		Attributes: map[string]string{"reclaimed": strconv.FormatUint(rep.SpaceReclaimed, 10)},
 	})
 	return rep, nil
@@ -216,7 +216,7 @@ func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Ar
 		return rep, nil
 	default:
 	}
-	daemon.EventsService.Log("prune", events.NetworkEventType, events.Actor{
+	daemon.EventsService.Log(events.ActionPrune, events.NetworkEventType, events.Actor{
 		Attributes: map[string]string{"reclaimed": "0"},
 	})
 	return rep, nil

+ 2 - 1
daemon/reload.go

@@ -7,6 +7,7 @@ import (
 	"strconv"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	"github.com/hashicorp/go-multierror"
 	"github.com/mitchellh/copystructure"
 
@@ -127,7 +128,7 @@ func (daemon *Daemon) Reload(conf *config.Config) error {
 	})
 	log.G(context.TODO()).Infof("Reloaded configuration: %s", jsonString)
 	daemon.configStore.Store(newCfg)
-	daemon.LogDaemonEventWithAttributes("reload", attributes)
+	daemon.LogDaemonEventWithAttributes(events.ActionReload, attributes)
 	return txn.Commit()
 }
 

+ 3 - 2
daemon/rename.go

@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	dockercontainer "github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libnetwork"
@@ -93,7 +94,7 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
 	}
 
 	if !container.Running {
-		daemon.LogContainerEventWithAttributes(container, "rename", attributes)
+		daemon.LogContainerEventWithAttributes(container, events.ActionRename, attributes)
 		return nil
 	}
 
@@ -120,6 +121,6 @@ func (daemon *Daemon) ContainerRename(oldName, newName string) error {
 		}
 	}
 
-	daemon.LogContainerEventWithAttributes(container, "rename", attributes)
+	daemon.LogContainerEventWithAttributes(container, events.ActionRename, attributes)
 	return nil
 }

+ 3 - 3
daemon/resize.go

@@ -6,6 +6,7 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/errdefs"
 )
 
@@ -25,11 +26,10 @@ func (daemon *Daemon) ContainerResize(name string, height, width int) error {
 	}
 
 	if err = tsk.Resize(context.Background(), uint32(width), uint32(height)); err == nil {
-		attributes := map[string]string{
+		daemon.LogContainerEventWithAttributes(container, events.ActionResize, map[string]string{
 			"height": strconv.Itoa(height),
 			"width":  strconv.Itoa(width),
-		}
-		daemon.LogContainerEventWithAttributes(container, "resize", attributes)
+		})
 	}
 	return err
 }

+ 2 - 1
daemon/restart.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 )
 
@@ -64,6 +65,6 @@ func (daemon *Daemon) containerRestart(ctx context.Context, daemonCfg *configSto
 		return err
 	}
 
-	daemon.LogContainerEvent(container, "restart")
+	daemon.LogContainerEvent(container, events.ActionRestart)
 	return nil
 }

+ 2 - 1
daemon/start.go

@@ -8,6 +8,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/libcontainerd"
@@ -222,7 +223,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore
 			Errorf("failed to store container")
 	}
 
-	daemon.LogContainerEvent(container, "start")
+	daemon.LogContainerEvent(container, events.ActionStart)
 	containerActions.WithValues("start").UpdateSince(start)
 
 	return nil

+ 2 - 1
daemon/stop.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/moby/sys/signal"
@@ -73,7 +74,7 @@ func (daemon *Daemon) containerStop(_ context.Context, ctr *container.Container,
 	}
 	defer func() {
 		if retErr == nil {
-			daemon.LogContainerEvent(ctr, "stop")
+			daemon.LogContainerEvent(ctr, events.ActionStop)
 		}
 	}()
 

+ 2 - 1
daemon/top_unix.go

@@ -12,6 +12,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/errdefs"
 	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
 	"github.com/pkg/errors"
@@ -198,6 +199,6 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*container.Conta
 	if err != nil {
 		return nil, err
 	}
-	daemon.LogContainerEvent(ctr, "top")
+	daemon.LogContainerEvent(ctr, events.ActionTop)
 	return procList, nil
 }

+ 2 - 1
daemon/unpause.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/container"
 )
 
@@ -38,7 +39,7 @@ func (daemon *Daemon) containerUnpause(ctr *container.Container) error {
 	ctr.Paused = false
 	daemon.setStateCounter(ctr)
 	daemon.updateHealthMonitor(ctr)
-	daemon.LogContainerEvent(ctr, "unpause")
+	daemon.LogContainerEvent(ctr, events.ActionUnPause)
 
 	if err := ctr.CheckpointTo(daemon.containersReplica); err != nil {
 		log.G(context.TODO()).WithError(err).Warn("could not save container to disk")

+ 2 - 1
daemon/update.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/errdefs"
 	"github.com/pkg/errors"
 )
@@ -75,7 +76,7 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
 		ctr.UpdateMonitor(hostConfig.RestartPolicy)
 	}
 
-	defer daemon.LogContainerEvent(ctr, "update")
+	defer daemon.LogContainerEvent(ctr, events.ActionUpdate)
 
 	// If container is not running, update hostConfig struct is enough,
 	// resources will be updated when the container is started again.

+ 3 - 3
daemon/volumes_unix.go

@@ -9,6 +9,7 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/docker/docker/api/types/events"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/container"
 	volumemounts "github.com/docker/docker/volume/mounts"
@@ -73,14 +74,13 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er
 				mnt.ReadOnlyForceRecursive = m.Spec.BindOptions.ReadOnlyForceRecursive
 			}
 			if m.Volume != nil {
-				attributes := map[string]string{
+				daemon.LogVolumeEvent(m.Volume.Name(), events.ActionMount, map[string]string{
 					"driver":      m.Volume.DriverName(),
 					"container":   c.ID,
 					"destination": m.Destination,
 					"read/write":  strconv.FormatBool(m.RW),
 					"propagation": string(m.Propagation),
-				}
-				daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes)
+				})
 			}
 			mounts = append(mounts, mnt)
 		}

+ 2 - 1
distribution/config.go

@@ -9,6 +9,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
@@ -38,7 +39,7 @@ type Config struct {
 	// and endpoint lookup.
 	RegistryService RegistryResolver
 	// ImageEventLogger notifies events for a given image
-	ImageEventLogger func(id, name, action string)
+	ImageEventLogger func(id, name string, action events.Action)
 	// MetadataStore is the storage backend for distribution-specific
 	// metadata.
 	MetadataStore metadata.Store

+ 2 - 1
distribution/pull.go

@@ -7,6 +7,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/types/events"
 	refstore "github.com/docker/docker/reference"
 	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
@@ -73,7 +74,7 @@ func Pull(ctx context.Context, ref reference.Named, config *ImagePullConfig, loc
 			return translatePullError(err, ref)
 		}
 
-		config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "pull")
+		config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), events.ActionPull)
 		return nil
 	}
 

+ 2 - 1
distribution/push.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/pkg/progress"
 )
 
@@ -77,7 +78,7 @@ func Push(ctx context.Context, ref reference.Named, config *ImagePushConfig) err
 			return err
 		}
 
-		config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), "push")
+		config.ImageEventLogger(reference.FamiliarString(ref), reference.FamiliarName(repoInfo.Name), events.ActionPush)
 		return nil
 	}
 

+ 2 - 1
image/tarexport/load.go

@@ -14,6 +14,7 @@ import (
 	"github.com/containerd/containerd/log"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/image"
 	v1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
@@ -137,7 +138,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		}
 
 		parentLinks = append(parentLinks, parentLink{imgID, m.Parent})
-		l.loggerImgEvent.LogImageEvent(imgID.String(), imgID.String(), "load")
+		l.loggerImgEvent.LogImageEvent(imgID.String(), imgID.String(), events.ActionLoad)
 	}
 
 	for _, p := range validatedParentLinks(parentLinks) {

+ 2 - 1
image/tarexport/save.go

@@ -12,6 +12,7 @@ import (
 	"github.com/containerd/containerd/images"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/reference"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/image"
 	v1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
@@ -290,7 +291,7 @@ func (s *saveSession) save(outStream io.Writer) error {
 
 		parentID, _ := s.is.GetParent(id)
 		parentLinks = append(parentLinks, parentLink{id, parentID})
-		s.tarexporter.loggerImgEvent.LogImageEvent(id.String(), id.String(), "save")
+		s.tarexporter.loggerImgEvent.LogImageEvent(id.String(), id.String(), events.ActionSave)
 	}
 
 	for i, p := range validatedParentLinks(parentLinks) {

+ 2 - 1
image/tarexport/tarexport.go

@@ -2,6 +2,7 @@ package tarexport // import "github.com/docker/docker/image/tarexport"
 
 import (
 	"github.com/docker/distribution"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	refstore "github.com/docker/docker/reference"
@@ -36,7 +37,7 @@ type tarexporter struct {
 // LogImageEvent defines interface for event generation related to image tar(load and save) operations
 type LogImageEvent interface {
 	// LogImageEvent generates an event related to an image operation
-	LogImageEvent(imageID, refName, action string)
+	LogImageEvent(imageID, refName string, action events.Action)
 }
 
 // NewTarExporter returns new Exporter for tar packages

+ 1 - 1
integration-cli/docker_cli_events_test.go

@@ -736,7 +736,7 @@ func (s *DockerCLIEventSuite) TestEventsFormat(c *testing.T) {
 			break
 		}
 		assert.NilError(c, err)
-		if ev.Action == "start" {
+		if ev.Action == eventtypes.ActionStart {
 			startCount++
 		}
 	}

+ 3 - 3
integration/container/pause_test.go

@@ -50,7 +50,7 @@ func TestPause(t *testing.T) {
 		Until:   until,
 		Filters: filters.NewArgs(filters.Arg(string(events.ContainerEventType), cID)),
 	})
-	assert.Check(t, is.DeepEqual([]string{"pause", "unpause"}, getEventActions(t, messages, errs)))
+	assert.Check(t, is.DeepEqual([]events.Action{events.ActionPause, events.ActionUnPause}, getEventActions(t, messages, errs)))
 }
 
 func TestPauseFailsOnWindowsServerContainers(t *testing.T) {
@@ -87,9 +87,9 @@ func TestPauseStopPausedContainer(t *testing.T) {
 	poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID), poll.WithDelay(100*time.Millisecond))
 }
 
-func getEventActions(t *testing.T, messages <-chan events.Message, errs <-chan error) []string {
+func getEventActions(t *testing.T, messages <-chan events.Message, errs <-chan error) []events.Action {
 	t.Helper()
-	var actions []string
+	var actions []events.Action
 	for {
 		select {
 		case err := <-errs:

+ 2 - 2
integration/plugin/authz/authz_plugin_test.go

@@ -232,10 +232,10 @@ func TestAuthZPluginAllowEventStream(t *testing.T) {
 		select {
 		case event := <-events:
 			if event.Type == eventtypes.ContainerEventType && event.Actor.ID == cID {
-				if event.Action == "create" {
+				if event.Action == eventtypes.ActionCreate {
 					created = true
 				}
-				if event.Action == "start" {
+				if event.Action == eventtypes.ActionStart {
 					started = true
 				}
 			}

+ 2 - 2
integration/system/event_test.go

@@ -46,7 +46,7 @@ func TestEventsExecDie(t *testing.T) {
 	msg, errs := client.Events(ctx, types.EventsOptions{
 		Filters: filters.NewArgs(
 			filters.Arg("container", cID),
-			filters.Arg("event", "exec_die"),
+			filters.Arg("event", string(events.ActionExecDie)),
 		),
 	})
 
@@ -62,7 +62,7 @@ func TestEventsExecDie(t *testing.T) {
 	case m := <-msg:
 		assert.Equal(t, m.Type, events.ContainerEventType)
 		assert.Equal(t, m.Actor.ID, cID)
-		assert.Equal(t, m.Action, "exec_die")
+		assert.Equal(t, m.Action, events.ActionExecDie)
 		assert.Equal(t, m.Actor.Attributes["execID"], id.ID)
 		assert.Equal(t, m.Actor.Attributes["exitCode"], "0")
 	case err = <-errs:

+ 7 - 6
plugin/backend_linux.go

@@ -23,6 +23,7 @@ import (
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/dockerversion"
@@ -69,7 +70,7 @@ func (pm *Manager) Disable(refOrID string, config *types.PluginDisableConfig) er
 		return err
 	}
 	pm.publisher.Publish(EventDisable{Plugin: p.PluginObj})
-	pm.config.LogPluginEvent(p.GetID(), refOrID, "disable")
+	pm.config.LogPluginEvent(p.GetID(), refOrID, events.ActionDisable)
 	return nil
 }
 
@@ -85,7 +86,7 @@ func (pm *Manager) Enable(refOrID string, config *types.PluginEnableConfig) erro
 		return err
 	}
 	pm.publisher.Publish(EventEnable{Plugin: p.PluginObj})
-	pm.config.LogPluginEvent(p.GetID(), refOrID, "enable")
+	pm.config.LogPluginEvent(p.GetID(), refOrID, events.ActionEnable)
 	return nil
 }
 
@@ -239,7 +240,7 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string
 	if err := pm.fetch(ctx, ref, authConfig, out, metaHeader, storeFetchMetadata(&md), childrenHandler(pm.blobStore), applyLayer(pm.blobStore, tmpRootFSDir, out)); err != nil {
 		return err
 	}
-	pm.config.LogPluginEvent(reference.FamiliarString(ref), name, "pull")
+	pm.config.LogPluginEvent(reference.FamiliarString(ref), name, events.ActionPull)
 
 	if err := validateFetchedMetadata(md); err != nil {
 		return err
@@ -285,7 +286,7 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m
 	if err := pm.fetch(ctx, ref, authConfig, out, metaHeader, storeFetchMetadata(&md), childrenHandler(pm.blobStore), applyLayer(pm.blobStore, tmpRootFSDir, out)); err != nil {
 		return err
 	}
-	pm.config.LogPluginEvent(reference.FamiliarString(ref), name, "pull")
+	pm.config.LogPluginEvent(reference.FamiliarString(ref), name, events.ActionPull)
 
 	if err := validateFetchedMetadata(md); err != nil {
 		return err
@@ -599,7 +600,7 @@ func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
 	}
 
 	pm.config.Store.Remove(p)
-	pm.config.LogPluginEvent(id, name, "remove")
+	pm.config.LogPluginEvent(id, name, events.ActionRemove)
 	pm.publisher.Publish(EventRemove{Plugin: p.PluginObj})
 	return nil
 }
@@ -727,7 +728,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser,
 	p.PluginObj.PluginReference = name
 
 	pm.publisher.Publish(EventCreate{Plugin: p.PluginObj})
-	pm.config.LogPluginEvent(p.PluginObj.ID, name, "create")
+	pm.config.LogPluginEvent(p.PluginObj.ID, name, events.ActionCreate)
 
 	return nil
 }

+ 2 - 1
plugin/manager.go

@@ -17,6 +17,7 @@ import (
 	"github.com/containerd/containerd/content/local"
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/pkg/authorization"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/ioutils"
@@ -56,7 +57,7 @@ func (pm *Manager) restorePlugin(p *v2.Plugin, c *controller) error {
 	return nil
 }
 
-type eventLogger func(id, name, action string)
+type eventLogger func(id, name string, action events.Action)
 
 // ManagerConfig defines configuration needed to start new manager.
 type ManagerConfig struct {

+ 5 - 4
plugin/manager_linux_test.go

@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/stringid"
 	v2 "github.com/docker/docker/plugin/v2"
@@ -40,7 +41,7 @@ func TestManagerWithPluginMounts(t *testing.T) {
 			Root:           managerRoot,
 			ExecRoot:       filepath.Join(root, "exec"),
 			CreateExecutor: func(*Manager) (Executor, error) { return nil, nil },
-			LogPluginEvent: func(_, _, _ string) {},
+			LogPluginEvent: func(_, _ string, _ events.Action) {},
 		})
 	if err != nil {
 		t.Fatal(err)
@@ -112,7 +113,7 @@ func TestCreateFailed(t *testing.T) {
 			Root:           managerRoot,
 			ExecRoot:       filepath.Join(root, "exec"),
 			CreateExecutor: func(*Manager) (Executor, error) { return &simpleExecutor{}, nil },
-			LogPluginEvent: func(_, _, _ string) {},
+			LogPluginEvent: func(_, _ string, _ events.Action) {},
 		})
 	if err != nil {
 		t.Fatal(err)
@@ -181,13 +182,13 @@ func TestPluginAlreadyRunningOnStartup(t *testing.T) {
 		{
 			desc: "live-restore-disabled",
 			config: ManagerConfig{
-				LogPluginEvent: func(_, _, _ string) {},
+				LogPluginEvent: func(_, _ string, _ events.Action) {},
 			},
 		},
 		{
 			desc: "live-restore-enabled",
 			config: ManagerConfig{
-				LogPluginEvent:     func(_, _, _ string) {},
+				LogPluginEvent:     func(_, _ string, _ events.Action) {},
 				LiveRestoreEnabled: true,
 			},
 		},

+ 1 - 1
testutil/daemon/daemon.go

@@ -781,7 +781,7 @@ func (d *Daemon) ReloadConfig() error {
 			if e.Type != events.DaemonEventType {
 				continue
 			}
-			if e.Action != "reload" {
+			if e.Action != events.ActionReload {
 				continue
 			}
 			close(errCh) // notify that we are done

+ 2 - 1
testutil/fixtures/plugin/plugin.go

@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/registry"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/plugin"
@@ -117,7 +118,7 @@ func CreateInRegistry(ctx context.Context, repo string, auth *registry.AuthConfi
 		Root:            filepath.Join(tmpDir, "root"),
 		ExecRoot:        "/run/docker", // manager init fails if not set
 		CreateExecutor:  dummyExec,
-		LogPluginEvent:  func(id, name, action string) {}, // panics when not set
+		LogPluginEvent:  func(id, name string, action events.Action) {}, // panics when not set
 	}
 	manager, err := plugin.NewManager(managerConfig)
 	if err != nil {

+ 3 - 2
volume/service/service.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	"github.com/docker/docker/errdefs"
@@ -27,7 +28,7 @@ type ds interface {
 // VolumeEventLogger interface provides methods to log volume-related events
 type VolumeEventLogger interface {
 	// LogVolumeEvent generates an event related to a volume.
-	LogVolumeEvent(volumeID, action string, attributes map[string]string)
+	LogVolumeEvent(volumeID string, action events.Action, attributes map[string]string)
 }
 
 // VolumesService manages access to volumes
@@ -248,7 +249,7 @@ func (s *VolumesService) Prune(ctx context.Context, filter filters.Args) (*types
 		rep.SpaceReclaimed += uint64(vSize)
 		rep.VolumesDeleted = append(rep.VolumesDeleted, v.Name())
 	}
-	s.eventLogger.LogVolumeEvent("", "prune", map[string]string{
+	s.eventLogger.LogVolumeEvent("", events.ActionPrune, map[string]string{
 		"reclaimed": strconv.FormatInt(int64(rep.SpaceReclaimed), 10),
 	})
 	return rep, nil

+ 2 - 1
volume/service/service_test.go

@@ -5,6 +5,7 @@ import (
 	"os"
 	"testing"
 
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/volume"
@@ -248,4 +249,4 @@ func newTestService(t *testing.T, ds *volumedrivers.Store) (*VolumesService, fun
 
 type dummyEventLogger struct{}
 
-func (dummyEventLogger) LogVolumeEvent(_, _ string, _ map[string]string) {}
+func (dummyEventLogger) LogVolumeEvent(_ string, _ events.Action, _ map[string]string) {}

+ 3 - 2
volume/service/store.go

@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/containerd/containerd/log"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume/drivers"
@@ -501,7 +502,7 @@ func (s *VolumeStore) Create(ctx context.Context, name, driverName string, creat
 	}
 
 	if created && s.eventLogger != nil {
-		s.eventLogger.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()})
+		s.eventLogger.LogVolumeEvent(v.Name(), events.ActionCreate, map[string]string{"driver": v.DriverName()})
 	}
 	s.setNamed(v, cfg.Reference)
 	return v, nil
@@ -833,7 +834,7 @@ func (s *VolumeStore) Remove(ctx context.Context, v volume.Volume, rmOpts ...opt
 		}
 	}
 	if err == nil && s.eventLogger != nil {
-		s.eventLogger.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()})
+		s.eventLogger.LogVolumeEvent(v.Name(), events.ActionDestroy, map[string]string{"driver": v.DriverName()})
 	}
 	return err
 }