-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpatrol_apps.go
179 lines (177 loc) · 5.84 KB
/
patrol_apps.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package patrol
import (
"log"
"sync"
"time"
)
func (self *Patrol) shutdownApps() {
if self.config.unittesting {
// WE'RE UNITTESTING!!!
// we DO NOT want to send a signal on stop!!
// we need predictable behavior since we will kill both parent and child process to test patrol
return
}
var wg sync.WaitGroup
log.Printf("./patrol.shutdownApps(): signalling to all apps that we are shutting down!\n")
for _, app := range self.apps {
wg.Add(1)
go func(app *App) {
defer wg.Done()
app.o.Lock()
if app.isAppRunning() == nil {
log.Printf("./patrol.shutdownApps(): App ID: %s is running - Signalling!\n", app.id)
app.signalStop()
}
app.o.Unlock()
// call trigger outside of lock
if app.config.TriggerShutdown != nil {
// call trigger
app.config.TriggerShutdown(app)
}
}(app)
}
wg.Wait()
}
func (self *Patrol) runApps() {
var wg sync.WaitGroup
self.mu.RLock()
// we need a copy of our initial "start" time
started := self.ticker_running
shutdown := self.shutdown
self.mu.RUnlock()
// we're not going to initially start HTTP and UDP Apps on boot
// there's a chance these may actually be already running, we're going to wait up to at least PingTimeout * 2
can_start_pingable := time.Now().After(started.Add(time.Duration(self.config.PingTimeout*2) * time.Second))
for _, app := range self.apps {
wg.Add(1)
go func(app *App) {
defer wg.Done()
// we're not going to defer unlocking our app mutex, we're going to occasionally unlock and allow our tiggers to run
// for example when we check if our app is running, if we call close() we want to trigger our close right away
// if we do not unlock, we could then call startApp() without having ever signalled our close trigger
app.o.Lock()
// we have to check if we're running on every loop, regardless if we're disabled
// there's a chance our app could become enabled should we check and find we're running
// if we aren't running and we call close() and call our close trigger
is_running_err := app.isAppRunning()
is_running := (is_running_err == nil)
// if we're shutting down we're going to ignore state triggers and signal apps to stop
if shutdown {
// we're shutting down!
if is_running {
// signal our app to stop
log.Printf("./patrol.runApps(): App ID: %s is running AND we're shutting down! - Signalling!\n", app.id)
app.signalStop()
}
app.o.Unlock()
// we're done!
return
}
// patrol is still active!
// we need to run our state triggers
if is_running {
// we're running!
//log.Printf("./patrol.runApps(): App ID: %s is running\n", app.id)
if app.config.TriggerRunning != nil {
app.o.Unlock()
app.config.TriggerRunning(app)
app.o.Lock()
}
// if we're disabled or restarting we're going to signal our apps to stop
if app.o.IsRestart() {
// signal our app to stop
log.Printf("./patrol.runApps(): App ID: %s is running AND we're restarting! - Signalling!\n", app.id)
app.signalRestart()
// we will only attempt to restart ONCE, we consume restart even if we fail to restart!
app.o.SetRestart(false)
// it's going to ultimately be up to our App to exit
// we're not going to immediately attempt to start our app on this tick
// in fact, if our app chooses not to exit we will do nothing!
} else if app.o.IsDisabled() {
// signal our app to stop
log.Printf("./patrol.runApps(): App ID: %s is running AND is disabled! - Signalling!\n", app.id)
app.signalStop()
}
app.o.Unlock()
// we're done!
return
} else {
// we aren't running
if app.o.IsDisabled() {
// app is disabled
//log.Printf("./patrol.runApps(): App ID: %s is not running AND is disabled! - Reason: \"%s\"\n", app.id, is_running_err)
if app.config.TriggerDisabled != nil {
app.o.Unlock()
app.config.TriggerDisabled(app)
app.o.Lock()
}
// check if we're still disabled
if app.o.IsDisabled() {
// still disabled!!!
// nothing we can do
app.o.Unlock()
// we're done!
return
}
// we're now enabled!!!
log.Printf("./patrol.runApps(): App ID: %s was disabled and now enabled!\n", app.id)
} else {
// app is enabled and we aren't running
log.Printf("./patrol.runApps(): App ID: %s was not running, starting! - Reason: \"%s\"\n", app.id, is_running_err)
}
}
// check if we're pingable and if we can start yet
if !is_running {
if app.config.KeepAlive == APP_KEEPALIVE_HTTP ||
app.config.KeepAlive == APP_KEEPALIVE_UDP {
if !can_start_pingable {
// we can't start this service yet
app.o.Unlock()
// we're done!
log.Printf("./patrol.runApps(): App ID: %s is Pingable and can't be started yet, ignoring!\n", app.id)
return
}
}
}
// time to start our app!
log.Printf("./patrol.runApps(): App ID: %s starting!\n", app.id)
if app.config.TriggerStart != nil {
app.o.Unlock()
app.config.TriggerStart(app)
app.o.Lock()
// this will be our LAST chance to check disabled!!
if app.o.IsDisabled() {
// disabled!!!
app.o.Unlock()
// we're done!
log.Printf("./patrol.runApps(): App ID: %s can't start, we're disabled!\n", app.id)
return
}
}
// run!
if err := app.startApp(); err != nil {
log.Printf("./patrol.runApps(): App ID: %s failed to start: \"%s\"\n", app.id, err)
// call start failed trigger
if app.config.TriggerStartFailed != nil {
app.o.Unlock()
// we're done!
app.config.TriggerStartFailed(app)
return
}
} else {
log.Printf("./patrol.runApps(): App ID: %s started\n", app.id)
// call started trigger
if app.config.TriggerStarted != nil {
app.o.Unlock()
// we're done!
app.config.TriggerStarted(app)
return
}
}
app.o.Unlock()
// we're done!
return
}(app)
}
wg.Wait()
}