From 9d12d093009d3c4bf3bd4ebad3f8327c36d2d584 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 21 Dec 2015 19:45:31 -0500 Subject: [PATCH] Add volume events. Signed-off-by: David Calavera --- container/container_unix.go | 8 +++- container/container_windows.go | 2 +- daemon/archive.go | 12 ++--- daemon/container_operations_unix.go | 20 +++++++-- daemon/create.go | 5 +++ daemon/delete.go | 1 + daemon/events.go | 5 ++- daemon/start.go | 2 +- daemon/volumes_unix.go | 11 +++++ integration-cli/docker_cli_events_test.go | 44 ++++--------------- .../docker_cli_events_unix_test.go | 26 +++++++++++ integration-cli/events_utils.go | 1 + 12 files changed, 88 insertions(+), 49 deletions(-) diff --git a/container/container_unix.go b/container/container_unix.go index 0c3011d004..c9afcaa0c0 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -618,7 +618,7 @@ func detachMounted(path string) error { } // UnmountVolumes unmounts all volumes -func (container *Container) UnmountVolumes(forceSyscall bool) error { +func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { var ( volumeMounts []volume.MountPoint err error @@ -649,6 +649,12 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error { if err := volumeMount.Volume.Unmount(); err != nil { return err } + + attributes := map[string]string{ + "driver": volumeMount.Volume.DriverName(), + "container": container.ID, + } + volumeEventLog(volumeMount.Volume.Name(), "unmount", attributes) } } diff --git a/container/container_windows.go b/container/container_windows.go index 87d59c07bf..cd8134472f 100644 --- a/container/container_windows.go +++ b/container/container_windows.go @@ -39,7 +39,7 @@ func (container *Container) IpcMounts() []execdriver.Mount { } // UnmountVolumes explicitly unmounts volumes from the container. -func (container *Container) UnmountVolumes(forceSyscall bool) error { +func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error { return nil } diff --git a/daemon/archive.go b/daemon/archive.go index de329741c2..1c3cec0e9d 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -84,7 +84,7 @@ func (daemon *Daemon) containerStatPath(container *container.Container, path str defer daemon.Unmount(container) err = daemon.mountVolumes(container) - defer container.UnmountVolumes(true) + defer container.UnmountVolumes(true, daemon.LogVolumeEvent) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path defer func() { if err != nil { // unmount any volumes - container.UnmountVolumes(true) + container.UnmountVolumes(true, daemon.LogVolumeEvent) // unmount the container's rootfs daemon.Unmount(container) } @@ -154,7 +154,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path content = ioutils.NewReadCloserWrapper(data, func() error { err := data.Close() - container.UnmountVolumes(true) + container.UnmountVolumes(true, daemon.LogVolumeEvent) daemon.Unmount(container) container.Unlock() return err @@ -181,7 +181,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path defer daemon.Unmount(container) err = daemon.mountVolumes(container) - defer container.UnmountVolumes(true) + defer container.UnmountVolumes(true, daemon.LogVolumeEvent) if err != nil { return err } @@ -283,7 +283,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str defer func() { if err != nil { // unmount any volumes - container.UnmountVolumes(true) + container.UnmountVolumes(true, daemon.LogVolumeEvent) // unmount the container's rootfs daemon.Unmount(container) } @@ -320,7 +320,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str reader := ioutils.NewReadCloserWrapper(archive, func() error { err := archive.Close() - container.UnmountVolumes(true) + container.UnmountVolumes(true, daemon.LogVolumeEvent) daemon.Unmount(container) container.Unlock() return err diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 5858f74fcf..0b1091a68f 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -719,6 +719,11 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n li if err := container.ToDiskLocking(); err != nil { return fmt.Errorf("Error saving container to disk: %v", err) } + + attributes := map[string]string{ + "container": container.ID, + } + daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes) return nil } @@ -844,14 +849,14 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) { } sid := container.NetworkSettings.SandboxID - networks := container.NetworkSettings.Networks - for n := range networks { - networks[n] = &networktypes.EndpointSettings{} + settings := container.NetworkSettings.Networks + for n := range settings { + settings[n] = &networktypes.EndpointSettings{} } container.NetworkSettings = &network.Settings{Networks: networks} - if sid == "" || len(networks) == 0 { + if sid == "" || len(settings) == 0 { return } @@ -864,6 +869,13 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) { if err := sb.Delete(); err != nil { logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) } + + attributes := map[string]string{ + "container": container.ID, + } + for nwID := range settings { + daemon.logNetworkEventWithID(nwID, "disconnect", attributes) + } } func (daemon *Daemon) setupIpcDirs(c *container.Container) error { diff --git a/daemon/create.go b/daemon/create.go index bebf1ac29e..42002ce98e 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -169,5 +169,10 @@ func (daemon *Daemon) VolumeCreate(name, driverName string, opts map[string]stri if (driverName != "" && v.DriverName() != driverName) || (driverName == "" && v.DriverName() != volume.DefaultDriverName) { return nil, derr.ErrorVolumeNameTaken.WithArgs(name, v.DriverName()) } + + if driverName == "" { + driverName = volume.DefaultDriverName + } + daemon.LogVolumeEvent(name, "create", map[string]string{"driver": driverName}) return volumeToAPIType(v), nil } diff --git a/daemon/delete.go b/daemon/delete.go index dc01433291..6aaecc1289 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -157,5 +157,6 @@ func (daemon *Daemon) VolumeRm(name string) error { } return derr.ErrorCodeRmVolume.WithArgs(name, err) } + daemon.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()}) return nil } diff --git a/daemon/events.go b/daemon/events.go index cee57631c3..e9f9432af4 100644 --- a/daemon/events.go +++ b/daemon/events.go @@ -61,9 +61,12 @@ func (daemon *Daemon) LogNetworkEvent(nw libnetwork.Network, action string) { func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, action string, attributes map[string]string) { attributes["name"] = nw.Name() attributes["type"] = nw.Type() + daemon.logNetworkEventWithID(nw.ID(), action, attributes) +} +func (daemon *Daemon) logNetworkEventWithID(id, action string, attributes map[string]string) { actor := events.Actor{ - ID: nw.ID(), + ID: id, Attributes: attributes, } daemon.EventsService.Log(action, events.NetworkEventType, actor) diff --git a/daemon/start.go b/daemon/start.go index 8ea3f2db01..54cb94884f 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -156,7 +156,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) { daemon.unregisterExecCommand(container, eConfig) } - if err := container.UnmountVolumes(false); err != nil { + if err := container.UnmountVolumes(false, daemon.LogVolumeEvent); err != nil { logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) } } diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index f0d9f2df91..ea19953093 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -5,6 +5,7 @@ package daemon import ( "os" "sort" + "strconv" "github.com/docker/docker/container" "github.com/docker/docker/daemon/execdriver" @@ -30,6 +31,16 @@ func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver. Writable: m.RW, Propagation: m.Propagation, } + if m.Volume != nil { + attributes := map[string]string{ + "driver": m.Volume.DriverName(), + "container": container.ID, + "destination": m.Destination, + "read/write": strconv.FormatBool(m.RW), + "propagation": m.Propagation, + } + daemon.LogVolumeEvent(m.Volume.Name(), "mount", attributes) + } mounts = append(mounts, mnt) } } diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index 82d720455d..60bce8a709 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -402,11 +402,6 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) { func (s *DockerSuite) TestEventsStreaming(c *check.C) { testRequires(c, DaemonIsLinux) - eventCreate := make(chan struct{}) - eventStart := make(chan struct{}) - eventDie := make(chan struct{}) - eventDestroy := make(chan struct{}) - observer, err := newEventObserver(c) c.Assert(err, checker.IsNil) err = observer.Start() @@ -415,42 +410,21 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) { out, _ := dockerCmd(c, "run", "-d", "busybox:latest", "true") containerID := strings.TrimSpace(out) - matchCreate := regexp.MustCompile(containerID + `: \(from busybox:latest\) create\z`) - matchStart := regexp.MustCompile(containerID + `: \(from busybox:latest\) start\z`) - matchDie := regexp.MustCompile(containerID + `: \(from busybox:latest\) die\z`) - matchDestroy := regexp.MustCompile(containerID + `: \(from busybox:latest\) destroy\z`) - matcher := func(text string) { - switch { - case matchCreate.MatchString(text): - close(eventCreate) - case matchStart.MatchString(text): - close(eventStart) - case matchDie.MatchString(text): - close(eventDie) - case matchDestroy.MatchString(text): - close(eventDestroy) - } + testActions := map[string]chan bool{ + "create": make(chan bool), + "start": make(chan bool), + "die": make(chan bool), + "destroy": make(chan bool), } - go observer.Match(matcher) + + go observer.Match(matchEventLine(containerID, "container", testActions)) select { case <-time.After(5 * time.Second): - c.Fatal(observer.TimeoutError(containerID, "create")) + c.Fatal(observer.TimeoutError(containerID, "create/start/die")) case <-testActions["create"]: - // ignore, done - } - - select { - case <-time.After(5 * time.Second): - c.Fatal(observer.TimeoutError(containerID, "start")) case <-testActions["start"]: - // ignore, done - } - - select { - case <-time.After(5 * time.Second): - c.Fatal(observer.TimeoutError(containerID, "die")) case <-testActions["die"]: // ignore, done } @@ -460,7 +434,7 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) { select { case <-time.After(5 * time.Second): c.Fatal(observer.TimeoutError(containerID, "destroy")) - case <-eventDestroy: + case <-testActions["destroy"]: // ignore, done } } diff --git a/integration-cli/docker_cli_events_unix_test.go b/integration-cli/docker_cli_events_unix_test.go index b287a65e1b..a6353d0453 100644 --- a/integration-cli/docker_cli_events_unix_test.go +++ b/integration-cli/docker_cli_events_unix_test.go @@ -151,3 +151,29 @@ func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) { <-ch c.Assert(out, checker.Contains, cID, check.Commentf("Missing event of container (foo)")) } + +func (s *DockerSuite) TestVolumeEvents(c *check.C) { + testRequires(c, DaemonIsLinux) + + since := daemonTime(c).Unix() + + // Observe create/mount volume actions + dockerCmd(c, "volume", "create", "--name", "test-event-volume-local") + dockerCmd(c, "run", "--name", "test-volume-container", "--volume", "test-event-volume-local:/foo", "-d", "busybox", "true") + waitRun("test-volume-container") + + // Observe unmount/destroy volume actions + dockerCmd(c, "rm", "-f", "test-volume-container") + dockerCmd(c, "volume", "rm", "test-event-volume-local") + + out, _ := dockerCmd(c, "events", fmt.Sprintf("--since=%d", since), fmt.Sprintf("--until=%d", daemonTime(c).Unix())) + events := strings.Split(strings.TrimSpace(out), "\n") + c.Assert(len(events), checker.GreaterThan, 4) + + volumeEvents := eventActionsByIDAndType(c, events, "test-event-volume-local", "volume") + c.Assert(volumeEvents, checker.HasLen, 4) + c.Assert(volumeEvents[0], checker.Equals, "create") + c.Assert(volumeEvents[1], checker.Equals, "mount") + c.Assert(volumeEvents[2], checker.Equals, "unmount") + c.Assert(volumeEvents[3], checker.Equals, "destroy") +} diff --git a/integration-cli/events_utils.go b/integration-cli/events_utils.go index 59e88477d1..9548d1fff2 100644 --- a/integration-cli/events_utils.go +++ b/integration-cli/events_utils.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os/exec" + "regexp" "strconv" "strings"