coze-studio/backend/infra/impl/coderunner/sandbox/runner.go

104 lines
2.7 KiB
Go

package sandbox
import (
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/coze-dev/coze-studio/backend/infra/contract/coderunner"
"github.com/coze-dev/coze-studio/backend/pkg/goutil"
"github.com/coze-dev/coze-studio/backend/pkg/logs"
)
func NewRunner(config *Config) coderunner.Runner {
return &runner{
pyPath: goutil.GetPython3Path(),
scriptPath: goutil.GetPythonFilePath("sandbox.py"),
config: config,
}
}
type Config struct {
AllowEnv []string `json:"allow_env,omitempty"`
AllowRead []string `json:"allow_read,omitempty"`
AllowWrite []string `json:"allow_write,omitempty"`
AllowNet []string `json:"allow_net,omitempty"`
AllowRun []string `json:"allow_run,omitempty"`
AllowFFI []string `json:"allow_ffi,omitempty"`
NodeModulesDir string `json:"node_modules_dir,omitempty"`
TimeoutSeconds float64 `json:"timeout_seconds,omitempty"`
MemoryLimitMB int64 `json:"memory_limit_mb,omitempty"`
}
type runner struct {
pyPath, scriptPath string
config *Config
}
func (runner *runner) Run(ctx context.Context, request *coderunner.RunRequest) (*coderunner.RunResponse, error) {
if request.Language == coderunner.JavaScript {
return nil, fmt.Errorf("js not supported yet")
}
b, err := json.Marshal(req{
Config: runner.config,
Code: request.Code,
Params: request.Params,
})
if err != nil {
return nil, err
}
pr, pw, err := os.Pipe()
if err != nil {
return nil, err
}
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
if _, err = pw.Write(b); err != nil {
return nil, err
}
if err = pw.Close(); err != nil {
return nil, err
}
cmd := exec.Command(runner.pyPath, runner.scriptPath)
cmd.ExtraFiles = []*os.File{w, pr}
if err = cmd.Start(); err != nil {
return nil, err
}
if err = w.Close(); err != nil {
return nil, err
}
result := &resp{}
d := json.NewDecoder(r)
d.UseNumber()
if err = d.Decode(result); err != nil {
return nil, err
}
if err = cmd.Wait(); err != nil {
return nil, err
}
logs.CtxDebugf(ctx, "resp=%v\n", result)
if result.Status != "success" {
return nil, fmt.Errorf("exec failed, stdout=%s, stderr=%s, sandbox_err=%s", result.Stdout, result.Stderr, result.SandboxError)
}
return &coderunner.RunResponse{Result: result.Result}, nil
}
type req struct {
Config *Config `json:"config"`
Code string `json:"code"`
Params map[string]any `json:"params"`
}
type resp struct {
Result map[string]any `json:"result"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
Status string `json:"status"`
ExecutionTime float64 `json:"execution_time"`
SandboxError string `json:"sandbox_error"`
}