github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/daemon/monitor.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "runtime" 8 "strconv" 9 10 "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/daemon/exec" 12 "github.com/docker/docker/libcontainerd" 13 "github.com/docker/docker/runconfig" 14 "github.com/docker/engine-api/types" 15 ) 16 17 // StateChanged updates daemon state changes from containerd 18 func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error { 19 c := daemon.containers.Get(id) 20 if c == nil { 21 return fmt.Errorf("no such container: %s", id) 22 } 23 24 switch e.State { 25 case libcontainerd.StateOOM: 26 // StateOOM is Linux specific and should never be hit on Windows 27 if runtime.GOOS == "windows" { 28 return errors.New("Received StateOOM from libcontainerd on Windows. This should never happen.") 29 } 30 daemon.updateHealthMonitor(c) 31 daemon.LogContainerEvent(c, "oom") 32 case libcontainerd.StateExit: 33 // if containers AutoRemove flag is set, remove it after clean up 34 if c.HostConfig.AutoRemove { 35 defer func() { 36 if err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { 37 logrus.Errorf("can't remove container %s: %v", c.ID, err) 38 } 39 }() 40 } 41 c.Lock() 42 defer c.Unlock() 43 c.Wait() 44 c.Reset(false) 45 c.SetStopped(platformConstructExitStatus(e)) 46 attributes := map[string]string{ 47 "exitCode": strconv.Itoa(int(e.ExitCode)), 48 } 49 daemon.updateHealthMonitor(c) 50 daemon.LogContainerEventWithAttributes(c, "die", attributes) 51 daemon.Cleanup(c) 52 // FIXME: here is race condition between two RUN instructions in Dockerfile 53 // because they share same runconfig and change image. Must be fixed 54 // in builder/builder.go 55 if err := c.ToDisk(); err != nil { 56 return err 57 } 58 return daemon.postRunProcessing(c, e) 59 case libcontainerd.StateRestart: 60 c.Lock() 61 defer c.Unlock() 62 c.Reset(false) 63 c.RestartCount++ 64 c.SetRestarting(platformConstructExitStatus(e)) 65 attributes := map[string]string{ 66 "exitCode": strconv.Itoa(int(e.ExitCode)), 67 } 68 daemon.LogContainerEventWithAttributes(c, "die", attributes) 69 daemon.updateHealthMonitor(c) 70 return c.ToDisk() 71 case libcontainerd.StateExitProcess: 72 c.Lock() 73 defer c.Unlock() 74 if execConfig := c.ExecCommands.Get(e.ProcessID); execConfig != nil { 75 ec := int(e.ExitCode) 76 execConfig.ExitCode = &ec 77 execConfig.Running = false 78 execConfig.Wait() 79 if err := execConfig.CloseStreams(); err != nil { 80 logrus.Errorf("%s: %s", c.ID, err) 81 } 82 83 // remove the exec command from the container's store only and not the 84 // daemon's store so that the exec command can be inspected. 85 c.ExecCommands.Delete(execConfig.ID) 86 } else { 87 logrus.Warnf("Ignoring StateExitProcess for %v but no exec command found", e) 88 } 89 case libcontainerd.StateStart, libcontainerd.StateRestore: 90 // Container is already locked in this case 91 c.SetRunning(int(e.Pid), e.State == libcontainerd.StateStart) 92 c.HasBeenManuallyStopped = false 93 if err := c.ToDisk(); err != nil { 94 c.Reset(false) 95 return err 96 } 97 daemon.initHealthMonitor(c) 98 daemon.LogContainerEvent(c, "start") 99 case libcontainerd.StatePause: 100 // Container is already locked in this case 101 c.Paused = true 102 daemon.updateHealthMonitor(c) 103 daemon.LogContainerEvent(c, "pause") 104 case libcontainerd.StateResume: 105 // Container is already locked in this case 106 c.Paused = false 107 daemon.updateHealthMonitor(c) 108 daemon.LogContainerEvent(c, "unpause") 109 } 110 111 return nil 112 } 113 114 // AttachStreams is called by libcontainerd to connect the stdio. 115 func (daemon *Daemon) AttachStreams(id string, iop libcontainerd.IOPipe) error { 116 var ( 117 s *runconfig.StreamConfig 118 ec *exec.Config 119 ) 120 121 c := daemon.containers.Get(id) 122 if c == nil { 123 var err error 124 ec, err = daemon.getExecConfig(id) 125 if err != nil { 126 return fmt.Errorf("no such exec/container: %s", id) 127 } 128 s = ec.StreamConfig 129 } else { 130 s = c.StreamConfig 131 if err := daemon.StartLogging(c); err != nil { 132 c.Reset(false) 133 return err 134 } 135 } 136 137 copyFunc := func(w io.Writer, r io.Reader) { 138 s.Add(1) 139 go func() { 140 if _, err := io.Copy(w, r); err != nil { 141 logrus.Errorf("%v stream copy error: %v", id, err) 142 } 143 s.Done() 144 }() 145 } 146 147 if iop.Stdout != nil { 148 copyFunc(s.Stdout(), iop.Stdout) 149 } 150 if iop.Stderr != nil { 151 copyFunc(s.Stderr(), iop.Stderr) 152 } 153 154 if stdin := s.Stdin(); stdin != nil { 155 if iop.Stdin != nil { 156 go func() { 157 io.Copy(iop.Stdin, stdin) 158 iop.Stdin.Close() 159 }() 160 } 161 } else { 162 //TODO(swernli): On Windows, not closing stdin when no tty is requested by the exec Config 163 // results in a hang. We should re-evaluate generalizing this fix for all OSes if 164 // we can determine that is the right thing to do more generally. 165 if (c != nil && !c.Config.Tty) || (ec != nil && !ec.Tty && runtime.GOOS == "windows") { 166 // tty is enabled, so dont close containerd's iopipe stdin. 167 if iop.Stdin != nil { 168 iop.Stdin.Close() 169 } 170 } 171 } 172 173 return nil 174 }