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")
}
} |