codapi/internal/engine/exec.go

68 lines
1.8 KiB
Go
Raw Normal View History

2023-11-24 23:02:45 +00:00
package engine
import (
"context"
"io"
"os/exec"
"strings"
"time"
2023-12-04 18:40:41 +00:00
"github.com/nalgeon/codapi/internal/execy"
"github.com/nalgeon/codapi/internal/logx"
2023-11-24 23:02:45 +00:00
)
// A Program is an executable program.
type Program struct {
timeout time.Duration
nOutput int64
}
// NewProgram creates a new program.
func NewProgram(timeoutSec int, nOutput int64) *Program {
return &Program{
timeout: time.Duration(timeoutSec) * time.Second,
nOutput: nOutput,
}
}
// Run starts the program and waits for it to complete (or timeout).
func (p *Program) Run(id, name string, arg ...string) (stdout string, stderr string, err error) {
return p.RunStdin(nil, id, name, arg...)
}
// RunStdin starts the program with data from stdin
// and waits for it to complete (or timeout).
func (p *Program) RunStdin(stdin io.Reader, id, name string, arg ...string) (stdout string, stderr string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
defer cancel()
var cmdout, cmderr strings.Builder
cmd := exec.CommandContext(ctx, name, arg...)
cmd.Cancel = func() error {
err := cmd.Process.Kill()
logx.Debug("%s: execution timeout, killed process=%d, err=%v", id, cmd.Process.Pid, err)
2024-04-25 15:19:19 +00:00
if arg[0] == actionRun {
// we have to "docker kill" the container here, because the process
// inside the container is not related to the "docker run" process,
// and will hang forever after the "docker run" process is killed
go func() {
err = dockerKill(id)
if err == nil {
logx.Debug("%s: docker kill ok", id)
} else {
logx.Log("%s: docker kill failed: %v", id, err)
}
}()
}
2023-11-24 23:02:45 +00:00
return err
}
cmd.Stdin = stdin
cmd.Stdout = LimitWriter(&cmdout, p.nOutput)
cmd.Stderr = LimitWriter(&cmderr, p.nOutput)
err = execy.Run(cmd)
stdout = strings.TrimSpace(cmdout.String())
stderr = strings.TrimSpace(cmderr.String())
2023-11-24 23:02:45 +00:00
return
}