docker.go 6.5 KB

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