daemon/monitor.go
9c4570a9
 package daemon
 
 import (
ddae20c0
 	"context"
9c4570a9
 	"errors"
 	"fmt"
 	"runtime"
 	"strconv"
606a245d
 	"time"
9c4570a9
 
91e197d6
 	"github.com/docker/docker/api/types"
e4c03623
 	"github.com/docker/docker/container"
9c4570a9
 	"github.com/docker/docker/libcontainerd"
606a245d
 	"github.com/docker/docker/restartmanager"
1009e6a4
 	"github.com/sirupsen/logrus"
9c4570a9
 )
 
e4c03623
 func (daemon *Daemon) setStateCounter(c *container.Container) {
 	switch c.StateString() {
 	case "paused":
 		stateCtr.set(c.ID, "paused")
 	case "running":
 		stateCtr.set(c.ID, "running")
 	default:
 		stateCtr.set(c.ID, "stopped")
 	}
 }
 
ddae20c0
 // ProcessEvent is called by libcontainerd whenever an event occurs
 func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libcontainerd.EventInfo) error {
 	c, err := daemon.GetContainer(id)
 	if c == nil || err != nil {
9c4570a9
 		return fmt.Errorf("no such container: %s", id)
 	}
 
ddae20c0
 	switch e {
 	case libcontainerd.EventOOM:
9c4570a9
 		// StateOOM is Linux specific and should never be hit on Windows
 		if runtime.GOOS == "windows" {
9b47b7b1
 			return errors.New("received StateOOM from libcontainerd on Windows. This should never happen")
9c4570a9
 		}
972cb497
 
 		c.Lock()
 		defer c.Unlock()
b6c7becb
 		daemon.updateHealthMonitor(c)
04bd768a
 		if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 			return err
 		}
972cb497
 
9c4570a9
 		daemon.LogContainerEvent(c, "oom")
ddae20c0
 	case libcontainerd.EventExit:
 		if int(ei.Pid) == c.Pid {
6c03aa31
 			c.Lock()
ddae20c0
 			_, _, err := daemon.containerd.DeleteTask(context.Background(), c.ID)
 			if err != nil {
 				logrus.WithError(err).Warnf("failed to delete container %s from containerd", c.ID)
 			}
606a245d
 
ddae20c0
 			c.StreamConfig.Wait()
 			c.Reset(false)
606a245d
 
ddae20c0
 			exitStatus := container.ExitStatus{
 				ExitCode:  int(ei.ExitCode),
 				ExitedAt:  ei.ExitedAt,
 				OOMKilled: ei.OOMKilled,
 			}
 			restart, wait, err := c.RestartManager().ShouldRestart(ei.ExitCode, daemon.IsShuttingDown() || c.HasBeenManuallyStopped, time.Since(c.StartedAt))
 			if err == nil && restart {
 				c.RestartCount++
 				c.SetRestarting(&exitStatus)
 			} else {
 				c.SetStopped(&exitStatus)
 				defer daemon.autoRemove(c)
 			}
6c03aa31
 			defer c.Unlock() // needs to be called before autoRemove
ddae20c0
 
 			// cancel healthcheck here, they will be automatically
 			// restarted if/when the container is started again
 			daemon.stopHealthchecks(c)
 			attributes := map[string]string{
 				"exitCode": strconv.Itoa(int(ei.ExitCode)),
 			}
 			daemon.LogContainerEventWithAttributes(c, "die", attributes)
 			daemon.Cleanup(c)
 
 			if err == nil && restart {
 				go func() {
 					err := <-wait
 					if err == nil {
 						// daemon.netController is initialized when daemon is restoring containers.
 						// But containerStart will use daemon.netController segment.
 						// So to avoid panic at startup process, here must wait util daemon restore done.
 						daemon.waitForStartupDone()
 						if err = daemon.containerStart(c, "", "", false); err != nil {
 							logrus.Debugf("failed to restart container: %+v", err)
 						}
606a245d
 					}
ddae20c0
 					if err != nil {
6c03aa31
 						c.Lock()
ddae20c0
 						c.SetStopped(&exitStatus)
6c03aa31
 						c.Unlock()
ddae20c0
 						defer daemon.autoRemove(c)
 						if err != restartmanager.ErrRestartCanceled {
 							logrus.Errorf("restartmanger wait error: %+v", err)
 						}
606a245d
 					}
ddae20c0
 				}()
 			}
e4c03623
 
ddae20c0
 			daemon.setStateCounter(c)
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
 			return daemon.postRunProcessing(c, ei)
818a5198
 		}
ddae20c0
 
6f3e86e9
 		if execConfig := c.ExecCommands.Get(ei.ProcessID); execConfig != nil {
ddae20c0
 			ec := int(ei.ExitCode)
07cd1965
 			execConfig.Lock()
 			defer execConfig.Unlock()
9c4570a9
 			execConfig.ExitCode = &ec
 			execConfig.Running = false
5ea75bb6
 			execConfig.StreamConfig.Wait()
9c4570a9
 			if err := execConfig.CloseStreams(); err != nil {
3cc0d6bb
 				logrus.Errorf("failed to cleanup exec %s streams: %s", c.ID, err)
9c4570a9
 			}
 
 			// remove the exec command from the container's store only and not the
 			// daemon's store so that the exec command can be inspected.
ddae20c0
 			c.ExecCommands.Delete(execConfig.ID, execConfig.Pid)
aa6bb5cb
 			attributes := map[string]string{
 				"execID":   execConfig.ID,
 				"exitCode": strconv.Itoa(ec),
 			}
 			daemon.LogContainerEventWithAttributes(c, "exec_die", attributes)
9c4570a9
 		} else {
ddae20c0
 			logrus.WithFields(logrus.Fields{
 				"container": c.ID,
6f3e86e9
 				"exec-id":   ei.ProcessID,
ddae20c0
 				"exec-pid":  ei.Pid,
 			}).Warnf("Ignoring Exit Event, no such exec command found")
9c4570a9
 		}
ddae20c0
 	case libcontainerd.EventStart:
 		c.Lock()
 		defer c.Unlock()
 
 		// This is here to handle start not generated by docker
 		if !c.Running {
 			c.SetRunning(int(ei.Pid), false)
 			c.HasBeenManuallyStopped = false
 			c.HasBeenStartedBefore = true
 			daemon.setStateCounter(c)
 
 			daemon.initHealthMonitor(c)
 
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
 			daemon.LogContainerEvent(c, "start")
9c4570a9
 		}
e4c03623
 
ddae20c0
 	case libcontainerd.EventPaused:
 		c.Lock()
 		defer c.Unlock()
 
 		if !c.Paused {
 			c.Paused = true
 			daemon.setStateCounter(c)
 			daemon.updateHealthMonitor(c)
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
 			daemon.LogContainerEvent(c, "pause")
f4bbfc34
 		}
ddae20c0
 	case libcontainerd.EventResumed:
 		c.Lock()
 		defer c.Unlock()
 
 		if c.Paused {
 			c.Paused = false
 			daemon.setStateCounter(c)
 			daemon.updateHealthMonitor(c)
 
 			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
 				return err
 			}
 			daemon.LogContainerEvent(c, "unpause")
f4bbfc34
 		}
9c4570a9
 	}
 	return nil
 }
54dcbab2
 
 func (daemon *Daemon) autoRemove(c *container.Container) {
 	c.Lock()
 	ar := c.HostConfig.AutoRemove
 	c.Unlock()
 	if !ar {
 		return
 	}
 
 	var err error
 	if err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err == nil {
 		return
 	}
 	if c := daemon.containers.Get(c.ID); c == nil {
 		return
 	}
 
 	if err != nil {
 		logrus.WithError(err).WithField("container", c.ID).Error("error removing container")
 	}
 }