mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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
|
// 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 (
|
var (
|
||||||
volumeMounts []volume.MountPoint
|
volumeMounts []volume.MountPoint
|
||||||
err error
|
err error
|
||||||
|
@ -649,6 +649,12 @@ func (container *Container) UnmountVolumes(forceSyscall bool) error {
|
||||||
if err := volumeMount.Volume.Unmount(); err != nil {
|
if err := volumeMount.Volume.Unmount(); err != nil {
|
||||||
return err
|
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.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (daemon *Daemon) containerStatPath(container *container.Container, path str
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.UnmountVolumes(true)
|
defer container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.UnmountVolumes(true)
|
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path
|
||||||
|
|
||||||
content = ioutils.NewReadCloserWrapper(data, func() error {
|
content = ioutils.NewReadCloserWrapper(data, func() error {
|
||||||
err := data.Close()
|
err := data.Close()
|
||||||
container.UnmountVolumes(true)
|
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
|
@ -181,7 +181,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path
|
||||||
defer daemon.Unmount(container)
|
defer daemon.Unmount(container)
|
||||||
|
|
||||||
err = daemon.mountVolumes(container)
|
err = daemon.mountVolumes(container)
|
||||||
defer container.UnmountVolumes(true)
|
defer container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// unmount any volumes
|
// unmount any volumes
|
||||||
container.UnmountVolumes(true)
|
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
// unmount the container's rootfs
|
// unmount the container's rootfs
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
}
|
}
|
||||||
|
@ -320,7 +320,7 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str
|
||||||
|
|
||||||
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
reader := ioutils.NewReadCloserWrapper(archive, func() error {
|
||||||
err := archive.Close()
|
err := archive.Close()
|
||||||
container.UnmountVolumes(true)
|
container.UnmountVolumes(true, daemon.LogVolumeEvent)
|
||||||
daemon.Unmount(container)
|
daemon.Unmount(container)
|
||||||
container.Unlock()
|
container.Unlock()
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -719,6 +719,11 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n li
|
||||||
if err := container.ToDiskLocking(); err != nil {
|
if err := container.ToDiskLocking(); err != nil {
|
||||||
return fmt.Errorf("Error saving container to disk: %v", err)
|
return fmt.Errorf("Error saving container to disk: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes := map[string]string{
|
||||||
|
"container": container.ID,
|
||||||
|
}
|
||||||
|
daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,14 +849,14 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sid := container.NetworkSettings.SandboxID
|
sid := container.NetworkSettings.SandboxID
|
||||||
networks := container.NetworkSettings.Networks
|
settings := container.NetworkSettings.Networks
|
||||||
for n := range networks {
|
for n := range settings {
|
||||||
networks[n] = &networktypes.EndpointSettings{}
|
settings[n] = &networktypes.EndpointSettings{}
|
||||||
}
|
}
|
||||||
|
|
||||||
container.NetworkSettings = &network.Settings{Networks: networks}
|
container.NetworkSettings = &network.Settings{Networks: networks}
|
||||||
|
|
||||||
if sid == "" || len(networks) == 0 {
|
if sid == "" || len(settings) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,6 +869,13 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
|
||||||
if err := sb.Delete(); err != nil {
|
if err := sb.Delete(); err != nil {
|
||||||
logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err)
|
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 {
|
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) {
|
if (driverName != "" && v.DriverName() != driverName) || (driverName == "" && v.DriverName() != volume.DefaultDriverName) {
|
||||||
return nil, derr.ErrorVolumeNameTaken.WithArgs(name, v.DriverName())
|
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
|
return volumeToAPIType(v), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,5 +157,6 @@ func (daemon *Daemon) VolumeRm(name string) error {
|
||||||
}
|
}
|
||||||
return derr.ErrorCodeRmVolume.WithArgs(name, err)
|
return derr.ErrorCodeRmVolume.WithArgs(name, err)
|
||||||
}
|
}
|
||||||
|
daemon.LogVolumeEvent(v.Name(), "destroy", map[string]string{"driver": v.DriverName()})
|
||||||
return nil
|
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) {
|
func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, action string, attributes map[string]string) {
|
||||||
attributes["name"] = nw.Name()
|
attributes["name"] = nw.Name()
|
||||||
attributes["type"] = nw.Type()
|
attributes["type"] = nw.Type()
|
||||||
|
daemon.logNetworkEventWithID(nw.ID(), action, attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) logNetworkEventWithID(id, action string, attributes map[string]string) {
|
||||||
actor := events.Actor{
|
actor := events.Actor{
|
||||||
ID: nw.ID(),
|
ID: id,
|
||||||
Attributes: attributes,
|
Attributes: attributes,
|
||||||
}
|
}
|
||||||
daemon.EventsService.Log(action, events.NetworkEventType, actor)
|
daemon.EventsService.Log(action, events.NetworkEventType, actor)
|
||||||
|
|
|
@ -156,7 +156,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
|
||||||
daemon.unregisterExecCommand(container, eConfig)
|
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)
|
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ package daemon
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/docker/docker/container"
|
"github.com/docker/docker/container"
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
@ -30,6 +31,16 @@ func (daemon *Daemon) setupMounts(container *container.Container) ([]execdriver.
|
||||||
Writable: m.RW,
|
Writable: m.RW,
|
||||||
Propagation: m.Propagation,
|
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)
|
mounts = append(mounts, mnt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -402,11 +402,6 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
|
||||||
func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
eventCreate := make(chan struct{})
|
|
||||||
eventStart := make(chan struct{})
|
|
||||||
eventDie := make(chan struct{})
|
|
||||||
eventDestroy := make(chan struct{})
|
|
||||||
|
|
||||||
observer, err := newEventObserver(c)
|
observer, err := newEventObserver(c)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
err = observer.Start()
|
err = observer.Start()
|
||||||
|
@ -415,42 +410,21 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
||||||
|
|
||||||
out, _ := dockerCmd(c, "run", "-d", "busybox:latest", "true")
|
out, _ := dockerCmd(c, "run", "-d", "busybox:latest", "true")
|
||||||
containerID := strings.TrimSpace(out)
|
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) {
|
testActions := map[string]chan bool{
|
||||||
switch {
|
"create": make(chan bool),
|
||||||
case matchCreate.MatchString(text):
|
"start": make(chan bool),
|
||||||
close(eventCreate)
|
"die": make(chan bool),
|
||||||
case matchStart.MatchString(text):
|
"destroy": make(chan bool),
|
||||||
close(eventStart)
|
|
||||||
case matchDie.MatchString(text):
|
|
||||||
close(eventDie)
|
|
||||||
case matchDestroy.MatchString(text):
|
|
||||||
close(eventDestroy)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
go observer.Match(matcher)
|
go observer.Match(matchEventLine(containerID, "container", testActions))
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
c.Fatal(observer.TimeoutError(containerID, "create"))
|
c.Fatal(observer.TimeoutError(containerID, "create/start/die"))
|
||||||
case <-testActions["create"]:
|
case <-testActions["create"]:
|
||||||
// ignore, done
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
c.Fatal(observer.TimeoutError(containerID, "start"))
|
|
||||||
case <-testActions["start"]:
|
case <-testActions["start"]:
|
||||||
// ignore, done
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
c.Fatal(observer.TimeoutError(containerID, "die"))
|
|
||||||
case <-testActions["die"]:
|
case <-testActions["die"]:
|
||||||
// ignore, done
|
// ignore, done
|
||||||
}
|
}
|
||||||
|
@ -460,7 +434,7 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
|
||||||
select {
|
select {
|
||||||
case <-time.After(5 * time.Second):
|
case <-time.After(5 * time.Second):
|
||||||
c.Fatal(observer.TimeoutError(containerID, "destroy"))
|
c.Fatal(observer.TimeoutError(containerID, "destroy"))
|
||||||
case <-eventDestroy:
|
case <-testActions["destroy"]:
|
||||||
// ignore, done
|
// ignore, done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,3 +151,29 @@ func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) {
|
||||||
<-ch
|
<-ch
|
||||||
c.Assert(out, checker.Contains, cID, check.Commentf("Missing event of container (foo)"))
|
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue