docker.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package RunCode_service
  2. import (
  3. "bytes"
  4. context2 "context"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "os/exec"
  12. "regexp"
  13. "strings"
  14. "time"
  15. )
  16. var TimeoutError = errors.New("execute timeout")
  17. var Success = errors.New("success")
  18. var Exited = errors.New("exited")
  19. var DockerRunner dockerRunner
  20. func InitDockerRunner(config string) error {
  21. log.SetPrefix("[cloud-run-code]")
  22. dockerPath, err := exec.LookPath("docker")
  23. //dockerPath := "/usr/bin/docker"
  24. println("dockerPath:", dockerPath)
  25. content, err := ioutil.ReadFile(config)
  26. if err != nil {
  27. return err
  28. }
  29. err = json.Unmarshal(content, &DockerRunner)
  30. if err != nil {
  31. return err
  32. }
  33. DockerRunner.DockerPath = dockerPath
  34. if err := DockerRunner.InstallImages(); err != nil {
  35. return err
  36. }
  37. return nil
  38. }
  39. type DRunner struct {
  40. Ext string `json:"ext"`
  41. Filename string `json:"filename"`
  42. Image string `json:"image"`
  43. Cmd string `json:"cmd"`
  44. Build string `json:"build"`
  45. }
  46. func (dr *DRunner) InstallImage() ([]byte, error) {
  47. cmd := exec.Command("docker", "pull", dr.Image)
  48. var out bytes.Buffer
  49. var stderr bytes.Buffer
  50. cmd.Stdout = &out
  51. cmd.Stderr = &stderr
  52. if err := cmd.Run(); err != nil {
  53. return stderr.Bytes(), err
  54. }
  55. return out.Bytes(), nil
  56. }
  57. type dockerRunner struct {
  58. Timeout int `json:"timeout"`
  59. TmpPath string `json:"tmp_path"`
  60. DockerBase string `json:"docker_base"`
  61. Runners map[string]*DRunner `json:"docker_runner"`
  62. DockerPath string
  63. }
  64. func (dr dockerRunner) RunnerExists(runner string) bool {
  65. if _, ok := dr.Runners[runner]; ok {
  66. return true
  67. }
  68. return false
  69. }
  70. func (dr dockerRunner) getDockerImages() (map[string]interface{}, error) {
  71. cmd := exec.Command("docker", "images")
  72. var out bytes.Buffer
  73. cmd.Stdout = &out
  74. if err := cmd.Run(); err != nil {
  75. return nil, err
  76. }
  77. lines := strings.Split(out.String(), "\n")
  78. var images = make(map[string]interface{})
  79. regexpC, _ := regexp.Compile("[\\w+\\-\\/\\_\\.]+")
  80. for i, line := range lines {
  81. if i == 0 {
  82. continue
  83. }
  84. columns := regexpC.FindAllStringSubmatch(line, -1)
  85. if len(columns) == 0 {
  86. continue
  87. }
  88. images[columns[0][0]+":"+columns[1][0]] = struct{}{}
  89. }
  90. return images, nil
  91. }
  92. func (dr dockerRunner) InstallImages() error {
  93. if images, err := dr.getDockerImages(); err != nil {
  94. return err
  95. } else {
  96. for lang, conf := range dr.Runners {
  97. if _, ok := images[conf.Image]; ok {
  98. log.Println(lang + " dependent image " + conf.Image + " exists skip.")
  99. } else {
  100. //pull image
  101. log.Println(lang + " pull docker image " + conf.Image)
  102. if res, err := conf.InstallImage(); err != nil {
  103. log.Println("pull image " + conf.Image + " failure: " + string(res))
  104. } else {
  105. log.Println("pull docker image " + conf.Image + " success. ")
  106. }
  107. }
  108. }
  109. log.Println("tip:If the installation fails, please use the \"docker pull\" command or run this script again after finding out the reason.")
  110. return nil
  111. }
  112. }
  113. func (dr dockerRunner) Exec(ctx context2.Context, runnerName string, code string) (result []byte, err error) {
  114. runner, _ := dr.Runners[runnerName]
  115. tmpFileName := fmt.Sprintf("%d.%s_", time.Now().UnixMilli(), runner.Ext)
  116. var tmpFile *os.File
  117. if tmpFile, err = os.CreateTemp("", tmpFileName); err != nil {
  118. return nil, err
  119. }
  120. _, _ = tmpFile.WriteString(code)
  121. log.Println("tmp file path: " + tmpFile.Name())
  122. defer func() {
  123. //println("删除:", tmpFile.Name())
  124. //_ = syscall.Unlink(tmpFile.Name())
  125. }()
  126. cmdStr := strings.Clone(dr.DockerBase)
  127. cmdStr = strings.Replace(cmdStr, "{tmp_file}", tmpFile.Name(), 1)
  128. cmdStr = strings.Replace(cmdStr, "{runner_filename}", runner.Filename, 1)
  129. cmdStr = strings.Replace(cmdStr, "{image}", runner.Image, 1)
  130. args := strings.Split(cmdStr, " ")
  131. args = append(args, runner.Cmd)
  132. fmt.Println("args:", args)
  133. result, err = dr.ExecDocker(ctx, args)
  134. return
  135. }
  136. func (dr dockerRunner) ExecDocker(ctx context2.Context, args []string) (result []byte, err error) {
  137. cmd := exec.CommandContext(ctx, dr.DockerPath, args...)
  138. var out bytes.Buffer
  139. var stderr bytes.Buffer
  140. cmd.Stdout = &out
  141. cmd.Stderr = &stderr
  142. if err = cmd.Start(); err != nil {
  143. return
  144. }
  145. chErr := make(chan error)
  146. go func() {
  147. for {
  148. select {
  149. case <-ctx.Done():
  150. chErr <- TimeoutError
  151. _ = cmd.Process.Kill()
  152. return
  153. default:
  154. if state, err := cmd.Process.Wait(); err != nil {
  155. chErr <- err
  156. return
  157. } else {
  158. if state.Success() {
  159. chErr <- Success
  160. return
  161. }
  162. if state.Exited() {
  163. chErr <- Exited
  164. return
  165. }
  166. }
  167. }
  168. }
  169. }()
  170. err = <-chErr
  171. if err == Success || err == Exited {
  172. err = nil
  173. }
  174. if stderr.Len() > 0 {
  175. return stderr.Bytes(), err
  176. }
  177. return out.Bytes(), err
  178. }
  179. func (dr dockerRunner) Build(ctx context2.Context, namerand int64, runnerName string, code string) (result []byte, err error) {
  180. runner, _ := dr.Runners[runnerName]
  181. tmpFileName := fmt.Sprintf("%d.%s_", namerand, runner.Ext)
  182. var tmpFile *os.File
  183. if tmpFile, err = os.CreateTemp("", tmpFileName); err != nil {
  184. return nil, err
  185. }
  186. _, _ = tmpFile.WriteString(code)
  187. log.Println("tmp file path: " + tmpFile.Name())
  188. defer func() {
  189. //println("删除:", tmpFile.Name())
  190. //_ = syscall.Unlink(tmpFile.Name())
  191. }()
  192. cmdStr := strings.Clone(dr.DockerBase)
  193. cmdStr = strings.Replace(cmdStr, "{tmp_file}", tmpFile.Name(), 1)
  194. cmdStr = strings.Replace(cmdStr, "{runner_filename}", runner.Filename, 1)
  195. cmdStr = strings.Replace(cmdStr, "{image}", runner.Image, 1)
  196. args := strings.Split(cmdStr, " ")
  197. args = append(args, strings.Replace(runner.Build, "{name}", fmt.Sprintf("%d", namerand), 1))
  198. fmt.Println("args:", args)
  199. result, err = dr.ExecDocker_Build(ctx, args)
  200. return
  201. }
  202. func (dr dockerRunner) ExecDocker_Build(ctx context2.Context, args []string) (result []byte, err error) {
  203. cmd := exec.CommandContext(ctx, dr.DockerPath, args...)
  204. var out bytes.Buffer
  205. var stderr bytes.Buffer
  206. cmd.Stdout = &out
  207. cmd.Stderr = &stderr
  208. if err = cmd.Start(); err != nil {
  209. return
  210. }
  211. chErr := make(chan error)
  212. go func() {
  213. for {
  214. select {
  215. case <-ctx.Done():
  216. chErr <- TimeoutError
  217. _ = cmd.Process.Kill()
  218. return
  219. default:
  220. if state, err := cmd.Process.Wait(); err != nil {
  221. chErr <- err
  222. return
  223. } else {
  224. if state.Success() {
  225. chErr <- Success
  226. return
  227. }
  228. if state.Exited() {
  229. chErr <- Exited
  230. return
  231. }
  232. }
  233. }
  234. }
  235. }()
  236. err = <-chErr
  237. if err == Success || err == Exited || err == TimeoutError {
  238. err = nil
  239. }
  240. if stderr.Len() > 0 {
  241. return stderr.Bytes(), err
  242. }
  243. return out.Bytes(), err
  244. }