func flushProcess(conn net.Conn, process garden.Process, stdout <-chan []byte, stderr <-chan []byte) { stdoutSource := transport.Stdout stderrSource := transport.Stderr for { select { case data := <-stdout: d := string(data) transport.WriteMessage(conn, &transport.ProcessPayload{ ProcessID: process.ID(), Source: &stdoutSource, Data: &d, }) case data := <-stderr: d := string(data) transport.WriteMessage(conn, &transport.ProcessPayload{ ProcessID: process.ID(), Source: &stderrSource, Data: &d, }) default: return } } }
func (c *checker) wait(logger lager.Logger, proc garden.Process) (int, error) { logger = logger.Session("wait") logger.Debug("starting") defer logger.Debug("finished") var exitCode int err := retryOnFail(c.retryInterval, func(attempt uint) (waitErr error) { exitCode, waitErr = proc.Wait() if waitErr != nil { logger.Error("failed", waitErr, lager.Data{"attempt": attempt}) return waitErr } logger.Debug("succeeded", lager.Data{"attempt": attempt}) return nil }) return exitCode, err }
func (conn RetryableConnection) Run(handle string, processSpec garden.ProcessSpec, processIO garden.ProcessIO) (garden.Process, error) { var innerProcess garden.Process err := conn.retry(func() error { var err error innerProcess, err = conn.Connection.Run(handle, processSpec, processIO) return err }) if err != nil { return nil, err } return &retryableProcess{ Process: innerProcess, rehydrate: func() (garden.Process, error) { return conn.Attach(handle, innerProcess.ID(), processIO) }, }, nil }
func (s *GardenServer) streamInput(decoder *json.Decoder, in *io.PipeWriter, process garden.Process, connCloseCh chan struct{}) { for { var payload transport.ProcessPayload err := decoder.Decode(&payload) if err != nil { close(connCloseCh) in.CloseWithError(errors.New("Connection closed")) return } switch { case payload.TTY != nil: process.SetTTY(*payload.TTY) case payload.Source != nil: if payload.Data == nil { in.Close() return } else { _, err := in.Write([]byte(*payload.Data)) if err != nil { return } } case payload.Signal != nil: s.logger.Info("stream-input-process-signal", lager.Data{"payload": payload}) switch *payload.Signal { case garden.SignalKill: err = process.Signal(garden.SignalKill) if err != nil { s.logger.Error("stream-input-process-signal-kill-failed", err, lager.Data{"payload": payload}) } case garden.SignalTerminate: err = process.Signal(garden.SignalTerminate) if err != nil { s.logger.Error("stream-input-process-signal-terminate-failed", err, lager.Data{"payload": payload}) } default: s.logger.Error("stream-input-unknown-process-payload-signal", nil, lager.Data{"payload": payload}) in.Close() return } default: s.logger.Error("stream-input-unknown-process-payload", nil, lager.Data{"payload": payload}) in.Close() return } } }
func (s *GardenServer) streamProcess(logger lager.Logger, conn net.Conn, process garden.Process, stdinPipe *io.PipeWriter) { 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 } } }
Describe("Running processes", func() { It("runs the process and returns its exit code", func() { cmd := exec.Command("bash", "-c", "exit 42") process, err := processTracker.Run(555, cmd, garden.ProcessIO{}, nil, signaller) Expect(err).NotTo(HaveOccurred()) status, err := process.Wait() Expect(err).ToNot(HaveOccurred()) Expect(status).To(Equal(42)) }) Describe("signalling a running process", func() { var ( process garden.Process stdout *gbytes.Buffer cmd *exec.Cmd ) JustBeforeEach(func() { var err error cmd = exec.Command(testPrintBin) stdout = gbytes.NewBuffer() process, err = processTracker.Run( 2, cmd, garden.ProcessIO{ Stdout: io.MultiWriter(stdout, GinkgoWriter), Stderr: GinkgoWriter, }, nil, signaller) Expect(err).NotTo(HaveOccurred())
func (resource *resource) runScript( path string, args []string, input interface{}, output interface{}, logDest io.Writer, inputSource ArtifactSource, inputDestination ArtifactDestination, recoverable bool, ) ifrit.Runner { return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error { request, err := json.Marshal(input) if err != nil { return err } if recoverable { result, err := resource.container.Property(resourceResultPropertyName) if err == nil { return json.Unmarshal([]byte(result), &output) } } stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) processIO := garden.ProcessIO{ Stdin: bytes.NewBuffer(request), Stdout: stdout, } if logDest != nil { processIO.Stderr = logDest } else { processIO.Stderr = stderr } var process garden.Process var processIDProp string if recoverable { processIDProp, err = resource.container.Property(resourceProcessIDPropertyName) if err != nil { processIDProp = "" } } if processIDProp != "" { var processID uint32 _, err = fmt.Sscanf(processIDProp, "%d", &processID) if err != nil { return err } process, err = resource.container.Attach(processID, processIO) if err != nil { return err } } else { if inputSource != nil { err := inputSource.StreamTo(inputDestination) if err != nil { return err } } process, err = resource.container.Run(garden.ProcessSpec{ Path: path, Args: args, User: "******", }, processIO) if err != nil { return err } if recoverable { processIDValue := fmt.Sprintf("%d", process.ID()) err := resource.container.SetProperty(resourceProcessIDPropertyName, processIDValue) if err != nil { return err } } } close(ready) statusCh := make(chan int, 1) errCh := make(chan error, 1) go func() { status, err := process.Wait() if err != nil { errCh <- err } else { statusCh <- status } }() select { case status := <-statusCh: if status != 0 { return ErrResourceScriptFailed{ Path: path, Args: args, ExitStatus: status, Stderr: stderr.String(), } } if recoverable { err := resource.container.SetProperty(resourceResultPropertyName, stdout.String()) if err != nil { return err } } return json.Unmarshal(stdout.Bytes(), output) case err := <-errCh: return err case <-signals: resource.container.Stop(false) return ErrAborted } }) }
Expect(err).NotTo(HaveOccurred()) cmd := exec.Command("bash", "-c", "pwd") cmd.Dir = tmpDir stdout := gbytes.NewBuffer() _, err = processTracker.Run("556", cmd, garden.ProcessIO{Stdout: stdout}, nil) Expect(err).NotTo(HaveOccurred()) Eventually(stdout).Should(gbytes.Say(tmpDir)) }) Describe("signalling a running process", func() { var ( process garden.Process stdout *gbytes.Buffer cmd *exec.Cmd ) JustBeforeEach(func() { var err error cmd = exec.Command("sh", "-c", ` trap "echo 'terminated'; exit 42" TERM echo "trapping" sleep 100 & wait `) stdout = gbytes.NewBuffer() process, err = processTracker.Run(
calledHandle, calledSpec := innerConnection.StreamOutArgsForCall(0) Ω(calledHandle).Should(Equal(handle)) Ω(calledSpec.Path).Should(Equal("/etc/passwd")) Ω(calledSpec.User).Should(Equal("admin")) }) It("returns the reader", func() { Ω(gotReader).Should(Equal(gbytes.NewBuffer())) }) }) }) Describe("Attach", func() { var ( fakeProcess *gfakes.FakeProcess process garden.Process ) processIO := garden.ProcessIO{ Stdout: gbytes.NewBuffer(), } BeforeEach(func() { fakeProcess = new(gfakes.FakeProcess) fakeProcess.IDReturns(6) }) itRetries(func() error { var err error process, err = conn.Attach("la-contineur", 6, processIO) return err
calledHandle, calledSpec := innerConnection.StreamOutArgsForCall(0) Expect(calledHandle).To(Equal(handle)) Expect(calledSpec.Path).To(Equal("/etc/passwd")) Expect(calledSpec.User).To(Equal("admin")) }) It("returns the reader", func() { Expect(gotReader).To(Equal(gbytes.NewBuffer())) }) }) }) Describe("Attach", func() { var ( fakeProcess *gfakes.FakeProcess process garden.Process ) processIO := garden.ProcessIO{ Stdout: gbytes.NewBuffer(), } BeforeEach(func() { fakeProcess = new(gfakes.FakeProcess) fakeProcess.IDReturns("process-id") }) itRetries(func() error { var err error process, err = conn.Attach("la-contineur", "process-id", processIO) return err