...
1
2
3
4
5
6
7
8
9
10
11
12
13
14 package model
15
16 import (
17 "fmt"
18 "time"
19 )
20
21 type AlertStatus string
22
23 const (
24 AlertFiring AlertStatus = "firing"
25 AlertResolved AlertStatus = "resolved"
26 )
27
28
29 type Alert struct {
30
31
32 Labels LabelSet `json:"labels"`
33
34
35 Annotations LabelSet `json:"annotations"`
36
37
38 StartsAt time.Time `json:"startsAt,omitempty"`
39 EndsAt time.Time `json:"endsAt,omitempty"`
40 GeneratorURL string `json:"generatorURL"`
41 }
42
43
44 func (a *Alert) Name() string {
45 return string(a.Labels[AlertNameLabel])
46 }
47
48
49
50 func (a *Alert) Fingerprint() Fingerprint {
51 return a.Labels.Fingerprint()
52 }
53
54 func (a *Alert) String() string {
55 s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7])
56 if a.Resolved() {
57 return s + "[resolved]"
58 }
59 return s + "[active]"
60 }
61
62
63 func (a *Alert) Resolved() bool {
64 return a.ResolvedAt(time.Now())
65 }
66
67
68
69 func (a *Alert) ResolvedAt(ts time.Time) bool {
70 if a.EndsAt.IsZero() {
71 return false
72 }
73 return !a.EndsAt.After(ts)
74 }
75
76
77 func (a *Alert) Status() AlertStatus {
78 return a.StatusAt(time.Now())
79 }
80
81
82 func (a *Alert) StatusAt(ts time.Time) AlertStatus {
83 if a.ResolvedAt(ts) {
84 return AlertResolved
85 }
86 return AlertFiring
87 }
88
89
90 func (a *Alert) Validate() error {
91 if a.StartsAt.IsZero() {
92 return fmt.Errorf("start time missing")
93 }
94 if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
95 return fmt.Errorf("start time must be before end time")
96 }
97 if err := a.Labels.Validate(); err != nil {
98 return fmt.Errorf("invalid label set: %w", err)
99 }
100 if len(a.Labels) == 0 {
101 return fmt.Errorf("at least one label pair required")
102 }
103 if err := a.Annotations.Validate(); err != nil {
104 return fmt.Errorf("invalid annotations: %w", err)
105 }
106 return nil
107 }
108
109
110 type Alerts []*Alert
111
112 func (as Alerts) Len() int { return len(as) }
113 func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
114
115 func (as Alerts) Less(i, j int) bool {
116 if as[i].StartsAt.Before(as[j].StartsAt) {
117 return true
118 }
119 if as[i].EndsAt.Before(as[j].EndsAt) {
120 return true
121 }
122 return as[i].Fingerprint() < as[j].Fingerprint()
123 }
124
125
126 func (as Alerts) HasFiring() bool {
127 for _, a := range as {
128 if !a.Resolved() {
129 return true
130 }
131 }
132 return false
133 }
134
135
136
137 func (as Alerts) HasFiringAt(ts time.Time) bool {
138 for _, a := range as {
139 if !a.ResolvedAt(ts) {
140 return true
141 }
142 }
143 return false
144 }
145
146
147 func (as Alerts) Status() AlertStatus {
148 if as.HasFiring() {
149 return AlertFiring
150 }
151 return AlertResolved
152 }
153
154
155
156 func (as Alerts) StatusAt(ts time.Time) AlertStatus {
157 if as.HasFiringAt(ts) {
158 return AlertFiring
159 }
160 return AlertResolved
161 }
162
View as plain text