cmd_provider.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package worker
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "regexp"
  7. "time"
  8. "github.com/anmitsu/go-shlex"
  9. )
  10. type cmdConfig struct {
  11. name string
  12. upstreamURL, command string
  13. workingDir, logDir, logFile string
  14. interval time.Duration
  15. retry int
  16. env map[string]string
  17. failOnMatch string
  18. }
  19. type cmdProvider struct {
  20. baseProvider
  21. cmdConfig
  22. command []string
  23. failOnMatch *regexp.Regexp
  24. }
  25. func newCmdProvider(c cmdConfig) (*cmdProvider, error) {
  26. // TODO: check config options
  27. if c.retry == 0 {
  28. c.retry = defaultMaxRetry
  29. }
  30. provider := &cmdProvider{
  31. baseProvider: baseProvider{
  32. name: c.name,
  33. ctx: NewContext(),
  34. interval: c.interval,
  35. retry: c.retry,
  36. },
  37. cmdConfig: c,
  38. }
  39. provider.ctx.Set(_WorkingDirKey, c.workingDir)
  40. provider.ctx.Set(_LogDirKey, c.logDir)
  41. provider.ctx.Set(_LogFileKey, c.logFile)
  42. cmd, err := shlex.Split(c.command, true)
  43. if err != nil {
  44. return nil, err
  45. }
  46. provider.command = cmd
  47. if len(c.failOnMatch) > 0 {
  48. var err error
  49. failOnMatch, err := regexp.Compile(c.failOnMatch)
  50. if err != nil {
  51. return nil, errors.New("fail-on-match regexp error: " + err.Error())
  52. }
  53. provider.failOnMatch = failOnMatch
  54. }
  55. return provider, nil
  56. }
  57. func (p *cmdProvider) Type() providerEnum {
  58. return provCommand
  59. }
  60. func (p *cmdProvider) Upstream() string {
  61. return p.upstreamURL
  62. }
  63. func (p *cmdProvider) Run() error {
  64. if err := p.Start(); err != nil {
  65. return err
  66. }
  67. if err := p.Wait(); err != nil {
  68. return err
  69. }
  70. if p.failOnMatch != nil {
  71. if logContent, err := ioutil.ReadFile(p.LogFile()); err == nil {
  72. matches := p.failOnMatch.FindAllSubmatch(logContent, -1)
  73. if len(matches) != 0 {
  74. logger.Debug("Fail-on-match: %r", matches)
  75. return errors.New(
  76. fmt.Sprintf("Fail-on-match regexp found %d matches", len(matches)))
  77. }
  78. } else {
  79. return err
  80. }
  81. }
  82. return nil
  83. }
  84. func (p *cmdProvider) Start() error {
  85. p.Lock()
  86. defer p.Unlock()
  87. if p.IsRunning() {
  88. return errors.New("provider is currently running")
  89. }
  90. env := map[string]string{
  91. "TUNASYNC_MIRROR_NAME": p.Name(),
  92. "TUNASYNC_WORKING_DIR": p.WorkingDir(),
  93. "TUNASYNC_UPSTREAM_URL": p.upstreamURL,
  94. "TUNASYNC_LOG_DIR": p.LogDir(),
  95. "TUNASYNC_LOG_FILE": p.LogFile(),
  96. }
  97. for k, v := range p.env {
  98. env[k] = v
  99. }
  100. p.cmd = newCmdJob(p, p.command, p.WorkingDir(), env)
  101. if err := p.prepareLogFile(false); err != nil {
  102. return err
  103. }
  104. if err := p.cmd.Start(); err != nil {
  105. return err
  106. }
  107. p.isRunning.Store(true)
  108. return nil
  109. }