Skip to content

Commit

Permalink
Introduce ExecutableNotFoundError
Browse files Browse the repository at this point in the history
In the event that an executable to be run by runc doesn't exist, the
error returned has a concrete type.

[#110522896]

Signed-off-by: Claudia Beresford <[email protected]>
Signed-off-by: Julia Nedialkova <[email protected]>
  • Loading branch information
Tom Godkin authored and yulianedyalkova committed Feb 13, 2018
1 parent 2e6079f commit 59cf846
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 4 deletions.
40 changes: 40 additions & 0 deletions gqt/run_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,46 @@ var _ = Describe("Run", func() {
Eventually(status).Should(Receive(BeEquivalentTo(42)))
})
})

Describe("Errors", func() {
Context("when trying to run an executable which does not exist", func() {
var (
runErr error
binaryPath string
)

JustBeforeEach(func() {
client = runner.Start(config)

container, err := client.Create(garden.ContainerSpec{})
Expect(err).NotTo(HaveOccurred())

_, runErr = container.Run(garden.ProcessSpec{
Path: binaryPath,
}, garden.ProcessIO{})
})

Context("when the executable is a fully qualified path", func() {
BeforeEach(func() {
binaryPath = "/bin/fake"
})

It("returns a useful error type", func() {
Expect(runErr).To(BeAssignableToTypeOf(garden.ExecutableNotFoundError{}))
})
})

Context("when the executable should be somewhere on the $PATH", func() {
BeforeEach(func() {
binaryPath = "fake-path"
})

It("returns a useful error type", func() {
Expect(runErr).To(BeAssignableToTypeOf(garden.ExecutableNotFoundError{}))
})
})
})
})
})

var _ = Describe("Attach", func() {
Expand Down
8 changes: 6 additions & 2 deletions logging/logconverter.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ func (e WrappedError) Error() string {
}

func WrapWithErrorFromLastLogLine(tag string, originalError error, logfileContent []byte) error {
msg := lastNonEmptyLine(logfileContent)
return WrappedError{Underlying: originalError, tag: tag, lastRuncLogLine: MsgFromLastLogLine(logfileContent)}
}

func MsgFromLastLogLine(logFileContent []byte) string {
msg := lastNonEmptyLine(logFileContent)
var line logLine
if err := json.Unmarshal(msg, &line); err == nil {
msg = []byte(line.Msg)
}

return WrappedError{Underlying: originalError, tag: tag, lastRuncLogLine: string(msg)}
return string(msg)
}

func lastNonEmptyLine(content []byte) []byte {
Expand Down
19 changes: 17 additions & 2 deletions rundmc/execrunner/dadoo/execrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"sync"
"syscall"
Expand Down Expand Up @@ -136,9 +137,16 @@ func (d *ExecRunner) Run(

defer func() {
lastLogLine := <-doneReadingRuncLogs
if theErr != nil {
theErr = logging.WrapWithErrorFromLastLogLine("runc exec", theErr, lastLogLine)
if theErr == nil {
return
}

if isNoSuchExecutable(lastLogLine) {
theErr = garden.ExecutableNotFoundError{Message: logging.MsgFromLastLogLine(lastLogLine)}
return
}

theErr = logging.WrapWithErrorFromLastLogLine("runc exec", theErr, lastLogLine)
}()

log.Info("read-exit-fd")
Expand All @@ -155,6 +163,13 @@ func (d *ExecRunner) Run(
return process, nil
}

func isNoSuchExecutable(logLine []byte) bool {
noSuchFile := regexp.MustCompile(`starting container process caused \\"exec: .*: stat .*: no such file or directory`)
executableNotFound := regexp.MustCompile(`starting container process caused \\"exec: .*: executable file not found in \$PATH`)

return noSuchFile.Match(logLine) || executableNotFound.Match(logLine)
}

func buildDadooCommand(tty bool, dadooPath, dadooRunMode, runcPath, processID, processPath, sandboxHandle string, extraFiles []*os.File, stdin io.Reader) *exec.Cmd {
dadooArgs := []string{}
if tty {
Expand Down
32 changes: 32 additions & 0 deletions rundmc/execrunner/dadoo/execrunner_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,38 @@ var _ = Describe("Dadoo ExecRunner", func() {
close(done)
}, 10.0)
})

Context("when runc tries to exec a non-existent binary", func() {
Context("when the binary is not on the $PATH", func() {
BeforeEach(func() {
dadooWritesLogs = `{"time":"2016-03-02T13:56:38Z", "level":"fatal", "msg":"starting container process caused \"exec: \\\"potato\\\": executable file not found in $PATH\""}`
})

It("Returns a garden.RequestedBinaryNotFoundError error", func() {
_, err := runner.Run(log, processID, processPath, "some-handle", bundlePath, 123, 456, defaultProcessIO(), false, nil, nil)

message := `starting container process caused "exec: \"potato\": executable file not found in $PATH"`
expectedErr := garden.ExecutableNotFoundError{Message: message}

Expect(err).To(MatchError(expectedErr))
})
})

Context("when a fully qualified binary does not exist", func() {
BeforeEach(func() {
dadooWritesLogs = `{"time":"2016-03-02T13:56:38Z", "level":"fatal", "msg":"starting container process caused \"exec: \\\"/bin/potato\\\": stat /bin/potato: no such file or directory\""}`
})

It("Returns a garden.RequestedBinaryNotFoundError error", func() {
_, err := runner.Run(log, processID, processPath, "some-handle", bundlePath, 123, 456, defaultProcessIO(), false, nil, nil)

message := `starting container process caused "exec: \"/bin/potato\": stat /bin/potato: no such file or directory"`
expectedErr := garden.ExecutableNotFoundError{Message: message}

Expect(err).To(MatchError(expectedErr))
})
})
})
})

Context("when dadoo panics before reporting runc's exit code", func() {
Expand Down

0 comments on commit 59cf846

Please sign in to comment.