Add volume events.
Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
72f1881df1
commit
9d12d09300
12 changed files with 88 additions and 49 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
Loading…
Reference in a new issue