runner.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package worker
  2. import (
  3. "errors"
  4. "os"
  5. "os/exec"
  6. "strings"
  7. "syscall"
  8. "time"
  9. "golang.org/x/sys/unix"
  10. )
  11. // runner is to run os commands giving command line, env and log file
  12. // it's an alternative to python-sh or go-sh
  13. // TODO: cgroup excution
  14. type cmdJob struct {
  15. cmd *exec.Cmd
  16. workingDir string
  17. env map[string]string
  18. logFile *os.File
  19. finished chan struct{}
  20. }
  21. func newCmdJob(cmdAndArgs []string, workingDir string, env map[string]string) *cmdJob {
  22. var cmd *exec.Cmd
  23. if len(cmdAndArgs) == 1 {
  24. cmd = exec.Command(cmdAndArgs[0])
  25. } else if len(cmdAndArgs) > 1 {
  26. c := cmdAndArgs[0]
  27. args := cmdAndArgs[1:]
  28. cmd = exec.Command(c, args...)
  29. } else if len(cmdAndArgs) == 0 {
  30. panic("Command length should be at least 1!")
  31. }
  32. cmd.Dir = workingDir
  33. cmd.Env = newEnviron(env, true)
  34. return &cmdJob{
  35. cmd: cmd,
  36. workingDir: workingDir,
  37. env: env,
  38. }
  39. }
  40. // start job and wait
  41. func (c *cmdJob) Run() error {
  42. err := c.cmd.Start()
  43. if err != nil {
  44. return err
  45. }
  46. return c.Wait()
  47. }
  48. func (c *cmdJob) Start() error {
  49. c.finished = make(chan struct{}, 1)
  50. return c.cmd.Start()
  51. }
  52. func (c *cmdJob) Wait() error {
  53. err := c.cmd.Wait()
  54. c.finished <- struct{}{}
  55. return err
  56. }
  57. func (c *cmdJob) SetLogFile(logFile *os.File) {
  58. c.cmd.Stdout = logFile
  59. c.cmd.Stderr = logFile
  60. }
  61. func (c *cmdJob) Terminate() error {
  62. if c.cmd == nil {
  63. return errors.New("Command not initialized")
  64. }
  65. if c.cmd.Process == nil {
  66. return errors.New("No Process Running")
  67. }
  68. err := unix.Kill(c.cmd.Process.Pid, syscall.SIGTERM)
  69. if err != nil {
  70. return err
  71. }
  72. select {
  73. case <-time.After(2 * time.Second):
  74. unix.Kill(c.cmd.Process.Pid, syscall.SIGKILL)
  75. return errors.New("SIGTERM failed to kill the job")
  76. case <-c.finished:
  77. return nil
  78. }
  79. }
  80. // Copied from go-sh
  81. func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
  82. environ := make([]string, 0, len(env))
  83. if inherit {
  84. for _, line := range os.Environ() {
  85. // if os environment and env collapses,
  86. // omit the os one
  87. k := strings.Split(line, "=")[0]
  88. if _, ok := env[k]; ok {
  89. continue
  90. }
  91. environ = append(environ, line)
  92. }
  93. }
  94. for k, v := range env {
  95. environ = append(environ, k+"="+v)
  96. }
  97. return environ
  98. }