github.com/sams1990/dockerrepo@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 }