123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- package RunCode_service
- import (
- "bytes"
- context2 "context"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "regexp"
- "strings"
- "time"
- )
- var TimeoutError = errors.New("execute timeout")
- var Success = errors.New("success")
- var Exited = errors.New("exited")
- var DockerRunner dockerRunner
- func InitDockerRunner(config string) error {
- log.SetPrefix("[cloud-run-code]")
- dockerPath, err := exec.LookPath("docker")
- //dockerPath := "/usr/bin/docker"
- println("dockerPath:", dockerPath)
- content, err := ioutil.ReadFile(config)
- if err != nil {
- return err
- }
- err = json.Unmarshal(content, &DockerRunner)
- if err != nil {
- return err
- }
- DockerRunner.DockerPath = dockerPath
- if err := DockerRunner.InstallImages(); err != nil {
- return err
- }
- return nil
- }
- type DRunner struct {
- Ext string `json:"ext"`
- Filename string `json:"filename"`
- Image string `json:"image"`
- Cmd string `json:"cmd"`
- Build string `json:"build"`
- }
- func (dr *DRunner) InstallImage() ([]byte, error) {
- cmd := exec.Command("docker", "pull", dr.Image)
- var out bytes.Buffer
- var stderr bytes.Buffer
- cmd.Stdout = &out
- cmd.Stderr = &stderr
- if err := cmd.Run(); err != nil {
- return stderr.Bytes(), err
- }
- return out.Bytes(), nil
- }
- type dockerRunner struct {
- Timeout int `json:"timeout"`
- TmpPath string `json:"tmp_path"`
- DockerBase string `json:"docker_base"`
- Runners map[string]*DRunner `json:"docker_runner"`
- DockerPath string
- }
- func (dr dockerRunner) RunnerExists(runner string) bool {
- if _, ok := dr.Runners[runner]; ok {
- return true
- }
- return false
- }
- func (dr dockerRunner) getDockerImages() (map[string]interface{}, error) {
- cmd := exec.Command("docker", "images")
- var out bytes.Buffer
- cmd.Stdout = &out
- if err := cmd.Run(); err != nil {
- return nil, err
- }
- lines := strings.Split(out.String(), "\n")
- var images = make(map[string]interface{})
- regexpC, _ := regexp.Compile("[\\w+\\-\\/\\_\\.]+")
- for i, line := range lines {
- if i == 0 {
- continue
- }
- columns := regexpC.FindAllStringSubmatch(line, -1)
- if len(columns) == 0 {
- continue
- }
- images[columns[0][0]+":"+columns[1][0]] = struct{}{}
- }
- return images, nil
- }
- func (dr dockerRunner) InstallImages() error {
- if images, err := dr.getDockerImages(); err != nil {
- return err
- } else {
- for lang, conf := range dr.Runners {
- if _, ok := images[conf.Image]; ok {
- log.Println(lang + " dependent image " + conf.Image + " exists skip.")
- } else {
- //pull image
- log.Println(lang + " pull docker image " + conf.Image)
- if res, err := conf.InstallImage(); err != nil {
- log.Println("pull image " + conf.Image + " failure: " + string(res))
- } else {
- log.Println("pull docker image " + conf.Image + " success. ")
- }
- }
- }
- log.Println("tip:If the installation fails, please use the \"docker pull\" command or run this script again after finding out the reason.")
- return nil
- }
- }
- func (dr dockerRunner) Exec(ctx context2.Context, runnerName string, code string) (result []byte, err error) {
- runner, _ := dr.Runners[runnerName]
- tmpFileName := fmt.Sprintf("%d.%s_", time.Now().UnixMilli(), runner.Ext)
- var tmpFile *os.File
- if tmpFile, err = os.CreateTemp("", tmpFileName); err != nil {
- return nil, err
- }
- _, _ = tmpFile.WriteString(code)
- log.Println("tmp file path: " + tmpFile.Name())
- defer func() {
- //println("删除:", tmpFile.Name())
- //_ = syscall.Unlink(tmpFile.Name())
- }()
- cmdStr := strings.Clone(dr.DockerBase)
- cmdStr = strings.Replace(cmdStr, "{tmp_file}", tmpFile.Name(), 1)
- cmdStr = strings.Replace(cmdStr, "{runner_filename}", runner.Filename, 1)
- cmdStr = strings.Replace(cmdStr, "{image}", runner.Image, 1)
- args := strings.Split(cmdStr, " ")
- args = append(args, runner.Cmd)
- fmt.Println("args:", args)
- result, err = dr.ExecDocker(ctx, args)
- return
- }
- func (dr dockerRunner) ExecDocker(ctx context2.Context, args []string) (result []byte, err error) {
- cmd := exec.CommandContext(ctx, dr.DockerPath, args...)
- var out bytes.Buffer
- var stderr bytes.Buffer
- cmd.Stdout = &out
- cmd.Stderr = &stderr
- if err = cmd.Start(); err != nil {
- return
- }
- chErr := make(chan error)
- go func() {
- for {
- select {
- case <-ctx.Done():
- chErr <- TimeoutError
- _ = cmd.Process.Kill()
- return
- default:
- if state, err := cmd.Process.Wait(); err != nil {
- chErr <- err
- return
- } else {
- if state.Success() {
- chErr <- Success
- return
- }
- if state.Exited() {
- chErr <- Exited
- return
- }
- }
- }
- }
- }()
- err = <-chErr
- if err == Success || err == Exited {
- err = nil
- }
- if stderr.Len() > 0 {
- return stderr.Bytes(), err
- }
- return out.Bytes(), err
- }
- func (dr dockerRunner) Build(ctx context2.Context, namerand int64, runnerName string, code string) (result []byte, err error) {
- runner, _ := dr.Runners[runnerName]
- tmpFileName := fmt.Sprintf("%d.%s_", namerand, runner.Ext)
- var tmpFile *os.File
- if tmpFile, err = os.CreateTemp("", tmpFileName); err != nil {
- return nil, err
- }
- _, _ = tmpFile.WriteString(code)
- log.Println("tmp file path: " + tmpFile.Name())
- defer func() {
- //println("删除:", tmpFile.Name())
- //_ = syscall.Unlink(tmpFile.Name())
- }()
- cmdStr := strings.Clone(dr.DockerBase)
- cmdStr = strings.Replace(cmdStr, "{tmp_file}", tmpFile.Name(), 1)
- cmdStr = strings.Replace(cmdStr, "{runner_filename}", runner.Filename, 1)
- cmdStr = strings.Replace(cmdStr, "{image}", runner.Image, 1)
- args := strings.Split(cmdStr, " ")
- args = append(args, strings.Replace(runner.Build, "{name}", fmt.Sprintf("%d", namerand), 1))
- fmt.Println("args:", args)
- result, err = dr.ExecDocker_Build(ctx, args)
- return
- }
- func (dr dockerRunner) ExecDocker_Build(ctx context2.Context, args []string) (result []byte, err error) {
- cmd := exec.CommandContext(ctx, dr.DockerPath, args...)
- var out bytes.Buffer
- var stderr bytes.Buffer
- cmd.Stdout = &out
- cmd.Stderr = &stderr
- if err = cmd.Start(); err != nil {
- return
- }
- chErr := make(chan error)
- go func() {
- for {
- select {
- case <-ctx.Done():
- chErr <- TimeoutError
- _ = cmd.Process.Kill()
- return
- default:
- if state, err := cmd.Process.Wait(); err != nil {
- chErr <- err
- return
- } else {
- if state.Success() {
- chErr <- Success
- return
- }
- if state.Exited() {
- chErr <- Exited
- return
- }
- }
- }
- }
- }()
- err = <-chErr
- if err == Success || err == Exited || err == TimeoutError {
- err = nil
- }
- if stderr.Len() > 0 {
- return stderr.Bytes(), err
- }
- return out.Bytes(), err
- }
|