@@ -10,6 +10,7 @@ import (
10
10
"github.com/icinga/icinga-notifications/internal/recipient"
11
11
"github.com/icinga/icingadb/pkg/icingadb"
12
12
"github.com/icinga/icingadb/pkg/logging"
13
+ "github.com/jmoiron/sqlx"
13
14
"golang.org/x/sync/errgroup"
14
15
"sync"
15
16
)
@@ -19,8 +20,123 @@ var (
19
20
currentIncidentsMu sync.Mutex
20
21
)
21
22
23
+ func LoadOpenIncidents (
24
+ ctx context.Context , db * icingadb.DB , logger * logging.Logger , runtimeConfig * config.RuntimeConfig , configFile * config.ConfigFile ,
25
+ ) error {
26
+ tx , err := db .BeginTxx (ctx , nil )
27
+ if err != nil {
28
+ return err
29
+ }
30
+ defer func () { _ = tx .Rollback () }()
31
+
32
+ var incidentRows []* IncidentRow
33
+ err = tx .SelectContext (ctx , & incidentRows , db .BuildSelectStmt (& IncidentRow {}, & IncidentRow {})+ ` WHERE "recovered_at" IS NULL` )
34
+ if err != nil {
35
+ return fmt .Errorf ("failed to fetch open incidents: %w" , err )
36
+ }
37
+
38
+ incidents := make (map [* object.Object ]* Incident )
39
+ g , childCtx := errgroup .WithContext (ctx )
40
+ for _ , incidentRow := range incidentRows {
41
+ incident := & Incident {
42
+ db : db ,
43
+ logger : logger ,
44
+ runtimeConfig : runtimeConfig ,
45
+ StartedAt : incidentRow .StartedAt .Time (),
46
+ incidentRowID : incidentRow .ID ,
47
+ configFile : configFile ,
48
+ SeverityBySource : map [int64 ]event.Severity {},
49
+ EscalationState : map [escalationID ]* EscalationState {},
50
+ Rules : map [ruleID ]struct {}{},
51
+ Recipients : map [recipient.Key ]* RecipientState {},
52
+ }
53
+
54
+ obj , err := object .FromDB (ctx , db , tx , incidentRow .ObjectID )
55
+ if err != nil {
56
+ return err
57
+ }
58
+
59
+ incident .Object = obj
60
+
61
+ g .Go (func () error {
62
+ sourceSeverity := & SourceSeverity {IncidentID : incidentRow .ID }
63
+ var sources []SourceSeverity
64
+ err = db .SelectContext (
65
+ childCtx , & sources ,
66
+ db .Rebind (db .BuildSelectStmt (sourceSeverity , sourceSeverity )+ ` WHERE "incident_id" = ? AND "severity" != ?` ),
67
+ incidentRow .ID , event .SeverityOK ,
68
+ )
69
+ if err != nil {
70
+ return fmt .Errorf ("failed to fetch incident sources Severity: %w" , err )
71
+ }
72
+
73
+ for _ , source := range sources {
74
+ incident .SeverityBySource [source .SourceID ] = source .Severity
75
+ }
76
+
77
+ return childCtx .Err ()
78
+ })
79
+
80
+ g .Go (func () error {
81
+ state := & EscalationState {}
82
+ var states []* EscalationState
83
+ err = db .SelectContext (childCtx , & states , db .Rebind (db .BuildSelectStmt (state , state )+ ` WHERE "incident_id" = ?` ), incidentRow .ID )
84
+ if err != nil {
85
+ return fmt .Errorf ("failed to fetch incident rule escalation state: %w" , err )
86
+ }
87
+
88
+ for _ , state := range states {
89
+ incident .EscalationState [state .RuleEscalationID ] = state
90
+ }
91
+
92
+ return childCtx .Err ()
93
+ })
94
+
95
+ g .Go (func () error {
96
+ return incident .ReloadRecipients (childCtx , tx )
97
+ })
98
+
99
+ g .Go (func () error {
100
+ incident .Lock ()
101
+ incident .runtimeConfig .RLock ()
102
+ defer func () {
103
+ defer incident .runtimeConfig .RUnlock ()
104
+ defer incident .Unlock ()
105
+ }()
106
+
107
+ err = incident .evaluateRules (childCtx , tx , 0 )
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ err = incident .evaluateEscalations ()
113
+ if err != nil {
114
+ return err
115
+ }
116
+
117
+ return childCtx .Err ()
118
+ })
119
+
120
+ incidents [obj ] = incident
121
+ }
122
+
123
+ if err = g .Wait (); err != nil {
124
+ return err
125
+ }
126
+ if err = tx .Commit (); err != nil {
127
+ return err
128
+ }
129
+
130
+ currentIncidentsMu .Lock ()
131
+ defer currentIncidentsMu .Unlock ()
132
+
133
+ currentIncidents = incidents
134
+
135
+ return nil
136
+ }
137
+
22
138
func GetCurrent (
23
- ctx context.Context , db * icingadb.DB , obj * object.Object , logger * logging.Logger , runtimeConfig * config.RuntimeConfig , configFile * config.ConfigFile , create bool ,
139
+ ctx context.Context , db * icingadb.DB , tx * sqlx. Tx , obj * object.Object , logger * logging.Logger , runtimeConfig * config.RuntimeConfig , configFile * config.ConfigFile , create bool ,
24
140
) (* Incident , bool , error ) {
25
141
currentIncidentsMu .Lock ()
26
142
defer currentIncidentsMu .Unlock ()
@@ -35,7 +151,7 @@ func GetCurrent(
35
151
incident .EscalationState = make (map [escalationID ]* EscalationState )
36
152
incident .Recipients = make (map [recipient.Key ]* RecipientState )
37
153
38
- err := db .QueryRowxContext (ctx , db .Rebind (db .BuildSelectStmt (ir , ir )+ ` WHERE "object_id" = ? AND "recovered_at" IS NULL` ), obj .ID ).StructScan (ir )
154
+ err := tx .QueryRowxContext (ctx , db .Rebind (db .BuildSelectStmt (ir , ir )+ ` WHERE "object_id" = ? AND "recovered_at" IS NULL` ), obj .ID ).StructScan (ir )
39
155
if err != nil && err != sql .ErrNoRows {
40
156
return nil , false , fmt .Errorf ("incident query failed with: %w" , err )
41
157
} else if err == nil {
@@ -46,7 +162,7 @@ func GetCurrent(
46
162
g .Go (func () error {
47
163
sourceSeverity := & SourceSeverity {IncidentID : ir .ID }
48
164
var sources []SourceSeverity
49
- err := db .SelectContext (
165
+ err := tx .SelectContext (
50
166
childCtx , & sources ,
51
167
db .Rebind (db .BuildSelectStmt (sourceSeverity , sourceSeverity )+ ` WHERE "incident_id" = ? AND "severity" != ?` ),
52
168
ir .ID , event .SeverityOK ,
@@ -65,7 +181,7 @@ func GetCurrent(
65
181
g .Go (func () error {
66
182
state := & EscalationState {}
67
183
var states []* EscalationState
68
- err = db .SelectContext (childCtx , & states , db .Rebind (db .BuildSelectStmt (state , state )+ ` WHERE "incident_id" = ?` ), ir .ID )
184
+ err = tx .SelectContext (childCtx , & states , db .Rebind (db .BuildSelectStmt (state , state )+ ` WHERE "incident_id" = ?` ), ir .ID )
69
185
if err != nil {
70
186
return fmt .Errorf ("failed to fetch incident rule escalation state: %w" , err )
71
187
}
@@ -95,22 +211,10 @@ func GetCurrent(
95
211
}
96
212
97
213
if ! created && currentIncident != nil {
98
- currentIncident .Lock ()
99
- defer currentIncident .Unlock ()
100
-
101
- contact := & ContactRow {}
102
- var contacts []* ContactRow
103
- err := db .SelectContext (ctx , & contacts , db .Rebind (db .BuildSelectStmt (contact , contact )+ ` WHERE "incident_id" = ?` ), currentIncident .ID ())
214
+ err := currentIncident .ReloadRecipients (ctx , tx )
104
215
if err != nil {
105
- return nil , false , fmt . Errorf ( "failed to fetch incident recipients: %w" , err )
216
+ return nil , false , err
106
217
}
107
-
108
- recipients := make (map [recipient.Key ]* RecipientState )
109
- for _ , contact := range contacts {
110
- recipients [contact .Key ] = & RecipientState {Role : contact .Role }
111
- }
112
-
113
- currentIncident .Recipients = recipients
114
218
}
115
219
116
220
return currentIncident , created , nil
0 commit comments