2016-06-11 10:13:33 +02:00
package alerting
2016-04-18 14:15:03 +02:00
import (
2021-11-05 09:41:24 +01:00
"context"
2022-08-10 13:37:51 +00:00
"os"
2016-04-18 14:15:03 +02:00
"testing"
2018-11-05 13:14:02 +01:00
"time"
2016-04-18 14:15:03 +02:00
2022-06-27 12:23:15 -04:00
"github.com/stretchr/testify/require"
2016-04-18 14:15:03 +02:00
"github.com/grafana/grafana/pkg/components/simplejson"
2023-01-18 16:01:25 +01:00
"github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/localcache"
2023-01-23 08:19:25 -05:00
"github.com/grafana/grafana/pkg/services/alerting/models"
2023-01-16 16:33:55 +01:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-02-28 09:54:56 +01:00
"github.com/grafana/grafana/pkg/services/datasources"
2023-08-21 14:26:49 +01:00
"github.com/grafana/grafana/pkg/services/datasources/guardian"
2023-04-06 11:16:15 +03:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2023-01-18 16:01:25 +01:00
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
2016-04-18 14:15:03 +02:00
)
2016-06-11 10:13:33 +02:00
func TestAlertRuleExtraction ( t * testing . T ) {
2021-04-12 15:53:51 +03:00
RegisterCondition ( "query" , func ( model * simplejson . Json , index int ) ( Condition , error ) {
return & FakeCondition { } , nil
} )
// mock data
2023-02-02 17:22:43 +01:00
defaultDs := & datasources . DataSource { ID : 12 , OrgID : 1 , Name : "I am default" , IsDefault : true , UID : "def-uid" }
graphite2Ds := & datasources . DataSource { ID : 15 , OrgID : 1 , Name : "graphite2" , UID : "graphite2-uid" }
2021-04-12 15:53:51 +03:00
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/graphite-alert.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
2023-08-21 14:26:49 +01:00
dsGuardian := guardian . ProvideGuardian ( )
2022-02-28 09:54:56 +01:00
dsService := & fakeDatasourceService { ExpectedDatasource : defaultDs }
2023-01-18 16:01:25 +01:00
db := dbtest . NewFakeDB ( )
2023-04-06 11:16:15 +03:00
cfg := & setting . Cfg { }
store := ProvideAlertStore ( db , localcache . ProvideService ( ) , cfg , nil , featuremgmt . WithFeatures ( ) )
2023-08-21 14:26:49 +01:00
extractor := ProvideDashAlertExtractorService ( dsGuardian , dsService , store )
2022-02-28 09:54:56 +01:00
2021-04-12 15:53:51 +03:00
t . Run ( "Parsing alert rules from dashboard json" , func ( t * testing . T ) {
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
getTarget := func ( j * simplejson . Json ) string {
rowObj := j . Get ( "rows" ) . MustArray ( ) [ 0 ]
row := simplejson . NewFromAny ( rowObj )
panelObj := row . Get ( "panels" ) . MustArray ( ) [ 0 ]
panel := simplejson . NewFromAny ( panelObj )
conditionObj := panel . Get ( "alert" ) . Get ( "conditions" ) . MustArray ( ) [ 0 ]
condition := simplejson . NewFromAny ( conditionObj )
return condition . Get ( "query" ) . Get ( "model" ) . Get ( "target" ) . MustString ( )
}
require . Equal ( t , getTarget ( dashJSON ) , "" )
2022-02-28 09:54:56 +01:00
_ , _ = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Equal ( t , getTarget ( dashJSON ) , "" )
} )
t . Run ( "Parsing and validating dashboard containing graphite alerts" , func ( t * testing . T ) {
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2023-02-02 17:22:43 +01:00
dsService . ExpectedDatasource = & datasources . DataSource { ID : 12 }
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
require . Len ( t , alerts , 2 )
for _ , v := range alerts {
2023-02-02 17:22:43 +01:00
require . EqualValues ( t , v . DashboardID , 57 )
2021-04-12 15:53:51 +03:00
require . NotEmpty ( t , v . Name )
require . NotEmpty ( t , v . Message )
settings := simplejson . NewFromAny ( v . Settings )
require . Equal ( t , settings . Get ( "interval" ) . MustString ( "" ) , "" )
}
require . EqualValues ( t , alerts [ 0 ] . Handler , 1 )
require . EqualValues ( t , alerts [ 1 ] . Handler , 0 )
require . EqualValues ( t , alerts [ 0 ] . Frequency , 60 )
require . EqualValues ( t , alerts [ 1 ] . Frequency , 60 )
2023-02-02 17:22:43 +01:00
require . EqualValues ( t , alerts [ 0 ] . PanelID , 3 )
require . EqualValues ( t , alerts [ 1 ] . PanelID , 4 )
2021-04-12 15:53:51 +03:00
require . Equal ( t , alerts [ 0 ] . For , time . Minute * 2 )
require . Equal ( t , alerts [ 1 ] . For , time . Duration ( 0 ) )
require . Equal ( t , alerts [ 0 ] . Name , "name1" )
require . Equal ( t , alerts [ 0 ] . Message , "desc1" )
require . Equal ( t , alerts [ 1 ] . Name , "name2" )
require . Equal ( t , alerts [ 1 ] . Message , "desc2" )
condition := simplejson . NewFromAny ( alerts [ 0 ] . Settings . Get ( "conditions" ) . MustArray ( ) [ 0 ] )
query := condition . Get ( "query" )
require . EqualValues ( t , query . Get ( "datasourceId" ) . MustInt64 ( ) , 12 )
condition = simplejson . NewFromAny ( alerts [ 0 ] . Settings . Get ( "conditions" ) . MustArray ( ) [ 0 ] )
model := condition . Get ( "query" ) . Get ( "model" )
require . Equal ( t , model . Get ( "target" ) . MustString ( ) , "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)" )
} )
t . Run ( "Panels missing id should return error" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
panelWithoutID , err := os . ReadFile ( "./testdata/panels-missing-id.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithoutID )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
_ , err = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . NotNil ( t , err )
} )
t . Run ( "Panels missing id should return error" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
panelWithIDZero , err := os . ReadFile ( "./testdata/panel-with-id-0.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithIDZero )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
_ , err = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . NotNil ( t , err )
2021-12-02 07:41:24 -08:00
} )
t . Run ( "Cannot save panel with query that is referenced by legacy alerting" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
panelWithQuery , err := os . ReadFile ( "./testdata/panel-with-bad-query-id.json" )
2021-12-02 07:41:24 -08:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithQuery )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
_ , err = extractor . GetAlerts ( WithUAEnabled ( context . Background ( ) , true ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-12-02 07:41:24 -08:00
require . Equal ( t , "alert validation error: Alert on PanelId: 2 refers to query(B) that cannot be found. Legacy alerting queries are not able to be removed at this time in order to preserve the ability to rollback to previous versions of Grafana" , err . Error ( ) )
2021-04-12 15:53:51 +03:00
} )
t . Run ( "Panel does not have datasource configured, use the default datasource" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
panelWithoutSpecifiedDatasource , err := os . ReadFile ( "./testdata/panel-without-specified-datasource.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithoutSpecifiedDatasource )
require . Nil ( t , err )
2023-02-02 17:22:43 +01:00
dsService . ExpectedDatasource = & datasources . DataSource { ID : 12 }
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
condition := simplejson . NewFromAny ( alerts [ 0 ] . Settings . Get ( "conditions" ) . MustArray ( ) [ 0 ] )
query := condition . Get ( "query" )
require . EqualValues ( t , query . Get ( "datasourceId" ) . MustInt64 ( ) , 12 )
} )
t . Run ( "Parse alerts from dashboard without rows" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/v5-dashboard.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
require . Len ( t , alerts , 2 )
} )
t . Run ( "Alert notifications are in DB" , func ( t * testing . T ) {
2023-01-18 16:01:25 +01:00
sqlStore := sqlStore { db : sqlstore . InitTestDB ( t ) }
2021-10-07 16:33:50 +02:00
2023-02-03 15:46:55 +01:00
firstNotification := models . CreateAlertNotificationCommand { UID : "notifier1" , OrgID : 1 , Name : "1" }
2023-02-02 09:41:05 +01:00
_ , err = sqlStore . CreateAlertNotificationCommand ( context . Background ( ) , & firstNotification )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
2021-10-07 16:33:50 +02:00
2023-02-03 15:46:55 +01:00
secondNotification := models . CreateAlertNotificationCommand { UID : "notifier2" , OrgID : 1 , Name : "2" }
2023-02-02 09:41:05 +01:00
_ , err = sqlStore . CreateAlertNotificationCommand ( context . Background ( ) , & secondNotification )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/influxdb-alert.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
require . Len ( t , alerts , 1 )
for _ , alert := range alerts {
2023-02-02 17:22:43 +01:00
require . EqualValues ( t , alert . DashboardID , 4 )
2021-04-12 15:53:51 +03:00
conditions := alert . Settings . Get ( "conditions" ) . MustArray ( )
cond := simplejson . NewFromAny ( conditions [ 0 ] )
require . Equal ( t , cond . Get ( "query" ) . Get ( "model" ) . Get ( "interval" ) . MustString ( ) , ">10s" )
}
} )
t . Run ( "Should be able to extract collapsed panels" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/collapsed-panels.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2023-01-16 16:33:55 +01:00
dash := dashboards . NewDashboardFromJson ( dashJSON )
2021-04-12 15:53:51 +03:00
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : dash ,
OrgID : 1 ,
} )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
require . Len ( t , alerts , 4 )
} )
t . Run ( "Parse and validate dashboard without id and containing an alert" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/dash-without-id.json" )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2018-12-20 17:12:47 +02:00
2022-02-28 09:54:56 +01:00
dashAlertInfo := DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
}
2018-03-13 22:23:37 +01:00
2022-02-28 09:54:56 +01:00
err = extractor . ValidateAlerts ( context . Background ( ) , dashAlertInfo )
2021-04-12 15:53:51 +03:00
require . Nil ( t , err )
2018-03-13 22:23:37 +01:00
2022-02-28 09:54:56 +01:00
_ , err = extractor . GetAlerts ( context . Background ( ) , dashAlertInfo )
2021-04-12 15:53:51 +03:00
require . Equal ( t , err . Error ( ) , "alert validation error: Panel id is not correct, alertName=Influxdb, panelId=1" )
2016-04-18 14:15:03 +02:00
} )
2021-10-29 10:57:24 -07:00
t . Run ( "Extract data source given new DataSourceRef object model" , func ( t * testing . T ) {
2022-08-10 13:37:51 +00:00
json , err := os . ReadFile ( "./testdata/panel-with-datasource-ref.json" )
2021-10-29 10:57:24 -07:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
dsService . ExpectedDatasource = graphite2Ds
dashAlertInfo := DashAlertInfo {
User : nil ,
2023-01-16 16:33:55 +01:00
Dash : dashboards . NewDashboardFromJson ( dashJSON ) ,
2022-02-28 09:54:56 +01:00
OrgID : 1 ,
}
2021-10-29 10:57:24 -07:00
2022-02-28 09:54:56 +01:00
err = extractor . ValidateAlerts ( context . Background ( ) , dashAlertInfo )
2021-10-29 10:57:24 -07:00
require . Nil ( t , err )
2022-02-28 09:54:56 +01:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , dashAlertInfo )
2021-10-29 10:57:24 -07:00
require . Nil ( t , err )
condition := simplejson . NewFromAny ( alerts [ 0 ] . Settings . Get ( "conditions" ) . MustArray ( ) [ 0 ] )
query := condition . Get ( "query" )
require . EqualValues ( t , 15 , query . Get ( "datasourceId" ) . MustInt64 ( ) )
} )
2016-04-18 14:15:03 +02:00
}
2022-02-28 09:54:56 +01:00
type fakeDatasourceService struct {
2022-06-27 12:23:15 -04:00
ExpectedDatasource * datasources . DataSource
2022-02-28 09:54:56 +01:00
datasources . DataSourceService
}
2023-02-09 15:49:44 +01:00
func ( f * fakeDatasourceService ) GetDefaultDataSource ( ctx context . Context , query * datasources . GetDefaultDataSourceQuery ) ( * datasources . DataSource , error ) {
return f . ExpectedDatasource , nil
2022-02-28 09:54:56 +01:00
}
2023-02-09 15:49:44 +01:00
func ( f * fakeDatasourceService ) GetDataSource ( ctx context . Context , query * datasources . GetDataSourceQuery ) ( * datasources . DataSource , error ) {
return f . ExpectedDatasource , nil
2022-02-28 09:54:56 +01:00
}