Example #1
0
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
		}
	}
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
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
		}
	}
}
Example #5
0
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())
Example #7
0
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