120 lines
3.3 KiB
Go
120 lines
3.3 KiB
Go
/*
|
|
* Copyright 2025 coze-dev Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
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"`
|
|
}
|