-
Notifications
You must be signed in to change notification settings - Fork 1
/
exec.go
109 lines (93 loc) · 1.99 KB
/
exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strings"
"sync"
"time"
"github.com/pkg/errors"
)
type ExecOptions struct {
Scripts map[string]CmdOptions `json:"scripts"`
}
type CmdOptions struct {
Command []string
Dir string
Timeout time.Duration
}
func (c *CmdOptions) UnmarshalJSON(data []byte) error {
var opt struct {
Command []string `json:"command"`
Dir string `json:"dir"`
Timeout string `json:"timeout"`
}
if err := json.Unmarshal(data, &opt); err != nil {
return err
}
if opt.Timeout == "" {
opt.Timeout = "5s"
}
d, err := time.ParseDuration(opt.Timeout)
if err != nil {
return errors.Wrap(err, "time.ParseDuration")
}
c.Command = opt.Command
c.Dir = opt.Dir
c.Timeout = d
return nil
}
type ExecService struct {
opts ExecOptions
active map[string]bool
mu sync.Mutex
}
func NewExecService(opts ExecOptions) *ExecService {
return &ExecService{
opts: opts,
active: make(map[string]bool, len(opts.Scripts)),
}
}
func (s *ExecService) handleExec(w http.ResponseWriter, r *http.Request) {
id := strings.TrimPrefix(r.URL.Path, "/exec/")
script, ok := s.opts.Scripts[id]
if !ok {
http.Error(w, "please specify a valid id", http.StatusBadRequest)
return
}
fmt.Fprintln(w, "ok")
go func() {
if !s.setActive(id) {
log.Printf("script %q is already running", id)
return
}
defer s.unsetActive(id)
ctx, cancel := context.WithTimeout(context.Background(), script.Timeout)
defer cancel()
cmd := exec.CommandContext(ctx, script.Command[0], script.Command[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = script.Dir
if err := cmd.Run(); err != nil {
log.Printf("failed to run script %q: %v", id, err)
}
}()
}
func (s *ExecService) setActive(id string) bool {
s.mu.Lock()
defer s.mu.Unlock()
if s.active[id] {
return false
}
s.active[id] = true
return true
}
func (s *ExecService) unsetActive(id string) {
s.mu.Lock()
defer s.mu.Unlock()
s.active[id] = false
}