-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathresources_monitor.go
More file actions
129 lines (102 loc) · 3.05 KB
/
Copy pathresources_monitor.go
File metadata and controls
129 lines (102 loc) · 3.05 KB
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
package resourcesmonitor
import (
"math"
"time"
"github.com/coder/coder/v2/agent/proto"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/util/slice"
)
type State int
const (
StateOK State = iota
StateNOK
StateUnknown
)
type AlertConfig struct {
// What percentage of datapoints in a row are
// required to put the monitor in an alert state.
ConsecutiveNOKsPercent int
// What percentage of datapoints in a window are
// required to put the monitor in an alert state.
MinimumNOKsPercent int
}
type Config struct {
// How many datapoints should the agent send
NumDatapoints int32
// How long between each datapoint should
// collection occur.
CollectionInterval time.Duration
Alert AlertConfig
}
func CalculateMemoryUsageStates(
monitor database.WorkspaceAgentMemoryResourceMonitor,
datapoints []*proto.PushResourcesMonitoringUsageRequest_Datapoint_MemoryUsage,
) []State {
states := make([]State, 0, len(datapoints))
for _, datapoint := range datapoints {
state := StateUnknown
if datapoint != nil {
percent := int32(float64(datapoint.Used) / float64(datapoint.Total) * 100)
if percent < monitor.Threshold {
state = StateOK
} else {
state = StateNOK
}
}
states = append(states, state)
}
return states
}
func CalculateVolumeUsageStates(
monitor database.WorkspaceAgentVolumeResourceMonitor,
datapoints []*proto.PushResourcesMonitoringUsageRequest_Datapoint_VolumeUsage,
) []State {
states := make([]State, 0, len(datapoints))
for _, datapoint := range datapoints {
state := StateUnknown
if datapoint != nil {
percent := int32(float64(datapoint.Used) / float64(datapoint.Total) * 100)
if percent < monitor.Threshold {
state = StateOK
} else {
state = StateNOK
}
}
states = append(states, state)
}
return states
}
func NextState(c Config, oldState database.WorkspaceAgentMonitorState, states []State) database.WorkspaceAgentMonitorState {
// If there are enough consecutive NOK states, we should be in an
// alert state.
consecutiveNOKs := slice.CountConsecutive(StateNOK, states...)
if percent(consecutiveNOKs, len(states)) >= c.Alert.ConsecutiveNOKsPercent {
return database.WorkspaceAgentMonitorStateNOK
}
// We do not explicitly handle StateUnknown because it could have
// been either StateOK or StateNOK if collection didn't fail. As
// it could be either, our best bet is to ignore it.
nokCount, okCount := 0, 0
for _, state := range states {
switch state {
case StateOK:
okCount++
case StateNOK:
nokCount++
}
}
// If there are enough NOK datapoints, we should be in an alert state.
if percent(nokCount, len(states)) >= c.Alert.MinimumNOKsPercent {
return database.WorkspaceAgentMonitorStateNOK
}
// If all datapoints are OK, we should be in an OK state
if okCount == len(states) {
return database.WorkspaceAgentMonitorStateOK
}
// Otherwise we stay in the same state as last.
return oldState
}
func percent[T int](numerator, denominator T) int {
percent := float64(numerator*100) / float64(denominator)
return int(math.Round(percent))
}