forked from gitter-badger/jetflow
/
packs.go
134 lines (106 loc) · 2.7 KB
/
packs.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package main
import (
"os"
"os/exec"
"path"
"syscall"
"time"
"github.com/BurntSushi/toml"
"github.com/surge/glog"
)
var configChan = make(chan configEvent)
// hubConfig represents the configuration file settings of the hub.
type hubConfig struct {
Packs map[string]string
}
// launchAllPacks is called once on startup, and is driven by the config file.
func launchAllPacks() {
config := hubConfig{make(map[string]string)}
go config.processEvents()
reload()
}
// reload can be called any time to force re-reading of the configuration file.
func reload() {
newConf := &hubConfig{}
if err := newConf.load(); err != nil {
glog.Error(err)
return
}
configChan <- configEvent{"", newConf}
}
// configEvent represents changes to the configuration and pack state changes
type configEvent struct {
name string
arg interface{}
}
// processEvents is called as goroutine from launchAllPacks.
func (h *hubConfig) processEvents() {
processes := make(map[string]*os.Process)
for event := range configChan {
name := event.name
switch argVal := event.arg.(type) {
case *hubConfig:
// only act on what has changed
for name, vOld := range h.Packs {
vNew, ok := argVal.Packs[name]
delete(argVal.Packs, name)
if ok && vNew == vOld {
continue
}
if ok {
h.Packs[name] = vNew
} else {
delete(h.Packs, name)
}
glog.Infoln("quit:", name)
if p, ok := processes[name]; ok {
p.Signal(syscall.SIGQUIT)
delete(processes, name)
continue // exit will re-launch
}
if ok {
h.launch(name, vNew)
}
}
for key, val := range argVal.Packs {
h.launch(key, val)
}
case *os.Process:
glog.Infoln("started:", name, "pid:", argVal.Pid)
processes[name] = argVal
default:
glog.Infoln("ended:", name, "status:", event.arg)
delete(processes, name)
time.Sleep(3 * time.Second)
if cmd, ok := h.Packs[name]; ok {
h.launch(name, cmd)
}
}
}
}
// load the configuration settings from file.
func (h *hubConfig) load() error {
configFile := path.Join(*runFlag, "hubtab.toml")
if _, err := toml.DecodeFile(configFile, h); err != nil {
return err
}
glog.Infoln("config loaded:", configFile)
return nil
}
// launch the specified pack and report all process state changes as events.
func (h *hubConfig) launch(name, command string) {
glog.Infoln("launch:", name, "cmd:", command)
h.Packs[name] = command
go func() {
cmd := exec.Command("/bin/sh", "-c", command)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
glog.Error(err)
return
}
configChan <- configEvent{name, cmd.Process} // lift-off!
err := cmd.Wait()
configChan <- configEvent{name, err} // touch-down!
}()
}