@@ -105,9 +105,17 @@ func (r *GrafanaAlertRuleGroupReconciler) Reconcile(ctx context.Context, req ctr
105
105
return ctrl.Result {}, fmt .Errorf ("folder uid not found: %w" , err )
106
106
}
107
107
108
+ editable := "true" //nolint:goconst
109
+ if group .Spec .Editable != nil && ! * group .Spec .Editable {
110
+ editable = "false"
111
+ }
112
+
113
+ mGroup := crToModel (group , folderUID )
114
+ log .V (1 ).Info ("converted cr to api model" )
115
+
108
116
applyErrors := make (map [string ]string )
109
117
for _ , grafana := range instances {
110
- err := r .reconcileWithInstance (ctx , & grafana , group , folderUID )
118
+ err := r .reconcileWithInstance (ctx , & grafana , group , & mGroup , editable )
111
119
if err != nil {
112
120
applyErrors [fmt .Sprintf ("%s/%s" , grafana .Namespace , grafana .Name )] = err .Error ()
113
121
}
@@ -123,26 +131,73 @@ func (r *GrafanaAlertRuleGroupReconciler) Reconcile(ctx context.Context, req ctr
123
131
return ctrl.Result {RequeueAfter : group .Spec .ResyncPeriod .Duration }, nil
124
132
}
125
133
126
- // SetupWithManager sets up the controller with the Manager.
127
- func (r * GrafanaAlertRuleGroupReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
128
- return ctrl .NewControllerManagedBy (mgr ).
129
- For (& grafanav1beta1.GrafanaAlertRuleGroup {}).
130
- WithEventFilter (ignoreStatusUpdates ()).
131
- Complete (r )
134
+ func crToModel (cr * grafanav1beta1.GrafanaAlertRuleGroup , folderUID string ) models.AlertRuleGroup {
135
+ groupName := cr .GroupName ()
136
+
137
+ mRules := make (models.ProvisionedAlertRules , 0 , len (cr .Spec .Rules ))
138
+
139
+ for _ , r := range cr .Spec .Rules {
140
+ apiRule := & models.ProvisionedAlertRule {
141
+ Annotations : r .Annotations ,
142
+ Condition : & r .Condition ,
143
+ Data : make ([]* models.AlertQuery , len (r .Data )),
144
+ ExecErrState : & r .ExecErrState ,
145
+ FolderUID : & folderUID ,
146
+ For : (* strfmt .Duration )(& r .For .Duration ),
147
+ IsPaused : r .IsPaused ,
148
+ Labels : r .Labels ,
149
+ NoDataState : r .NoDataState ,
150
+ RuleGroup : & groupName ,
151
+ Title : & r .Title ,
152
+ UID : r .UID ,
153
+ }
154
+
155
+ if r .NotificationSettings != nil {
156
+ apiRule .NotificationSettings = & models.AlertRuleNotificationSettings {
157
+ Receiver : & r .NotificationSettings .Receiver ,
158
+ GroupBy : r .NotificationSettings .GroupBy ,
159
+ GroupWait : r .NotificationSettings .GroupWait ,
160
+ MuteTimeIntervals : r .NotificationSettings .MuteTimeIntervals ,
161
+ GroupInterval : r .NotificationSettings .GroupInterval ,
162
+ RepeatInterval : r .NotificationSettings .RepeatInterval ,
163
+ }
164
+ }
165
+
166
+ if r .Record != nil {
167
+ apiRule .Record = & models.Record {
168
+ From : & r .Record .From ,
169
+ Metric : & r .Record .Metric ,
170
+ }
171
+ }
172
+
173
+ for idx , q := range r .Data {
174
+ apiRule .Data [idx ] = & models.AlertQuery {
175
+ DatasourceUID : q .DatasourceUID ,
176
+ Model : q .Model ,
177
+ QueryType : q .QueryType ,
178
+ RefID : q .RefID ,
179
+ RelativeTimeRange : q .RelativeTimeRange ,
180
+ }
181
+ }
182
+
183
+ mRules = append (mRules , apiRule )
184
+ }
185
+
186
+ return models.AlertRuleGroup {
187
+ FolderUID : folderUID ,
188
+ Interval : int64 (cr .Spec .Interval .Seconds ()),
189
+ Rules : mRules ,
190
+ Title : groupName ,
191
+ }
132
192
}
133
193
134
- func (r * GrafanaAlertRuleGroupReconciler ) reconcileWithInstance (ctx context.Context , instance * grafanav1beta1.Grafana , group * grafanav1beta1.GrafanaAlertRuleGroup , folderUID string ) error {
194
+ func (r * GrafanaAlertRuleGroupReconciler ) reconcileWithInstance (ctx context.Context , instance * grafanav1beta1.Grafana , group * grafanav1beta1.GrafanaAlertRuleGroup , mGroup * models. AlertRuleGroup , disableProvenance string ) error {
135
195
cl , err := client2 .NewGeneratedGrafanaClient (ctx , r .Client , instance )
136
196
if err != nil {
137
197
return fmt .Errorf ("building grafana client: %w" , err )
138
198
}
139
199
140
- trueRef := "true" //nolint:goconst
141
- editable := true //nolint:staticcheck
142
- if group .Spec .Editable != nil && ! * group .Spec .Editable {
143
- editable = false
144
- }
145
-
200
+ folderUID := mGroup .FolderUID
146
201
_ , err = cl .Folders .GetFolderByUID (folderUID ) //nolint:errcheck
147
202
if err != nil {
148
203
var folderNotFound * folders.GetFolderByUIDNotFound
@@ -152,122 +207,55 @@ func (r *GrafanaAlertRuleGroupReconciler) reconcileWithInstance(ctx context.Cont
152
207
return fmt .Errorf ("fetching folder: %w" , err )
153
208
}
154
209
155
- groupName := group .GroupName ()
156
- applied , err := cl .Provisioning .GetAlertRuleGroup (groupName , folderUID )
210
+ applied , err := cl .Provisioning .GetAlertRuleGroup (mGroup .Title , folderUID )
157
211
var ruleNotFound * provisioning.GetAlertRuleGroupNotFound
158
212
if err != nil && ! errors .As (err , & ruleNotFound ) {
159
213
return fmt .Errorf ("fetching existing alert rule group: %w" , err )
160
214
}
161
215
162
- currentRules := make (map [string ]bool )
163
- if applied != nil {
164
- for _ , rule := range applied .Payload .Rules {
165
- currentRules [rule .UID ] = false
166
- }
216
+ // Create an empty collection to loop over if group does not exist on remote
217
+ remoteRules := models.ProvisionedAlertRules {}
218
+ if applied != nil && applied .Payload != nil {
219
+ remoteRules = applied .Payload .Rules
167
220
}
168
221
169
- for _ , rule := range group .Spec .Rules {
170
- apiRule := & models.ProvisionedAlertRule {
171
- Annotations : rule .Annotations ,
172
- Condition : & rule .Condition ,
173
- Data : make ([]* models.AlertQuery , len (rule .Data )),
174
- ExecErrState : & rule .ExecErrState ,
175
- FolderUID : & folderUID ,
176
- For : (* strfmt .Duration )(& rule .For .Duration ),
177
- IsPaused : rule .IsPaused ,
178
- Labels : rule .Labels ,
179
- NoDataState : rule .NoDataState ,
180
- RuleGroup : & groupName ,
181
- Title : & rule .Title ,
182
- UID : rule .UID ,
183
- }
184
- if rule .NotificationSettings != nil {
185
- apiRule .NotificationSettings = & models.AlertRuleNotificationSettings {
186
- Receiver : & rule .NotificationSettings .Receiver ,
187
- GroupBy : rule .NotificationSettings .GroupBy ,
188
- GroupWait : rule .NotificationSettings .GroupWait ,
189
- MuteTimeIntervals : rule .NotificationSettings .MuteTimeIntervals ,
190
- GroupInterval : rule .NotificationSettings .GroupInterval ,
191
- RepeatInterval : rule .NotificationSettings .RepeatInterval ,
192
- }
193
- }
194
- if rule .Record != nil {
195
- apiRule .Record = & models.Record {
196
- From : & rule .Record .From ,
197
- Metric : & rule .Record .Metric ,
198
- }
199
- }
200
- for idx , q := range rule .Data {
201
- apiRule .Data [idx ] = & models.AlertQuery {
202
- DatasourceUID : q .DatasourceUID ,
203
- Model : q .Model ,
204
- QueryType : q .QueryType ,
205
- RefID : q .RefID ,
206
- RelativeTimeRange : q .RelativeTimeRange ,
222
+ // Rules must be created individually
223
+ // Find rules missing on the instance and create them
224
+ for _ , mRule := range mGroup .Rules {
225
+ ruleExists := false
226
+ for _ , remoteRule := range remoteRules {
227
+ if mRule .UID == remoteRule .UID {
228
+ ruleExists = true
229
+ break
207
230
}
208
231
}
209
232
210
- if _ , ok := currentRules [rule .UID ]; ok {
211
- params := provisioning .NewPutAlertRuleParams ().
212
- WithBody (apiRule ).
213
- WithUID (rule .UID )
214
- if editable {
215
- params .SetXDisableProvenance (& trueRef )
216
- }
217
- _ , err := cl .Provisioning .PutAlertRule (params ) //nolint:errcheck
218
- if err != nil {
219
- return fmt .Errorf ("updating rule: %w" , err )
220
- }
221
- } else {
233
+ if ! ruleExists {
222
234
params := provisioning .NewPostAlertRuleParams ().
223
- WithBody (apiRule )
224
- if editable {
225
- params .SetXDisableProvenance (& trueRef )
226
- }
235
+ WithBody (mRule ).
236
+ WithXDisableProvenance (& disableProvenance )
227
237
_ , err = cl .Provisioning .PostAlertRule (params ) //nolint:errcheck
228
238
if err != nil {
229
239
return fmt .Errorf ("creating rule: %w" , err )
230
240
}
231
241
}
232
-
233
- currentRules [rule .UID ] = true
234
- }
235
-
236
- for uid , present := range currentRules {
237
- if present {
238
- continue
239
- }
240
- params := provisioning .NewDeleteAlertRuleParams ().
241
- WithUID (uid )
242
- if editable {
243
- params .SetXDisableProvenance (& trueRef )
244
- }
245
- _ , err := cl .Provisioning .DeleteAlertRule (params ) //nolint:errcheck
246
- if err != nil {
247
- return fmt .Errorf ("deleting old alert rule %s: %w" , uid , err )
248
- }
249
242
}
250
243
251
- mGroup := & models.AlertRuleGroup {
252
- FolderUID : folderUID ,
253
- Interval : int64 (group .Spec .Interval .Seconds ()),
254
- Rules : []* models.ProvisionedAlertRule {},
255
- Title : "" ,
256
- }
244
+ // Update whole group and all rules existing rules at once
245
+ // Will delete rules not present in the body
257
246
params := provisioning .NewPutAlertRuleGroupParams ().
258
247
WithBody (mGroup ).
259
- WithGroup (groupName ).
260
- WithFolderUID (folderUID )
261
- if editable {
262
- params .SetXDisableProvenance (& trueRef )
263
- }
248
+ WithGroup (mGroup .Title ).
249
+ WithFolderUID (folderUID ).
250
+ WithXDisableProvenance (& disableProvenance )
251
+
264
252
_ , err = cl .Provisioning .PutAlertRuleGroup (params ) //nolint:errcheck
265
253
if err != nil {
266
254
return fmt .Errorf ("updating group: %s" , err .Error ())
267
255
}
268
256
269
257
// Update grafana instance Status
270
- instance .Status .AlertRuleGroups = instance .Status .AlertRuleGroups .Add (group .Namespace , group .Name , groupName )
258
+ instance .Status .AlertRuleGroups = instance .Status .AlertRuleGroups .Add (group .Namespace , group .Name , mGroup . Title )
271
259
return r .Client .Status ().Update (ctx , instance )
272
260
}
273
261
@@ -306,3 +294,11 @@ func (r *GrafanaAlertRuleGroupReconciler) finalize(ctx context.Context, group *g
306
294
}
307
295
return nil
308
296
}
297
+
298
+ // SetupWithManager sets up the controller with the Manager.
299
+ func (r * GrafanaAlertRuleGroupReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
300
+ return ctrl .NewControllerManagedBy (mgr ).
301
+ For (& grafanav1beta1.GrafanaAlertRuleGroup {}).
302
+ WithEventFilter (ignoreStatusUpdates ()).
303
+ Complete (r )
304
+ }
0 commit comments