@ -6,6 +6,8 @@ import (
"testing"
"time"
alertingNotify "github.com/grafana/alerting/notify"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/require"
@ -22,18 +24,7 @@ import (
)
func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgs ( t * testing . T ) {
configStore := NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration { } )
orgStore := & FakeOrgStore {
orgs : [ ] int64 { 1 , 2 , 3 } ,
}
tmpDir := t . TempDir ( )
kvStore := ngfakes . NewFakeKVStore ( t )
provStore := ngfakes . NewFakeProvisioningStore ( )
secretsService := secretsManager . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
decryptFn := secretsService . GetDecryptedValue
reg := prometheus . NewPedanticRegistry ( )
m := metrics . NewNGAlert ( reg )
cfg := & setting . Cfg {
DataPath : tmpDir ,
UnifiedAlerting : setting . UnifiedAlertingSettings {
@ -42,8 +33,9 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgs(t *testing.T) {
DisabledOrgs : map [ int64 ] struct { } { 5 : { } } ,
} , // do not poll in tests.
}
mam , err := NewMultiOrgAlertmanager ( cfg , configStore , orgStore , kvStore , provStore , decryptFn , m . GetMultiOrgAlertmanagerMetrics ( ) , nil , log . New ( "testlogger" ) , secretsService , featuremgmt . WithFeatures ( ) )
require . NoError ( t , err )
mam := setupMam ( t , cfg )
reg := mam . metrics . Registerer . ( * prometheus . Registry )
orgStore := mam . orgStore . ( * FakeOrgStore )
ctx := context . Background ( )
// Ensure that one Alertmanager is created per org.
@ -61,7 +53,7 @@ grafana_alerting_discovered_configurations 3
// Configurations should be marked as successfully applied.
for _ , org := range orgStore . orgs {
configs , err := configStore . GetAppliedConfigurations ( ctx , org , 10 )
configs , err := mam . configStore . GetAppliedConfigurations ( ctx , org , 10 )
require . NoError ( t , err )
require . Len ( t , configs , 1 )
}
@ -106,58 +98,40 @@ grafana_alerting_discovered_configurations 4
orgID := int64 ( 6 )
// Populate the kvstore with orphaned records.
err = kvStore . Set ( ctx , orgID , KVNamespace , SilencesFilename , "file_1" )
err := mam . kvStore . Set ( ctx , orgID , KVNamespace , SilencesFilename , "file_1" )
require . NoError ( t , err )
err = kvStore . Set ( ctx , orgID , KVNamespace , NotificationLogFilename , "file_1" )
err = mam . kvStore . Set ( ctx , orgID , KVNamespace , NotificationLogFilename , "file_1" )
require . NoError ( t , err )
// Now re run the sync job once.
require . NoError ( t , mam . LoadAndSyncAlertmanagersForOrgs ( ctx ) )
// The organization kvstore records should be gone by now.
_ , exists , _ := kvStore . Get ( ctx , orgID , KVNamespace , SilencesFilename )
_ , exists , _ := mam . kvStore . Get ( ctx , orgID , KVNamespace , SilencesFilename )
require . False ( t , exists )
_ , exists , _ = kvStore . Get ( ctx , orgID , KVNamespace , NotificationLogFilename )
_ , exists , _ = mam . kvStore . Get ( ctx , orgID , KVNamespace , NotificationLogFilename )
require . False ( t , exists )
}
}
func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures ( t * testing . T ) {
mam := setupMam ( t , nil )
ctx := context . Background ( )
// Include a broken configuration for organization 2.
var orgWithBadConfig int64 = 2
configStore := NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration {
mam . configStore = NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration {
2 : { AlertmanagerConfiguration : brokenConfig , OrgID : orgWithBadConfig } ,
} )
orgs := [ ] int64 { 1 , 2 , 3 }
orgStore := & FakeOrgStore {
orgs : orgs ,
}
tmpDir := t . TempDir ( )
kvStore := ngfakes . NewFakeKVStore ( t )
provStore := ngfakes . NewFakeProvisioningStore ( )
secretsService := secretsManager . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
decryptFn := secretsService . GetDecryptedValue
reg := prometheus . NewPedanticRegistry ( )
m := metrics . NewNGAlert ( reg )
cfg := & setting . Cfg {
DataPath : tmpDir ,
UnifiedAlerting : setting . UnifiedAlertingSettings {
AlertmanagerConfigPollInterval : 10 * time . Minute ,
DefaultConfiguration : setting . GetAlertmanagerDefaultConfiguration ( ) ,
} , // do not poll in tests.
}
mam , err := NewMultiOrgAlertmanager ( cfg , configStore , orgStore , kvStore , provStore , decryptFn , m . GetMultiOrgAlertmanagerMetrics ( ) , nil , log . New ( "testlogger" ) , secretsService , featuremgmt . WithFeatures ( ) )
orgs , err := mam . orgStore . GetOrgs ( ctx )
require . NoError ( t , err )
ctx := context . Background ( )
// No successfully applied configurations should be found at first.
{
for _ , org := range orgs {
configs , err := configStore . GetAppliedConfigurations ( ctx , org , 10 )
configs , err := mam . configStore . GetAppliedConfigurations ( ctx , org , 10 )
require . NoError ( t , err )
require . Len ( t , configs , 0 )
}
@ -173,7 +147,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
// Configurations should be marked as successfully applied for all orgs except for org 2.
for _ , org := range orgs {
configs , err := configStore . GetAppliedConfigurations ( ctx , org , 10 )
configs , err := mam . configStore . GetAppliedConfigurations ( ctx , org , 10 )
require . NoError ( t , err )
if org == orgWithBadConfig {
require . Len ( t , configs , 0 )
@ -193,7 +167,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
// The configuration should still be marked as successfully applied for all orgs except for org 2.
for _ , org := range orgs {
configs , err := configStore . GetAppliedConfigurations ( ctx , org , 10 )
configs , err := mam . configStore . GetAppliedConfigurations ( ctx , org , 10 )
require . NoError ( t , err )
if org == orgWithBadConfig {
require . Len ( t , configs , 0 )
@ -205,7 +179,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
// If we fix the configuration, it becomes ready.
{
configStore . configs = map [ int64 ] * models . AlertConfiguration { } // It'll apply the default config.
mam . configStore . ( * fakeConfigStore ) . configs = map [ int64 ] * models . AlertConfiguration { } // It'll apply the default config.
require . NoError ( t , mam . LoadAndSyncAlertmanagersForOrgs ( ctx ) )
require . Len ( t , mam . alertmanagers , 3 )
require . True ( t , mam . alertmanagers [ 1 ] . Ready ( ) )
@ -214,7 +188,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
// All configurations should be marked as successfully applied.
for _ , org := range orgs {
configs , err := configStore . GetAppliedConfigurations ( ctx , org , 10 )
configs , err := mam . configStore . GetAppliedConfigurations ( ctx , org , 10 )
require . NoError ( t , err )
require . NotEqual ( t , 0 , len ( configs ) )
}
@ -222,23 +196,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
}
func TestMultiOrgAlertmanager_AlertmanagerFor ( t * testing . T ) {
configStore := NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration { } )
orgStore := & FakeOrgStore {
orgs : [ ] int64 { 1 , 2 , 3 } ,
}
tmpDir := t . TempDir ( )
cfg := & setting . Cfg {
DataPath : tmpDir ,
UnifiedAlerting : setting . UnifiedAlertingSettings { AlertmanagerConfigPollInterval : 3 * time . Minute , DefaultConfiguration : setting . GetAlertmanagerDefaultConfiguration ( ) } , // do not poll in tests.
}
kvStore := ngfakes . NewFakeKVStore ( t )
provStore := ngfakes . NewFakeProvisioningStore ( )
secretsService := secretsManager . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
decryptFn := secretsService . GetDecryptedValue
reg := prometheus . NewPedanticRegistry ( )
m := metrics . NewNGAlert ( reg )
mam , err := NewMultiOrgAlertmanager ( cfg , configStore , orgStore , kvStore , provStore , decryptFn , m . GetMultiOrgAlertmanagerMetrics ( ) , nil , log . New ( "testlogger" ) , secretsService , featuremgmt . WithFeatures ( ) )
require . NoError ( t , err )
mam := setupMam ( t , nil )
ctx := context . Background ( )
// Ensure that one Alertmanagers is created per org.
@ -264,7 +222,7 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) {
}
// Let's now remove the previous queried organization.
orgStore . orgs = [ ] int64 { 1 , 3 }
mam . orgStore . ( * FakeOrgStore ) . orgs = [ ] int64 { 1 , 3 }
require . NoError ( t , mam . LoadAndSyncAlertmanagersForOrgs ( ctx ) )
{
_ , err := mam . AlertmanagerFor ( 2 )
@ -273,24 +231,7 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) {
}
func TestMultiOrgAlertmanager_ActivateHistoricalConfiguration ( t * testing . T ) {
configStore := NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration { } )
orgStore := & FakeOrgStore {
orgs : [ ] int64 { 1 , 2 , 3 } ,
}
tmpDir := t . TempDir ( )
defaultConfig := ` { "template_files":null,"alertmanager_config": { "route": { "receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"templates":null,"receivers":[ { "name":"grafana-default-email","grafana_managed_receiver_configs":[ { "uid":"","name":"email receiver","type":"email","disableResolveMessage":false,"settings": { "addresses":"\u003cexample@email.com\u003e"},"secureSettings":null}]}]}} `
cfg := & setting . Cfg {
DataPath : tmpDir ,
UnifiedAlerting : setting . UnifiedAlertingSettings { AlertmanagerConfigPollInterval : 3 * time . Minute , DefaultConfiguration : defaultConfig } , // do not poll in tests.
}
kvStore := ngfakes . NewFakeKVStore ( t )
provStore := ngfakes . NewFakeProvisioningStore ( )
secretsService := secretsManager . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
decryptFn := secretsService . GetDecryptedValue
reg := prometheus . NewPedanticRegistry ( )
m := metrics . NewNGAlert ( reg )
mam , err := NewMultiOrgAlertmanager ( cfg , configStore , orgStore , kvStore , provStore , decryptFn , m . GetMultiOrgAlertmanagerMetrics ( ) , nil , log . New ( "testlogger" ) , secretsService , featuremgmt . WithFeatures ( ) )
require . NoError ( t , err )
mam := setupMam ( t , nil )
ctx := context . Background ( )
// Ensure that one Alertmanager is created per org.
@ -339,9 +280,141 @@ func TestMultiOrgAlertmanager_ActivateHistoricalConfiguration(t *testing.T) {
// Verify that the org has the old default config.
cfgs , err = mam . getLatestConfigs ( ctx )
require . NoError ( t , err )
require . Equal ( t , defaultConfig , cfgs [ 2 ] . AlertmanagerConfiguration )
require . JSON Eq( t , defaultConfig , cfgs [ 2 ] . AlertmanagerConfiguration )
}
func TestMultiOrgAlertmanager_Silences ( t * testing . T ) {
mam := setupMam ( t , nil )
ctx := context . Background ( )
// Ensure that one Alertmanager is created per org.
{
require . NoError ( t , mam . LoadAndSyncAlertmanagersForOrgs ( ctx ) )
require . Len ( t , mam . alertmanagers , 3 )
}
am , err := mam . AlertmanagerFor ( 1 )
require . NoError ( t , err )
// Confirm no silences.
silences , err := am . ListSilences ( ctx , [ ] string { } )
require . NoError ( t , err )
require . Len ( t , silences , 0 )
// Confirm empty state.
state , err := am . SilenceState ( ctx )
require . NoError ( t , err )
require . Len ( t , state , 0 )
// Confirm empty kvstore.
v , ok , err := mam . kvStore . Get ( ctx , 1 , KVNamespace , SilencesFilename )
require . NoError ( t , err )
require . False ( t , ok )
require . Empty ( t , v )
// Create 2 silences.
sid , err := mam . CreateSilence ( ctx , 1 , GenSilence ( "test" ) )
require . NoError ( t , err )
require . NotEmpty ( t , sid )
sid2 , err := mam . CreateSilence ( ctx , 1 , GenSilence ( "test" ) )
require . NoError ( t , err )
require . NotEmpty ( t , sid2 )
// Confirm 2 silences.
silences , err = am . ListSilences ( ctx , [ ] string { } )
require . NoError ( t , err )
require . Len ( t , silences , 2 )
// Confirm 2 states.
state , err = am . SilenceState ( ctx )
require . NoError ( t , err )
require . Len ( t , state , 2 )
// Confirm 2 silences in the kvstore.
v , ok , err = mam . kvStore . Get ( ctx , 1 , KVNamespace , SilencesFilename )
require . NoError ( t , err )
require . True ( t , ok )
decoded , err := decode ( v )
require . NoError ( t , err )
state , err = alertingNotify . DecodeState ( bytes . NewReader ( decoded ) )
require . NoError ( t , err )
require . Len ( t , state , 2 )
// Delete silence.
err = mam . DeleteSilence ( ctx , 1 , sid )
require . NoError ( t , err )
// Confirm silence is expired in memory.
silence , err := am . GetSilence ( ctx , sid )
require . NoError ( t , err )
require . EqualValues ( t , types . SilenceStateExpired , * silence . Status . State )
// Confirm silence is expired in kvstore.
v , ok , err = mam . kvStore . Get ( ctx , 1 , KVNamespace , SilencesFilename )
require . NoError ( t , err )
require . True ( t , ok )
decoded , err = decode ( v )
require . NoError ( t , err )
state , err = alertingNotify . DecodeState ( bytes . NewReader ( decoded ) )
require . NoError ( t , err )
require . True ( t , time . Now ( ) . After ( state [ sid ] . Silence . EndsAt ) ) // Expired.
}
func setupMam ( t * testing . T , cfg * setting . Cfg ) * MultiOrgAlertmanager {
if cfg == nil {
tmpDir := t . TempDir ( )
cfg = & setting . Cfg {
DataPath : tmpDir ,
UnifiedAlerting : setting . UnifiedAlertingSettings { AlertmanagerConfigPollInterval : 3 * time . Minute , DefaultConfiguration : defaultConfig } , // do not poll in tests.
}
}
cs := NewFakeConfigStore ( t , map [ int64 ] * models . AlertConfiguration { } )
orgStore := & FakeOrgStore {
orgs : [ ] int64 { 1 , 2 , 3 } ,
}
kvStore := ngfakes . NewFakeKVStore ( t )
provStore := ngfakes . NewFakeProvisioningStore ( )
secretsService := secretsManager . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
decryptFn := secretsService . GetDecryptedValue
reg := prometheus . NewPedanticRegistry ( )
m := metrics . NewNGAlert ( reg )
mam , err := NewMultiOrgAlertmanager ( cfg , cs , orgStore , kvStore , provStore , decryptFn , m . GetMultiOrgAlertmanagerMetrics ( ) , nil , log . New ( "testlogger" ) , secretsService , featuremgmt . WithFeatures ( ) )
require . NoError ( t , err )
return mam
}
var defaultConfig = `
{
"template_files" : null ,
"alertmanager_config" : {
"route" : {
"receiver" : "grafana-default-email" ,
"group_by" : [
"grafana_folder" ,
"alertname"
]
} ,
"templates" : null ,
"receivers" : [
{
"name" : "grafana-default-email" ,
"grafana_managed_receiver_configs" : [
{
"uid" : "" ,
"name" : "email receiver" ,
"type" : "email" ,
"disableResolveMessage" : false ,
"settings" : {
"addresses" : "\u003cexample@email.com\u003e" } ,
"secureSettings" : null
}
]
}
]
}
} `
var brokenConfig = `
"alertmanager_config" : {
"route" : {