@@ -2,11 +2,37 @@ package notify
2
2
3
3
import (
4
4
"context"
5
+ "encoding/base64"
6
+ "encoding/json"
5
7
"errors"
8
+ "fmt"
9
+ "math/rand"
6
10
"net/url"
11
+ "strings"
7
12
"testing"
8
13
9
14
"github.com/stretchr/testify/require"
15
+
16
+ "github.com/grafana/alerting/receivers"
17
+ "github.com/grafana/alerting/receivers/alertmanager"
18
+ "github.com/grafana/alerting/receivers/dinding"
19
+ "github.com/grafana/alerting/receivers/discord"
20
+ "github.com/grafana/alerting/receivers/email"
21
+ "github.com/grafana/alerting/receivers/googlechat"
22
+ "github.com/grafana/alerting/receivers/kafka"
23
+ "github.com/grafana/alerting/receivers/line"
24
+ "github.com/grafana/alerting/receivers/opsgenie"
25
+ "github.com/grafana/alerting/receivers/pagerduty"
26
+ "github.com/grafana/alerting/receivers/pushover"
27
+ "github.com/grafana/alerting/receivers/sensugo"
28
+ "github.com/grafana/alerting/receivers/slack"
29
+ "github.com/grafana/alerting/receivers/teams"
30
+ "github.com/grafana/alerting/receivers/telegram"
31
+ "github.com/grafana/alerting/receivers/threema"
32
+ "github.com/grafana/alerting/receivers/victorops"
33
+ "github.com/grafana/alerting/receivers/webex"
34
+ "github.com/grafana/alerting/receivers/webhook"
35
+ "github.com/grafana/alerting/receivers/wecom"
10
36
)
11
37
12
38
func TestInvalidReceiverError_Error (t * testing.T ) {
@@ -78,3 +104,303 @@ func TestProcessNotifierError(t *testing.T) {
78
104
require .Equal (t , err , ProcessNotifierError (r , err ))
79
105
})
80
106
}
107
+
108
+ func TestBuildReceiverConfiguration (t * testing.T ) {
109
+ var decrypt receivers.GetDecryptedValueFn = func (ctx context.Context , sjd map [string ][]byte , key string , fallback string ) string {
110
+ v , ok := sjd [key ]
111
+ if ! ok {
112
+ return fallback
113
+ }
114
+ return string (v )
115
+ }
116
+
117
+ t .Run ("should decode secrets from base64" , func (t * testing.T ) {
118
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
119
+ for notifierType , cfg := range allKnownConfigs {
120
+ recCfg .Receivers = append (recCfg .Receivers , cfg .getRawNotifierConfig (notifierType ))
121
+ }
122
+ counter := 0
123
+ decryptCount := func (ctx context.Context , sjd map [string ][]byte , key string , fallback string ) string {
124
+ counter ++
125
+ return decrypt (ctx , sjd , key , fallback )
126
+ }
127
+ _ , _ = BuildReceiverConfiguration (context .Background (), recCfg , decryptCount )
128
+ require .Greater (t , counter , 0 )
129
+ })
130
+ t .Run ("should fail if at least one config is invalid" , func (t * testing.T ) {
131
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
132
+ for notifierType , cfg := range allKnownConfigs {
133
+ recCfg .Receivers = append (recCfg .Receivers , cfg .getRawNotifierConfig (notifierType ))
134
+ }
135
+ bad := & GrafanaReceiver {
136
+ UID : "invalid-test" ,
137
+ Name : "invalid-test" ,
138
+ Type : "slack" ,
139
+ Settings : json .RawMessage (`{ "test" : "test" }` ),
140
+ }
141
+ recCfg .Receivers = append (recCfg .Receivers , bad )
142
+
143
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
144
+ require .NotNil (t , err )
145
+ require .Equal (t , GrafanaReceiverConfig {}, parsed )
146
+ require .IsType (t , & ReceiverValidationError {}, err )
147
+ typedError := err .(* ReceiverValidationError )
148
+ require .NotNil (t , typedError .Cfg )
149
+ require .Equal (t , bad , typedError .Cfg )
150
+ require .ErrorContains (t , err , fmt .Sprintf (`failed to validate receiver "%s" of type "%s"` , bad .Name , bad .Type ))
151
+ })
152
+ t .Run ("should accept empty config" , func (t * testing.T ) {
153
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
154
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
155
+ require .NoError (t , err )
156
+ require .Equal (t , recCfg .Name , parsed .Name )
157
+ })
158
+ t .Run ("should fail if secrets decoding fails" , func (t * testing.T ) {
159
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
160
+ for notifierType , cfg := range allKnownConfigs {
161
+ notifierRaw := cfg .getRawNotifierConfig (notifierType )
162
+ if len (notifierRaw .SecureSettings ) == 0 {
163
+ continue
164
+ }
165
+ for key := range notifierRaw .SecureSettings {
166
+ notifierRaw .SecureSettings [key ] = "bad base-64"
167
+ }
168
+ recCfg .Receivers = append (recCfg .Receivers , notifierRaw )
169
+ }
170
+
171
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
172
+ require .NotNil (t , err )
173
+ require .Equal (t , GrafanaReceiverConfig {}, parsed )
174
+ require .IsType (t , & ReceiverValidationError {}, err )
175
+ typedError := err .(* ReceiverValidationError )
176
+ require .NotNil (t , typedError .Cfg )
177
+ require .ErrorContains (t , err , "failed to decode secure settings" )
178
+ })
179
+ t .Run ("should fail if notifier type is unknown" , func (t * testing.T ) {
180
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
181
+ for notifierType , cfg := range allKnownConfigs {
182
+ recCfg .Receivers = append (recCfg .Receivers , cfg .getRawNotifierConfig (notifierType ))
183
+ }
184
+ bad := & GrafanaReceiver {
185
+ UID : "test" ,
186
+ Name : "test" ,
187
+ Type : fmt .Sprintf ("invalid-%d" , rand .Uint32 ()),
188
+ Settings : json .RawMessage (`{ "test" : "test" }` ),
189
+ }
190
+ recCfg .Receivers = append (recCfg .Receivers , bad )
191
+
192
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
193
+ require .NotNil (t , err )
194
+ require .Equal (t , GrafanaReceiverConfig {}, parsed )
195
+ require .IsType (t , & ReceiverValidationError {}, err )
196
+ typedError := err .(* ReceiverValidationError )
197
+ require .NotNil (t , typedError .Cfg )
198
+ require .Equal (t , bad , typedError .Cfg )
199
+ require .ErrorContains (t , err , fmt .Sprintf ("notifier %s is not supported" , bad .Type ))
200
+ })
201
+ t .Run ("should recognize all known types" , func (t * testing.T ) {
202
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
203
+ for notifierType , cfg := range allKnownConfigs {
204
+ recCfg .Receivers = append (recCfg .Receivers , cfg .getRawNotifierConfig (notifierType ))
205
+ }
206
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
207
+ require .NoError (t , err )
208
+ require .Equal (t , recCfg .Name , parsed .Name )
209
+ require .Len (t , parsed .AlertmanagerConfigs , 1 )
210
+ require .Len (t , parsed .DingdingConfigs , 1 )
211
+ require .Len (t , parsed .DiscordConfigs , 1 )
212
+ require .Len (t , parsed .EmailConfigs , 1 )
213
+ require .Len (t , parsed .GooglechatConfigs , 1 )
214
+ require .Len (t , parsed .KafkaConfigs , 1 )
215
+ require .Len (t , parsed .LineConfigs , 1 )
216
+ require .Len (t , parsed .OpsgenieConfigs , 1 )
217
+ require .Len (t , parsed .PagerdutyConfigs , 1 )
218
+ require .Len (t , parsed .PushoverConfigs , 1 )
219
+ require .Len (t , parsed .SensugoConfigs , 1 )
220
+ require .Len (t , parsed .SlackConfigs , 1 )
221
+ require .Len (t , parsed .TeamsConfigs , 1 )
222
+ require .Len (t , parsed .TelegramConfigs , 1 )
223
+ require .Len (t , parsed .ThreemaConfigs , 1 )
224
+ require .Len (t , parsed .VictoropsConfigs , 1 )
225
+ require .Len (t , parsed .WebhookConfigs , 1 )
226
+ require .Len (t , parsed .WecomConfigs , 1 )
227
+ require .Len (t , parsed .WebexConfigs , 1 )
228
+
229
+ t .Run ("should populate metadata" , func (t * testing.T ) {
230
+ var all []receivers.Metadata
231
+ all = append (all , getMetadata (parsed .AlertmanagerConfigs )... )
232
+ all = append (all , getMetadata (parsed .DingdingConfigs )... )
233
+ all = append (all , getMetadata (parsed .DiscordConfigs )... )
234
+ all = append (all , getMetadata (parsed .EmailConfigs )... )
235
+ all = append (all , getMetadata (parsed .GooglechatConfigs )... )
236
+ all = append (all , getMetadata (parsed .KafkaConfigs )... )
237
+ all = append (all , getMetadata (parsed .LineConfigs )... )
238
+ all = append (all , getMetadata (parsed .OpsgenieConfigs )... )
239
+ all = append (all , getMetadata (parsed .PagerdutyConfigs )... )
240
+ all = append (all , getMetadata (parsed .PushoverConfigs )... )
241
+ all = append (all , getMetadata (parsed .SensugoConfigs )... )
242
+ all = append (all , getMetadata (parsed .SlackConfigs )... )
243
+ all = append (all , getMetadata (parsed .TeamsConfigs )... )
244
+ all = append (all , getMetadata (parsed .TelegramConfigs )... )
245
+ all = append (all , getMetadata (parsed .ThreemaConfigs )... )
246
+ all = append (all , getMetadata (parsed .VictoropsConfigs )... )
247
+ all = append (all , getMetadata (parsed .WebhookConfigs )... )
248
+ all = append (all , getMetadata (parsed .WecomConfigs )... )
249
+ all = append (all , getMetadata (parsed .WebexConfigs )... )
250
+
251
+ for idx , meta := range all {
252
+ require .NotEmptyf (t , meta .Type , "%s notifier (idx: %d) '%s' uid: '%s'." , meta .Type , idx , meta .Name , meta .UID )
253
+ require .NotEmptyf (t , meta .UID , "%s notifier (idx: %d) '%s' uid: '%s'." , meta .Type , idx , meta .Name , meta .UID )
254
+ require .NotEmptyf (t , meta .Name , "%s notifier (idx: %d) '%s' uid: '%s'." , meta .Type , idx , meta .Name , meta .UID )
255
+ var notifierRaw * GrafanaReceiver
256
+ for _ , receiver := range recCfg .Receivers {
257
+ if receiver .Type == meta .Type && receiver .UID == meta .UID && receiver .Name == meta .Name {
258
+ notifierRaw = receiver
259
+ break
260
+ }
261
+ }
262
+ require .NotNilf (t , notifierRaw , "cannot find raw settings for %s notifier '%s' uid: '%s'." , meta .Type , meta .Name , meta .UID )
263
+ require .Equalf (t , notifierRaw .DisableResolveMessage , meta .DisableResolveMessage , "%s notifier '%s' uid: '%s'." , meta .Type , meta .Name , meta .UID )
264
+ }
265
+ })
266
+ })
267
+ t .Run ("should recognize type in any case" , func (t * testing.T ) {
268
+ recCfg := & APIReceiver {ConfigReceiver : ConfigReceiver {Name : "test-receiver" }}
269
+ for notifierType , cfg := range allKnownConfigs {
270
+ notifierRaw := cfg .getRawNotifierConfig (notifierType )
271
+ notifierRaw .Type = strings .ToUpper (notifierRaw .Type )
272
+ recCfg .Receivers = append (recCfg .Receivers , cfg .getRawNotifierConfig (notifierType ))
273
+ }
274
+ parsed , err := BuildReceiverConfiguration (context .Background (), recCfg , decrypt )
275
+ require .NoError (t , err )
276
+ require .Len (t , parsed .AlertmanagerConfigs , 1 )
277
+ require .Len (t , parsed .DingdingConfigs , 1 )
278
+ require .Len (t , parsed .DiscordConfigs , 1 )
279
+ require .Len (t , parsed .EmailConfigs , 1 )
280
+ require .Len (t , parsed .GooglechatConfigs , 1 )
281
+ require .Len (t , parsed .KafkaConfigs , 1 )
282
+ require .Len (t , parsed .LineConfigs , 1 )
283
+ require .Len (t , parsed .OpsgenieConfigs , 1 )
284
+ require .Len (t , parsed .PagerdutyConfigs , 1 )
285
+ require .Len (t , parsed .PushoverConfigs , 1 )
286
+ require .Len (t , parsed .SensugoConfigs , 1 )
287
+ require .Len (t , parsed .SlackConfigs , 1 )
288
+ require .Len (t , parsed .TeamsConfigs , 1 )
289
+ require .Len (t , parsed .TelegramConfigs , 1 )
290
+ require .Len (t , parsed .ThreemaConfigs , 1 )
291
+ require .Len (t , parsed .VictoropsConfigs , 1 )
292
+ require .Len (t , parsed .WebhookConfigs , 1 )
293
+ require .Len (t , parsed .WecomConfigs , 1 )
294
+ require .Len (t , parsed .WebexConfigs , 1 )
295
+
296
+ })
297
+ }
298
+
299
+ func getMetadata [T any ](notifiers []* NotifierConfig [T ]) []receivers.Metadata {
300
+ result := make ([]receivers.Metadata , 0 , len (notifiers ))
301
+ for _ , notifier := range notifiers {
302
+ result = append (result , notifier .Metadata )
303
+ }
304
+ return result
305
+ }
306
+
307
+ var allKnownConfigs = map [string ]notifierConfigTest {
308
+ "prometheus-alertmanager" : {
309
+ notifierType : "prometheus-alertmanager" ,
310
+ config : alertmanager .FullValidConfigForTesting ,
311
+ secrets : alertmanager .FullValidSecretsForTesting ,
312
+ },
313
+ "dingding" : {notifierType : "dingding" ,
314
+ config : dinding .FullValidConfigForTesting ,
315
+ },
316
+ "discord" : {notifierType : "discord" ,
317
+ config : discord .FullValidConfigForTesting ,
318
+ },
319
+ "email" : {notifierType : "email" ,
320
+ config : email .FullValidConfigForTesting ,
321
+ },
322
+ "googlechat" : {notifierType : "googlechat" ,
323
+ config : googlechat .FullValidConfigForTesting ,
324
+ },
325
+ "kafka" : {notifierType : "kafka" ,
326
+ config : kafka .FullValidConfigForTesting ,
327
+ secrets : kafka .FullValidSecretsForTesting ,
328
+ },
329
+ "line" : {notifierType : "line" ,
330
+ config : line .FullValidConfigForTesting ,
331
+ secrets : line .FullValidSecretsForTesting ,
332
+ },
333
+ "opsgenie" : {notifierType : "opsgenie" ,
334
+ config : opsgenie .FullValidConfigForTesting ,
335
+ secrets : opsgenie .FullValidSecretsForTesting ,
336
+ },
337
+ "pagerduty" : {notifierType : "pagerduty" ,
338
+ config : pagerduty .FullValidConfigForTesting ,
339
+ secrets : pagerduty .FullValidSecretsForTesting ,
340
+ },
341
+ "pushover" : {notifierType : "pushover" ,
342
+ config : pushover .FullValidConfigForTesting ,
343
+ secrets : pushover .FullValidSecretsForTesting ,
344
+ },
345
+ "sensugo" : {notifierType : "sensugo" ,
346
+ config : sensugo .FullValidConfigForTesting ,
347
+ secrets : sensugo .FullValidSecretsForTesting ,
348
+ },
349
+ "slack" : {notifierType : "slack" ,
350
+ config : slack .FullValidConfigForTesting ,
351
+ secrets : slack .FullValidSecretsForTesting ,
352
+ },
353
+ "teams" : {notifierType : "teams" ,
354
+ config : teams .FullValidConfigForTesting ,
355
+ },
356
+ "telegram" : {notifierType : "telegram" ,
357
+ config : telegram .FullValidConfigForTesting ,
358
+ secrets : telegram .FullValidSecretsForTesting ,
359
+ },
360
+ "threema" : {notifierType : "threema" ,
361
+ config : threema .FullValidConfigForTesting ,
362
+ secrets : threema .FullValidSecretsForTesting ,
363
+ },
364
+ "victorops" : {notifierType : "victorops" ,
365
+ config : victorops .FullValidConfigForTesting ,
366
+ },
367
+ "webhook" : {notifierType : "webhook" ,
368
+ config : webhook .FullValidConfigForTesting ,
369
+ secrets : webhook .FullValidSecretsForTesting ,
370
+ },
371
+ "wecom" : {notifierType : "wecom" ,
372
+ config : wecom .FullValidConfigForTesting ,
373
+ secrets : wecom .FullValidSecretsForTesting ,
374
+ },
375
+ "webex" : {notifierType : "webex" ,
376
+ config : webex .FullValidConfigForTesting ,
377
+ secrets : webex .FullValidSecretsForTesting ,
378
+ },
379
+ }
380
+
381
+ type notifierConfigTest struct {
382
+ notifierType string
383
+ config string
384
+ secrets string
385
+ }
386
+
387
+ func (n notifierConfigTest ) getRawNotifierConfig (name string ) * GrafanaReceiver {
388
+ secrets := make (map [string ]string )
389
+ if n .secrets != "" {
390
+ err := json .Unmarshal ([]byte (n .secrets ), & secrets )
391
+ if err != nil {
392
+ panic (err )
393
+ }
394
+ for key , value := range secrets {
395
+ secrets [key ] = base64 .StdEncoding .EncodeToString ([]byte (value ))
396
+ }
397
+ }
398
+ return & GrafanaReceiver {
399
+ UID : fmt .Sprintf ("%s-%d" , name , rand .Uint32 ()),
400
+ Name : name ,
401
+ Type : n .notifierType ,
402
+ DisableResolveMessage : rand .Int ()% 2 == 0 ,
403
+ Settings : json .RawMessage (n .config ),
404
+ SecureSettings : secrets ,
405
+ }
406
+ }
0 commit comments