github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/monitor.go (about)

     1  package daemon
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/container"
    13  	"github.com/docker/docker/libcontainerd"
    14  	"github.com/docker/docker/restartmanager"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  func (daemon *Daemon) setStateCounter(c *container.Container) {
    19  	switch c.StateString() {
    20  	case "paused":
    21  		stateCtr.set(c.ID, "paused")
    22  	case "running":
    23  		stateCtr.set(c.ID, "running")
    24  	default:
    25  		stateCtr.set(c.ID, "stopped")
    26  	}
    27  }
    28  
    29  // ProcessEvent is called by libcontainerd whenever an event occurs
    30  func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libcontainerd.EventInfo) error {
    31  	c, err := daemon.GetContainer(id)
    32  	if c == nil || err != nil {
    33  		return fmt.Errorf("no such container: %s", id)
    34  	}
    35  
    36  	switch e {
    37  	case libcontainerd.EventOOM:
    38  		// StateOOM is Linux specific and should never be hit on Windows
    39  		if runtime.GOOS == "windows" {
    40  			return errors.New("received StateOOM from libcontainerd on Windows. This should never happen")
    41  		}
    42  
    43  		c.Lock()
    44  		defer c.Unlock()
    45  		daemon.updateHealthMonitor(c)
    46  		if err := c.CheckpointTo(daemon.containersReplica); err != nil {
    47  			return err
    48  		}
    49  
    50  		daemon.LogContainerEvent(c, "oom")
    51  	case libcontainerd.EventExit:
    52  		if int(ei.Pid) == c.Pid {
    53  			c.Lock()
    54  			_, _, err := daemon.containerd.DeleteTask(context.Background(), c.ID)
    55  			if err != nil {
    56  				logrus.WithError(err).Warnf("failed to delete container %s from containerd", c.ID)
    57  			}
    58  
    59  			c.StreamConfig.Wait()
    60  			c.Reset(false)
    61  
    62  			exitStatus := container.ExitStatus{
    63  				ExitCode:  int(ei.ExitCode),
    64  				ExitedAt:  ei.ExitedAt,
    65  				OOMKilled: ei.OOMKilled,
    66  			}
    67  			restart, wait, err := c.RestartManager().ShouldRestart(ei.ExitCode, daemon.IsShuttingDown() || c.HasBeenManuallyStopped, time.Since(c.StartedAt))
    68  			if err == nil && restart {
    69  				c.RestartCount++
    70  				c.SetRestarting(&exitStatus)
    71  			} else {
    72  				c.SetStopped(&exitStatus)
    73  				defer daemon.autoRemove(c)
    74  			}
    75  			defer c.Unlock() // needs to be called before autoRemove
    76  
    77  			// cancel healthcheck here, they will be automatically
    78  			// restarted if/when the container is started again
    79  			daemon.stopHealthchecks(c)
    80  			attributes := map[string]string{
    81  				"exitCode": strconv.Itoa(int(ei.ExitCode)),
    82  			}
    83  			daemon.LogContainerEventWithAttributes(c, "die", attributes)
    84  			daemon.Cleanup(c)
    85  
    86  			if err == nil && restart {
    87  				go func() {
    88  					err := <-wait
    89  					if err == nil {
    90  						// daemon.netController is initialized when daemon is restoring containers.
    91  						// But containerStart will use daemon.netController segment.
    92  						// So to avoid panic at startup process, here must wait util daemon restore done.
    93  						daemon.waitForStartupDone()
    94  						if err = daemon.containerStart(c, "", "", false); err != nil {
    95  							logrus.Debugf("failed to restart container: %+v", err)
    96  						}
    97  					}
    98  					if err != nil {
    99  						c.Lock()
   100  						c.SetStopped(&exitStatus)
   101  						c.Unlock()
   102  						defer daemon.autoRemove(c)
   103  						if err != restartmanager.ErrRestartCanceled {
   104  							logrus.Errorf("restartmanger wait error: %+v", err)
   105  						}
   106  					}
   107  				}()
   108  			}
   109  
   110  			daemon.setStateCounter(c)
   111  			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
   112  				return err
   113  			}
   114  			return daemon.postRunProcessing(c, ei)
   115  		}
   116  
   117  		if execConfig := c.ExecCommands.Get(ei.ProcessID); execConfig != nil {
   118  			ec := int(ei.ExitCode)
   119  			execConfig.Lock()
   120  			defer execConfig.Unlock()
   121  			execConfig.ExitCode = &ec
   122  			execConfig.Running = false
   123  			execConfig.StreamConfig.Wait()
   124  			if err := execConfig.CloseStreams(); err != nil {
   125  				logrus.Errorf("failed to cleanup exec %s streams: %s", c.ID, err)
   126  			}
   127  
   128  			// remove the exec command from the container's store only and not the
   129  			// daemon's store so that the exec command can be inspected.
   130  			c.ExecCommands.Delete(execConfig.ID, execConfig.Pid)
   131  		} else {
   132  			logrus.WithFields(logrus.Fields{
   133  				"container": c.ID,
   134  				"exec-id":   ei.ProcessID,
   135  				"exec-pid":  ei.Pid,
   136  			}).Warnf("Ignoring Exit Event, no such exec command found")
   137  		}
   138  	case libcontainerd.EventStart:
   139  		c.Lock()
   140  		defer c.Unlock()
   141  
   142  		// This is here to handle start not generated by docker
   143  		if !c.Running {
   144  			c.SetRunning(int(ei.Pid), false)
   145  			c.HasBeenManuallyStopped = false
   146  			c.HasBeenStartedBefore = true
   147  			daemon.setStateCounter(c)
   148  
   149  			daemon.initHealthMonitor(c)
   150  
   151  			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
   152  				return err
   153  			}
   154  			daemon.LogContainerEvent(c, "start")
   155  		}
   156  
   157  	case libcontainerd.EventPaused:
   158  		c.Lock()
   159  		defer c.Unlock()
   160  
   161  		if !c.Paused {
   162  			c.Paused = true
   163  			daemon.setStateCounter(c)
   164  			daemon.updateHealthMonitor(c)
   165  			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
   166  				return err
   167  			}
   168  			daemon.LogContainerEvent(c, "pause")
   169  		}
   170  	case libcontainerd.EventResumed:
   171  		c.Lock()
   172  		defer c.Unlock()
   173  
   174  		if c.Paused {
   175  			c.Paused = false
   176  			daemon.setStateCounter(c)
   177  			daemon.updateHealthMonitor(c)
   178  
   179  			if err := c.CheckpointTo(daemon.containersReplica); err != nil {
   180  				return err
   181  			}
   182  			daemon.LogContainerEvent(c, "unpause")
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  func (daemon *Daemon) autoRemove(c *container.Container) {
   189  	c.Lock()
   190  	ar := c.HostConfig.AutoRemove
   191  	c.Unlock()
   192  	if !ar {
   193  		return
   194  	}
   195  
   196  	var err error
   197  	if err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err == nil {
   198  		return
   199  	}
   200  	if c := daemon.containers.Get(c.ID); c == nil {
   201  		return
   202  	}
   203  
   204  	if err != nil {
   205  		logrus.WithError(err).WithField("container", c.ID).Error("error removing container")
   206  	}
   207  }