Add support for timeout

Signed-off-by: Mrunal Patel <mpatel@redhat.com>
This commit is contained in:
Mrunal Patel 2016-11-18 14:48:04 -08:00
parent 5c1adcbf6a
commit 78ecdcd298
2 changed files with 84 additions and 14 deletions

View file

@ -148,28 +148,98 @@ func (r *Runtime) StartContainer(c *Container) error {
return nil return nil
} }
// ExecSyncResponse is returned from ExecSync.
type ExecSyncResponse struct {
Stdout []byte
Stderr []byte
ExitCode int32
}
// ExecSync execs a command in a container and returns it's stdout, stderr and return code. // ExecSync execs a command in a container and returns it's stdout, stderr and return code.
func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (stdout []byte, stderr []byte, exitCode int32, err error) { func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp *ExecSyncResponse, err error) {
args := []string{"exec", c.name} args := []string{"exec", c.name}
args = append(args, command...) args = append(args, command...)
cmd := exec.Command(r.Path(), args...) cmd := exec.Command(r.Path(), args...)
logrus.Debugf("Command: +v\n", cmd)
var stdoutBuf, stderrBuf bytes.Buffer var stdoutBuf, stderrBuf bytes.Buffer
cmd.Stdout = &stdoutBuf cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf cmd.Stderr = &stderrBuf
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
return stdoutBuf.Bytes(), stderrBuf.Bytes(), -1, err return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: -1,
}, err
} }
if timeout > 0 {
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-time.After(time.Duration(timeout) * time.Second):
err = unix.Kill(cmd.Process.Pid, syscall.SIGKILL)
if err != nil && err != syscall.ESRCH {
return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: -1,
}, fmt.Errorf("failed to kill process on timeout: %v", err)
}
return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: -1,
}, fmt.Errorf("command timed out")
case err = <-done:
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: int32(status.ExitStatus()),
}, err
}
} else {
return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: -1,
}, err
}
}
}
} else {
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok { if exitErr, ok := err.(*exec.ExitError); ok {
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
return stdoutBuf.Bytes(), stderrBuf.Bytes(), int32(status.ExitStatus()), err return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: int32(status.ExitStatus()),
}, err
}
} else {
return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: -1,
}, err
}
} }
} }
}
return stdoutBuf.Bytes(), stderrBuf.Bytes(), 0, nil return &ExecSyncResponse{
Stdout: stdoutBuf.Bytes(),
Stderr: stderrBuf.Bytes(),
ExitCode: 0,
}, nil
} }
// StopContainer stops a container. // StopContainer stops a container.

View file

@ -597,13 +597,13 @@ func (s *Server) ExecSync(ctx context.Context, req *pb.ExecSyncRequest) (*pb.Exe
return nil, fmt.Errorf("exec command cannot be empty") return nil, fmt.Errorf("exec command cannot be empty")
} }
out, errout, rc, err := s.runtime.ExecSync(c, cmd, req.GetTimeout()) execResp, err := s.runtime.ExecSync(c, cmd, req.GetTimeout())
resp := &pb.ExecSyncResponse{ resp := &pb.ExecSyncResponse{
Stdout: out, Stdout: execResp.Stdout,
Stderr: errout, Stderr: execResp.Stderr,
ExitCode: &rc, ExitCode: &execResp.ExitCode,
} }
logrus.Debugf("ExecSyncResponse: %+v", resp) logrus.Debugf("ExecSyncResponse: %+v", resp)
return resp, err return resp, err
} }