-
Notifications
You must be signed in to change notification settings - Fork 9
/
status.go
254 lines (210 loc) · 6.58 KB
/
status.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package modules
import (
"context"
"github.com/tevino/abool"
)
// Module Status Values
const (
StatusDead uint8 = 0 // not prepared, not started
StatusPreparing uint8 = 1
StatusOffline uint8 = 2 // prepared, not started
StatusStopping uint8 = 3
StatusStarting uint8 = 4
StatusOnline uint8 = 5 // online and running
)
// Module Failure Status Values
const (
FailureNone uint8 = 0
FailureHint uint8 = 1
FailureWarning uint8 = 2
FailureError uint8 = 3
)
// ready status
const (
statusWaiting uint8 = iota
statusReady
statusNothingToDo
)
var (
failureUpdateNotifyFunc func(moduleFailure uint8, id, title, msg string)
failureUpdateNotifyFuncEnabled = abool.NewBool(false)
failureUpdateNotifyFuncReady = abool.NewBool(false)
)
// SetFailureUpdateNotifyFunc sets a function that is called on every change
// of a module's failure status.
func SetFailureUpdateNotifyFunc(fn func(moduleFailure uint8, id, title, msg string)) bool {
if failureUpdateNotifyFuncEnabled.SetToIf(false, true) {
failureUpdateNotifyFunc = fn
failureUpdateNotifyFuncReady.Set()
return true
}
return false
}
// Online returns whether the module is online.
func (m *Module) Online() bool {
return m.Status() == StatusOnline
}
// OnlineSoon returns whether the module is or is about to be online.
func (m *Module) OnlineSoon() bool {
if moduleMgmtEnabled.IsSet() &&
!m.enabled.IsSet() &&
!m.enabledAsDependency.IsSet() {
return false
}
return !m.stopFlag.IsSet()
}
// Status returns the current module status.
func (m *Module) Status() uint8 {
m.RLock()
defer m.RUnlock()
return m.status
}
// FailureStatus returns the current failure status, ID and message.
func (m *Module) FailureStatus() (failureStatus uint8, failureID, failureMsg string) {
m.RLock()
defer m.RUnlock()
return m.failureStatus, m.failureID, m.failureMsg
}
// Hint sets failure status to hint. This is a somewhat special failure status,
// as the module is believed to be working correctly, but there is an important
// module specific information to convey. The supplied failureID is for
// improved automatic handling within connected systems, the failureMsg is for
// humans.
// The given ID must be unique for the given title and message. A call to
// Hint(), Warning() or Error() with the same ID as the existing one will be
// ignored.
func (m *Module) Hint(id, title, msg string) {
m.Lock()
defer m.Unlock()
m.setFailure(FailureHint, id, title, msg)
}
// Warning sets failure status to warning. The supplied failureID is for
// improved automatic handling within connected systems, the failureMsg is for
// humans.
// The given ID must be unique for the given title and message. A call to
// Hint(), Warning() or Error() with the same ID as the existing one will be
// ignored.
func (m *Module) Warning(id, title, msg string) {
m.Lock()
defer m.Unlock()
m.setFailure(FailureWarning, id, title, msg)
}
// Error sets failure status to error. The supplied failureID is for improved
// automatic handling within connected systems, the failureMsg is for humans.
// The given ID must be unique for the given title and message. A call to
// Hint(), Warning() or Error() with the same ID as the existing one will be
// ignored.
func (m *Module) Error(id, title, msg string) {
m.Lock()
defer m.Unlock()
m.setFailure(FailureError, id, title, msg)
}
func (m *Module) setFailure(status uint8, id, title, msg string) {
// Ignore calls with the same ID.
if id == m.failureID {
return
}
// Copy data for failure status update worker.
resolveFailureID := m.failureID
// Set new failure status.
m.failureStatus = status
m.failureID = id
m.failureTitle = title
m.failureMsg = msg
// Notify of module change.
m.notifyOfChange()
// Propagate failure status.
if failureUpdateNotifyFuncReady.IsSet() {
m.RunWorker("failure status updater", func(context.Context) error {
// Only use data in worker that won't change anymore.
// Resolve previous failure state if available.
if resolveFailureID != "" {
failureUpdateNotifyFunc(FailureNone, resolveFailureID, "", "")
}
// Notify of new failure state.
failureUpdateNotifyFunc(status, id, title, msg)
return nil
})
}
}
// Resolve removes the failure state from the module if the given failureID matches the current failure ID. If the given failureID is an empty string, Resolve removes any failure state.
func (m *Module) Resolve(failureID string) {
m.Lock()
defer m.Unlock()
// Check if resolving is necessary.
if failureID != "" && failureID != m.failureID {
// Return immediately if not resolving any (`""`) or if the failure ID
// does not match.
return
}
// Copy data for failure status update worker.
resolveFailureID := m.failureID
// Set failure status on module.
m.failureStatus = FailureNone
m.failureID = ""
m.failureTitle = ""
m.failureMsg = ""
// Notify of module change.
m.notifyOfChange()
// Propagate failure status.
if failureUpdateNotifyFuncReady.IsSet() {
m.RunWorker("failure status updater", func(context.Context) error {
// Only use data in worker that won't change anymore.
failureUpdateNotifyFunc(FailureNone, resolveFailureID, "", "")
return nil
})
}
}
// readyToPrep returns whether all dependencies are ready for this module to prep.
func (m *Module) readyToPrep() uint8 {
// check if valid state for prepping
if m.Status() != StatusDead {
return statusNothingToDo
}
for _, dep := range m.depModules {
if dep.Status() < StatusOffline {
return statusWaiting
}
}
return statusReady
}
// readyToStart returns whether all dependencies are ready for this module to start.
func (m *Module) readyToStart() uint8 {
// check if start is wanted
if moduleMgmtEnabled.IsSet() {
if !m.enabled.IsSet() && !m.enabledAsDependency.IsSet() {
return statusNothingToDo
}
}
// check if valid state for starting
if m.Status() != StatusOffline {
return statusNothingToDo
}
// check if all dependencies are ready
for _, dep := range m.depModules {
if dep.Status() < StatusOnline {
return statusWaiting
}
}
return statusReady
}
// readyToStop returns whether all dependencies are ready for this module to stop.
func (m *Module) readyToStop() uint8 {
// check if stop is wanted
if moduleMgmtEnabled.IsSet() && !shutdownFlag.IsSet() {
if m.enabled.IsSet() || m.enabledAsDependency.IsSet() {
return statusNothingToDo
}
}
// check if valid state for stopping
if m.Status() != StatusOnline {
return statusNothingToDo
}
for _, revDep := range m.depReverse {
// not ready if a reverse dependency was started, but not yet stopped
if revDep.Status() > StatusOffline {
return statusWaiting
}
}
return statusReady
}