github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/libcontainerd/container_linux.go (about) 1 package libcontainerd 2 3 import ( 4 "encoding/json" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "syscall" 10 "time" 11 12 "github.com/Sirupsen/logrus" 13 containerd "github.com/docker/containerd/api/grpc/types" 14 "github.com/docker/docker/restartmanager" 15 "github.com/opencontainers/specs/specs-go" 16 "golang.org/x/net/context" 17 ) 18 19 type container struct { 20 containerCommon 21 22 // Platform specific fields are below here. 23 pauseMonitor 24 oom bool 25 runtime string 26 runtimeArgs []string 27 } 28 29 type runtime struct { 30 path string 31 args []string 32 } 33 34 // WithRuntime sets the runtime to be used for the created container 35 func WithRuntime(path string, args []string) CreateOption { 36 return runtime{path, args} 37 } 38 39 func (rt runtime) Apply(p interface{}) error { 40 if pr, ok := p.(*container); ok { 41 pr.runtime = rt.path 42 pr.runtimeArgs = rt.args 43 } 44 return nil 45 } 46 47 func (ctr *container) clean() error { 48 if os.Getenv("LIBCONTAINERD_NOCLEAN") == "1" { 49 return nil 50 } 51 if _, err := os.Lstat(ctr.dir); err != nil { 52 if os.IsNotExist(err) { 53 return nil 54 } 55 return err 56 } 57 58 if err := os.RemoveAll(ctr.dir); err != nil { 59 return err 60 } 61 return nil 62 } 63 64 // cleanProcess removes the fifos used by an additional process. 65 // Caller needs to lock container ID before calling this method. 66 func (ctr *container) cleanProcess(id string) { 67 if p, ok := ctr.processes[id]; ok { 68 for _, i := range []int{syscall.Stdin, syscall.Stdout, syscall.Stderr} { 69 if err := os.Remove(p.fifo(i)); err != nil { 70 logrus.Warnf("failed to remove %v for process %v: %v", p.fifo(i), id, err) 71 } 72 } 73 } 74 delete(ctr.processes, id) 75 } 76 77 func (ctr *container) spec() (*specs.Spec, error) { 78 var spec specs.Spec 79 dt, err := ioutil.ReadFile(filepath.Join(ctr.dir, configFilename)) 80 if err != nil { 81 return nil, err 82 } 83 if err := json.Unmarshal(dt, &spec); err != nil { 84 return nil, err 85 } 86 return &spec, nil 87 } 88 89 func (ctr *container) start() error { 90 spec, err := ctr.spec() 91 if err != nil { 92 return nil 93 } 94 iopipe, err := ctr.openFifos(spec.Process.Terminal) 95 if err != nil { 96 return err 97 } 98 99 r := &containerd.CreateContainerRequest{ 100 Id: ctr.containerID, 101 BundlePath: ctr.dir, 102 Stdin: ctr.fifo(syscall.Stdin), 103 Stdout: ctr.fifo(syscall.Stdout), 104 Stderr: ctr.fifo(syscall.Stderr), 105 // check to see if we are running in ramdisk to disable pivot root 106 NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", 107 Runtime: ctr.runtime, 108 RuntimeArgs: ctr.runtimeArgs, 109 } 110 ctr.client.appendContainer(ctr) 111 112 resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r) 113 if err != nil { 114 ctr.closeFifos(iopipe) 115 return err 116 } 117 ctr.startedAt = time.Now() 118 119 if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil { 120 return err 121 } 122 ctr.systemPid = systemPid(resp.Container) 123 124 return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{ 125 CommonStateInfo: CommonStateInfo{ 126 State: StateStart, 127 Pid: ctr.systemPid, 128 }}) 129 } 130 131 func (ctr *container) newProcess(friendlyName string) *process { 132 return &process{ 133 dir: ctr.dir, 134 processCommon: processCommon{ 135 containerID: ctr.containerID, 136 friendlyName: friendlyName, 137 client: ctr.client, 138 }, 139 } 140 } 141 142 func (ctr *container) handleEvent(e *containerd.Event) error { 143 ctr.client.lock(ctr.containerID) 144 defer ctr.client.unlock(ctr.containerID) 145 switch e.Type { 146 case StateExit, StatePause, StateResume, StateOOM: 147 st := StateInfo{ 148 CommonStateInfo: CommonStateInfo{ 149 State: e.Type, 150 ExitCode: e.Status, 151 }, 152 OOMKilled: e.Type == StateExit && ctr.oom, 153 } 154 if e.Type == StateOOM { 155 ctr.oom = true 156 } 157 if e.Type == StateExit && e.Pid != InitFriendlyName { 158 st.ProcessID = e.Pid 159 st.State = StateExitProcess 160 } 161 if st.State == StateExit && ctr.restartManager != nil { 162 restart, wait, err := ctr.restartManager.ShouldRestart(e.Status, false, time.Since(ctr.startedAt)) 163 if err != nil { 164 logrus.Warnf("container %s %v", ctr.containerID, err) 165 } else if restart { 166 st.State = StateRestart 167 ctr.restarting = true 168 ctr.client.deleteContainer(e.Id) 169 go func() { 170 err := <-wait 171 ctr.client.lock(ctr.containerID) 172 defer ctr.client.unlock(ctr.containerID) 173 ctr.restarting = false 174 if err != nil { 175 st.State = StateExit 176 ctr.clean() 177 ctr.client.q.append(e.Id, func() { 178 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 179 logrus.Error(err) 180 } 181 }) 182 if err != restartmanager.ErrRestartCanceled { 183 logrus.Error(err) 184 } 185 } else { 186 ctr.start() 187 } 188 }() 189 } 190 } 191 192 // Remove process from list if we have exited 193 // We need to do so here in case the Message Handler decides to restart it. 194 switch st.State { 195 case StateExit: 196 ctr.clean() 197 ctr.client.deleteContainer(e.Id) 198 case StateExitProcess: 199 ctr.cleanProcess(st.ProcessID) 200 } 201 ctr.client.q.append(e.Id, func() { 202 if err := ctr.client.backend.StateChanged(e.Id, st); err != nil { 203 logrus.Error(err) 204 } 205 if e.Type == StatePause || e.Type == StateResume { 206 ctr.pauseMonitor.handle(e.Type) 207 } 208 if e.Type == StateExit { 209 if en := ctr.client.getExitNotifier(e.Id); en != nil { 210 en.close() 211 } 212 } 213 }) 214 215 default: 216 logrus.Debugf("event unhandled: %+v", e) 217 } 218 return nil 219 } 220 221 // discardFifos attempts to fully read the container fifos to unblock processes 222 // that may be blocked on the writer side. 223 func (ctr *container) discardFifos() { 224 for _, i := range []int{syscall.Stdout, syscall.Stderr} { 225 f := ctr.fifo(i) 226 c := make(chan struct{}) 227 go func() { 228 close(c) // this channel is used to not close the writer too early, before readonly open has been called. 229 io.Copy(ioutil.Discard, openReaderFromFifo(f)) 230 }() 231 <-c 232 closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side 233 } 234 }