2
0

runner.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package worker
  2. import (
  3. "errors"
  4. "os"
  5. "os/exec"
  6. "strings"
  7. "sync"
  8. "syscall"
  9. "time"
  10. "golang.org/x/sys/unix"
  11. )
  12. // runner is to run os commands giving command line, env and log file
  13. // it's an alternative to python-sh or go-sh
  14. var errProcessNotStarted = errors.New("Process Not Started")
  15. type cmdJob struct {
  16. sync.Mutex
  17. cmd *exec.Cmd
  18. workingDir string
  19. env map[string]string
  20. logFile *os.File
  21. finished chan empty
  22. provider mirrorProvider
  23. retErr error
  24. }
  25. func newCmdJob(provider mirrorProvider, cmdAndArgs []string, workingDir string, env map[string]string) *cmdJob {
  26. var cmd *exec.Cmd
  27. if provider.Cgroup() != nil {
  28. c := "cgexec"
  29. args := []string{"-g", provider.Cgroup().Cgroup()}
  30. args = append(args, cmdAndArgs...)
  31. cmd = exec.Command(c, args...)
  32. } else {
  33. if len(cmdAndArgs) == 1 {
  34. cmd = exec.Command(cmdAndArgs[0])
  35. } else if len(cmdAndArgs) > 1 {
  36. c := cmdAndArgs[0]
  37. args := cmdAndArgs[1:]
  38. cmd = exec.Command(c, args...)
  39. } else if len(cmdAndArgs) == 0 {
  40. panic("Command length should be at least 1!")
  41. }
  42. }
  43. logger.Debugf("Executing command %s at %s", cmdAndArgs[0], workingDir)
  44. if _, err := os.Stat(workingDir); os.IsNotExist(err) {
  45. logger.Debugf("Making dir %s", workingDir)
  46. if err = os.MkdirAll(workingDir, 0755); err != nil {
  47. logger.Errorf("Error making dir %s", workingDir)
  48. }
  49. }
  50. cmd.Dir = workingDir
  51. cmd.Env = newEnviron(env, true)
  52. return &cmdJob{
  53. cmd: cmd,
  54. workingDir: workingDir,
  55. env: env,
  56. }
  57. }
  58. func (c *cmdJob) Start() error {
  59. c.finished = make(chan empty, 1)
  60. return c.cmd.Start()
  61. }
  62. func (c *cmdJob) Wait() error {
  63. c.Lock()
  64. defer c.Unlock()
  65. select {
  66. case <-c.finished:
  67. return c.retErr
  68. default:
  69. err := c.cmd.Wait()
  70. c.retErr = err
  71. close(c.finished)
  72. return err
  73. }
  74. }
  75. func (c *cmdJob) SetLogFile(logFile *os.File) {
  76. c.cmd.Stdout = logFile
  77. c.cmd.Stderr = logFile
  78. }
  79. func (c *cmdJob) Terminate() error {
  80. if c.cmd == nil || c.cmd.Process == nil {
  81. return errProcessNotStarted
  82. }
  83. err := unix.Kill(c.cmd.Process.Pid, syscall.SIGTERM)
  84. if err != nil {
  85. return err
  86. }
  87. select {
  88. case <-time.After(2 * time.Second):
  89. unix.Kill(c.cmd.Process.Pid, syscall.SIGKILL)
  90. return errors.New("SIGTERM failed to kill the job")
  91. case <-c.finished:
  92. return nil
  93. }
  94. }
  95. // Copied from go-sh
  96. func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
  97. environ := make([]string, 0, len(env))
  98. if inherit {
  99. for _, line := range os.Environ() {
  100. // if os environment and env collapses,
  101. // omit the os one
  102. k := strings.Split(line, "=")[0]
  103. if _, ok := env[k]; ok {
  104. continue
  105. }
  106. environ = append(environ, line)
  107. }
  108. }
  109. for k, v := range env {
  110. environ = append(environ, k+"="+v)
  111. }
  112. return environ
  113. }