func (s *GardenServer) streamProcess(logger lager.Logger, conn net.Conn, process garden.Process, stdinPipe *io.PipeWriter, connCloseCh chan struct{}) { statusCh := make(chan int, 1) errCh := make(chan error, 1) go func() { status, err := process.Wait() if err != nil { logger.Error("wait-failed", err, lager.Data{ "id": process.ID(), }) errCh <- err } else { logger.Info("exited", lager.Data{ "status": status, "id": process.ID(), }) statusCh <- status } }() for { select { case status := <-statusCh: transport.WriteMessage(conn, &transport.ProcessPayload{ ProcessID: process.ID(), ExitStatus: &status, }) stdinPipe.Close() return case err := <-errCh: e := err.Error() transport.WriteMessage(conn, &transport.ProcessPayload{ ProcessID: process.ID(), Error: &e, }) stdinPipe.Close() return case <-s.stopping: logger.Debug("detaching", lager.Data{ "id": process.ID(), }) return case <-connCloseCh: return } } }
buff = gbytes.NewBuffer() process, err = container.Run(garden.ProcessSpec{ User: "******", Path: "sh", Args: []string{ "-c", "while true; do echo stillhere; sleep 1; done", }, }, garden.ProcessIO{Stdout: buff}) Expect(err).ToNot(HaveOccurred()) Eventually(buff).Should(gbytes.Say("stillhere")) // make sure we dont kill before the process is spawned to avoid false-positives Expect(process.Signal(garden.SignalKill)).To(Succeed()) }) It("goes away", func(done Done) { Expect(process.Wait()).NotTo(Equal(0)) Consistently(buff, "5s").ShouldNot(gbytes.Say("stillhere")) close(done) }, 30) }) }) It("avoids a race condition when sending a kill signal", func(done Done) { for i := 0; i < 20; i++ { process, err := container.Run(garden.ProcessSpec{ User: "******", Path: "sh", Args: []string{"-c", `while true; do echo -n "x"; sleep 1; done`}, }, garden.ProcessIO{ Stdout: GinkgoWriter, Stderr: GinkgoWriter,