@@ -24,6 +24,7 @@ import (
24
24
"github.com/grafana/dskit/test"
25
25
"github.com/prometheus/alertmanager/cluster/clusterpb"
26
26
"github.com/prometheus/alertmanager/featurecontrol"
27
+ "github.com/prometheus/alertmanager/silence"
27
28
"github.com/prometheus/alertmanager/silence/silencepb"
28
29
"github.com/prometheus/alertmanager/types"
29
30
"github.com/prometheus/client_golang/prometheus"
@@ -330,17 +331,33 @@ func testLimiter(t *testing.T, limits Limits, ops []callbackOp) {
330
331
}
331
332
}
332
333
334
+ // cloneSilence returns a shallow copy of a silence. It is used in tests.
335
+ func cloneSilence (t * testing.T , sil * silencepb.Silence ) * silencepb.Silence {
336
+ t .Helper ()
337
+ s := * sil
338
+ return & s
339
+ }
340
+
341
+ func toMeshSilence (t * testing.T , sil * silencepb.Silence , retention time.Duration ) * silencepb.MeshSilence {
342
+ t .Helper ()
343
+ return & silencepb.MeshSilence {
344
+ Silence : sil ,
345
+ ExpiresAt : sil .EndsAt .Add (retention ),
346
+ }
347
+ }
348
+
333
349
func TestSilenceLimits (t * testing.T ) {
334
350
user := "test"
335
351
336
352
r := prometheus .NewPedanticRegistry ()
353
+ limits := mockAlertManagerLimits {
354
+ maxSilencesCount : 1 ,
355
+ maxSilenceSizeBytes : 2 << 11 , // 4KB,
356
+ }
337
357
am , err := New (& Config {
338
- UserID : user ,
339
- Logger : log .NewNopLogger (),
340
- Limits : & mockAlertManagerLimits {
341
- maxSilencesCount : 1 ,
342
- maxSilenceSizeBytes : 2 << 11 , // 4KB,
343
- },
358
+ UserID : user ,
359
+ Logger : log .NewNopLogger (),
360
+ Limits : & limits ,
344
361
Features : featurecontrol.NoopFlags {},
345
362
TenantDataDir : t .TempDir (),
346
363
ExternalURL : & url.URL {Path : "/am" },
@@ -364,38 +381,26 @@ func TestSilenceLimits(t *testing.T) {
364
381
StartsAt : time .Now (),
365
382
EndsAt : time .Now ().Add (5 * time .Minute ),
366
383
}
367
- id1 , err := am .silences .Set (sil1 )
368
- require .NoError (t , err )
369
- require .NotEqual (t , "" , id1 )
384
+ require .NoError (t , am .silences .Set (sil1 ))
370
385
371
- // Insert sil2 should fail because maximum number of silences
372
- // has been exceeded.
386
+ // Insert sil2 should fail because maximum number of silences has been
387
+ // exceeded.
373
388
sil2 := & silencepb.Silence {
374
- Matchers : []* silencepb.Matcher {{Name : "a " , Pattern : "b " }},
389
+ Matchers : []* silencepb.Matcher {{Name : "c " , Pattern : "d " }},
375
390
StartsAt : time .Now (),
376
391
EndsAt : time .Now ().Add (5 * time .Minute ),
377
392
}
378
- id2 , err := am .silences .Set (sil2 )
379
- require .EqualError (t , err , "exceeded maximum number of silences: 1 (limit: 1)" )
380
- require .Equal (t , "" , id2 )
393
+ require .EqualError (t , am .silences .Set (sil2 ), "exceeded maximum number of silences: 1 (limit: 1)" )
381
394
382
- // Expire sil1 and run the GC. This should allow sil2 to be
383
- // inserted.
384
- require .NoError (t , am .silences .Expire (id1 ))
395
+ // Expire sil1 and run the GC. This should allow sil2 to be inserted.
396
+ require .NoError (t , am .silences .Expire (sil1 .Id ))
385
397
n , err := am .silences .GC ()
386
398
require .NoError (t , err )
387
399
require .Equal (t , 1 , n )
400
+ require .NoError (t , am .silences .Set (sil2 ))
388
401
389
- id2 , err = am .silences .Set (sil2 )
390
- require .NoError (t , err )
391
- require .NotEqual (t , "" , id2 )
392
-
393
- // Should be able to update sil2 without hitting the limit.
394
- _ , err = am .silences .Set (sil2 )
395
- require .NoError (t , err )
396
-
397
- // Expire sil2.
398
- require .NoError (t , am .silences .Expire (id2 ))
402
+ // Expire sil2 and run the GC.
403
+ require .NoError (t , am .silences .Expire (sil2 .Id ))
399
404
n , err = am .silences .GC ()
400
405
require .NoError (t , err )
401
406
require .Equal (t , 1 , n )
@@ -404,25 +409,82 @@ func TestSilenceLimits(t *testing.T) {
404
409
sil3 := & silencepb.Silence {
405
410
Matchers : []* silencepb.Matcher {
406
411
{
407
- Name : strings .Repeat ("a " , 2 << 9 ),
408
- Pattern : strings .Repeat ("b " , 2 << 9 ),
412
+ Name : strings .Repeat ("e " , 2 << 9 ),
413
+ Pattern : strings .Repeat ("f " , 2 << 9 ),
409
414
},
410
415
{
411
- Name : strings .Repeat ("c " , 2 << 9 ),
412
- Pattern : strings .Repeat ("d " , 2 << 9 ),
416
+ Name : strings .Repeat ("g " , 2 << 9 ),
417
+ Pattern : strings .Repeat ("h " , 2 << 9 ),
413
418
},
414
419
},
415
- CreatedBy : strings .Repeat ("e " , 2 << 9 ),
416
- Comment : strings .Repeat ("f " , 2 << 9 ),
420
+ CreatedBy : strings .Repeat ("i " , 2 << 9 ),
421
+ Comment : strings .Repeat ("j " , 2 << 9 ),
417
422
StartsAt : time .Now (),
418
423
EndsAt : time .Now ().Add (5 * time .Minute ),
419
424
}
420
- id3 , err := am .silences .Set (sil3 )
421
- require .Error (t , err )
422
- // Do not check the exact size as it can change between consecutive runs
423
- // due to padding.
424
- require .Contains (t , err .Error (), "silence exceeded maximum size" )
425
- require .Equal (t , "" , id3 )
425
+ require .EqualError (t , am .silences .Set (sil3 ), fmt .Sprintf ("silence exceeded maximum size: %d bytes (limit: 4096 bytes)" , toMeshSilence (t , sil3 , 0 ).Size ()))
426
+
427
+ // Should be able to insert sil4.
428
+ sil4 := & silencepb.Silence {
429
+ Matchers : []* silencepb.Matcher {{Name : "k" , Pattern : "l" }},
430
+ StartsAt : time .Now (),
431
+ EndsAt : time .Now ().Add (5 * time .Minute ),
432
+ }
433
+ require .NoError (t , am .silences .Set (sil4 ))
434
+
435
+ // Should be able to update sil4 without modifications. It is expected to
436
+ // keep the same ID.
437
+ sil5 := cloneSilence (t , sil4 )
438
+ require .NoError (t , am .silences .Set (sil5 ))
439
+ require .Equal (t , sil4 .Id , sil5 .Id )
440
+
441
+ // Should be able to update the comment. It is also expected to keep the
442
+ // same ID.
443
+ sil6 := cloneSilence (t , sil5 )
444
+ sil6 .Comment = "m"
445
+ require .NoError (t , am .silences .Set (sil6 ))
446
+ require .Equal (t , sil5 .Id , sil6 .Id )
447
+
448
+ // Should not be able to update the start and end time as this requires
449
+ // sil6 to be expired and a new silence to be created. However, this would
450
+ // exceed the maximum number of silences, which counts both active and
451
+ // expired silences.
452
+ sil7 := cloneSilence (t , sil6 )
453
+ sil7 .StartsAt = time .Now ().Add (5 * time .Minute )
454
+ sil7 .EndsAt = time .Now ().Add (10 * time .Minute )
455
+ require .EqualError (t , am .silences .Set (sil7 ), "exceeded maximum number of silences: 1 (limit: 1)" )
456
+
457
+ // sil6 should not be expired because the update failed.
458
+ sils , _ , err := am .silences .Query (silence .QState (types .SilenceStateExpired ))
459
+ require .NoError (t , err )
460
+ require .Len (t , sils , 0 )
461
+
462
+ // Should not be able to update with a comment that exceeds maximum size.
463
+ // Need to increase the maximum number of silences to test this.
464
+ limits .maxSilencesCount = 2
465
+ sil8 := cloneSilence (t , sil6 )
466
+ sil8 .Comment = strings .Repeat ("m" , 2 << 11 )
467
+ require .EqualError (t , am .silences .Set (sil8 ), fmt .Sprintf ("silence exceeded maximum size: %d bytes (limit: 4096 bytes)" , toMeshSilence (t , sil8 , 0 ).Size ()))
468
+
469
+ // sil6 should not be expired because the update failed.
470
+ sils , _ , err = am .silences .Query (silence .QState (types .SilenceStateExpired ))
471
+ require .NoError (t , err )
472
+ require .Len (t , sils , 0 )
473
+
474
+ // Should not be able to replace with a silence that exceeds maximum size.
475
+ // This is different from the previous assertion as unlike when adding or
476
+ // updating a comment, changing the matchers for a silence should expire
477
+ // the existing silence, unless the silence that is replacing it exceeds
478
+ // limits, in which case the operation should fail and the existing silence
479
+ // should still be active.
480
+ sil9 := cloneSilence (t , sil8 )
481
+ sil9 .Matchers = []* silencepb.Matcher {{Name : "n" , Pattern : "o" }}
482
+ require .EqualError (t , am .silences .Set (sil9 ), fmt .Sprintf ("silence exceeded maximum size: %d bytes (limit: 4096 bytes)" , toMeshSilence (t , sil9 , 0 ).Size ()))
483
+
484
+ // sil6 should not be expired because the update failed.
485
+ sils , _ , err = am .silences .Query (silence .QState (types .SilenceStateExpired ))
486
+ require .NoError (t , err )
487
+ require .Len (t , sils , 0 )
426
488
}
427
489
428
490
func TestExperimentalReceiversAPI (t * testing.T ) {
0 commit comments