2016-06-11 03:13:33 -05:00
package alerting
2016-04-18 07:15:03 -05:00
import (
2021-11-05 03:41:24 -05:00
"context"
2022-03-02 04:04:29 -06:00
"errors"
2022-08-10 08:37:51 -05:00
"os"
2016-04-18 07:15:03 -05:00
"testing"
2018-11-05 06:14:02 -06:00
"time"
2016-04-18 07:15:03 -05:00
2022-06-27 11:23:15 -05:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2016-04-18 07:15:03 -05:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
2019-05-14 01:15:05 -05:00
"github.com/grafana/grafana/pkg/models"
2022-02-28 02:54:56 -06:00
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/permissions"
2022-04-08 07:30:25 -05:00
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
2016-04-18 07:15:03 -05:00
)
2016-06-11 03:13:33 -05:00
func TestAlertRuleExtraction ( t * testing . T ) {
2021-04-12 07:53:51 -05:00
RegisterCondition ( "query" , func ( model * simplejson . Json , index int ) ( Condition , error ) {
return & FakeCondition { } , nil
} )
// mock data
2022-06-27 11:23:15 -05: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 07:53:51 -05:00
2022-08-10 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/graphite-alert.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
dsPermissions := permissions . NewMockDatasourcePermissionService ( )
2022-06-27 11:23:15 -05:00
dsPermissions . DsResult = [ ] * datasources . DataSource {
2022-02-28 02:54:56 -06:00
{
Id : 1 ,
} ,
}
dsService := & fakeDatasourceService { ExpectedDatasource : defaultDs }
2022-04-08 07:30:25 -05:00
store := mockstore . NewSQLStoreMock ( )
extractor := ProvideDashAlertExtractorService ( dsPermissions , dsService , store )
2022-02-28 02:54:56 -06:00
2021-04-12 07:53:51 -05: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 02:54:56 -06:00
_ , _ = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05: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 )
2022-06-27 11:23:15 -05:00
dsService . ExpectedDatasource = & datasources . DataSource { Id : 12 }
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
require . Len ( t , alerts , 2 )
for _ , v := range alerts {
require . EqualValues ( t , v . DashboardId , 57 )
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 )
require . EqualValues ( t , alerts [ 0 ] . PanelId , 3 )
require . EqualValues ( t , alerts [ 1 ] . PanelId , 4 )
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 08:37:51 -05:00
panelWithoutID , err := os . ReadFile ( "./testdata/panels-missing-id.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithoutID )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
_ , err = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05:00
require . NotNil ( t , err )
} )
t . Run ( "Panels missing id should return error" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
panelWithIDZero , err := os . ReadFile ( "./testdata/panel-with-id-0.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithIDZero )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
_ , err = extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05:00
require . NotNil ( t , err )
2021-12-02 09:41:24 -06:00
} )
t . Run ( "Cannot save panel with query that is referenced by legacy alerting" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
panelWithQuery , err := os . ReadFile ( "./testdata/panel-with-bad-query-id.json" )
2021-12-02 09:41:24 -06:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithQuery )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
_ , err = extractor . GetAlerts ( WithUAEnabled ( context . Background ( ) , true ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-12-02 09:41:24 -06: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 07:53:51 -05:00
} )
t . Run ( "Panel does not have datasource configured, use the default datasource" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
panelWithoutSpecifiedDatasource , err := os . ReadFile ( "./testdata/panel-without-specified-datasource.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( panelWithoutSpecifiedDatasource )
require . Nil ( t , err )
2022-06-27 11:23:15 -05:00
dsService . ExpectedDatasource = & datasources . DataSource { Id : 12 }
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05: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 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/v5-dashboard.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
require . Len ( t , alerts , 2 )
} )
t . Run ( "Alert notifications are in DB" , func ( t * testing . T ) {
2022-10-19 08:02:15 -05:00
sqlStore := sqlStore { db : db . InitTestDB ( t ) }
2021-10-07 09:33:50 -05:00
2021-04-12 07:53:51 -05:00
firstNotification := models . CreateAlertNotificationCommand { Uid : "notifier1" , OrgId : 1 , Name : "1" }
2021-11-05 03:41:24 -05:00
err = sqlStore . CreateAlertNotificationCommand ( context . Background ( ) , & firstNotification )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
2021-10-07 09:33:50 -05:00
2021-04-12 07:53:51 -05:00
secondNotification := models . CreateAlertNotificationCommand { Uid : "notifier2" , OrgId : 1 , Name : "2" }
2021-11-05 03:41:24 -05:00
err = sqlStore . CreateAlertNotificationCommand ( context . Background ( ) , & secondNotification )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
2022-08-10 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/influxdb-alert.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
require . Len ( t , alerts , 1 )
for _ , alert := range alerts {
require . EqualValues ( t , alert . DashboardId , 4 )
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 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/collapsed-panels.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
dash := models . NewDashboardFromJson ( dashJSON )
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , DashAlertInfo {
User : nil ,
Dash : dash ,
OrgID : 1 ,
} )
2021-04-12 07:53:51 -05: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 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/dash-without-id.json" )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2018-12-20 09:12:47 -06:00
2022-02-28 02:54:56 -06:00
dashAlertInfo := DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
}
2018-03-13 16:23:37 -05:00
2022-02-28 02:54:56 -06:00
err = extractor . ValidateAlerts ( context . Background ( ) , dashAlertInfo )
2021-04-12 07:53:51 -05:00
require . Nil ( t , err )
2018-03-13 16:23:37 -05:00
2022-02-28 02:54:56 -06:00
_ , err = extractor . GetAlerts ( context . Background ( ) , dashAlertInfo )
2021-04-12 07:53:51 -05:00
require . Equal ( t , err . Error ( ) , "alert validation error: Panel id is not correct, alertName=Influxdb, panelId=1" )
2016-04-18 07:15:03 -05:00
} )
2021-10-29 12:57:24 -05:00
t . Run ( "Extract data source given new DataSourceRef object model" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/panel-with-datasource-ref.json" )
2021-10-29 12:57:24 -05:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
dsService . ExpectedDatasource = graphite2Ds
dashAlertInfo := DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
}
2021-10-29 12:57:24 -05:00
2022-02-28 02:54:56 -06:00
err = extractor . ValidateAlerts ( context . Background ( ) , dashAlertInfo )
2021-10-29 12:57:24 -05:00
require . Nil ( t , err )
2022-02-28 02:54:56 -06:00
alerts , err := extractor . GetAlerts ( context . Background ( ) , dashAlertInfo )
2021-10-29 12:57:24 -05: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 07:15:03 -05:00
}
2022-02-28 02:54:56 -06:00
2022-03-02 04:04:29 -06:00
func TestFilterPermissionsErrors ( t * testing . T ) {
RegisterCondition ( "query" , func ( model * simplejson . Json , index int ) ( Condition , error ) {
return & FakeCondition { } , nil
} )
// mock data
2022-06-27 11:23:15 -05:00
defaultDs := & datasources . DataSource { Id : 12 , OrgId : 1 , Name : "I am default" , IsDefault : true , Uid : "def-uid" }
2022-03-02 04:04:29 -06:00
2022-08-10 08:37:51 -05:00
json , err := os . ReadFile ( "./testdata/graphite-alert.json" )
2022-03-02 04:04:29 -06:00
require . Nil ( t , err )
dashJSON , err := simplejson . NewJson ( json )
require . Nil ( t , err )
dsPermissions := permissions . NewMockDatasourcePermissionService ( )
dsService := & fakeDatasourceService { ExpectedDatasource : defaultDs }
2022-04-08 07:30:25 -05:00
extractor := ProvideDashAlertExtractorService ( dsPermissions , dsService , nil )
2022-03-02 04:04:29 -06:00
tc := [ ] struct {
name string
2022-06-27 11:23:15 -05:00
result [ ] * datasources . DataSource
2022-03-02 04:04:29 -06:00
err error
expectedErr error
} {
{
"Data sources are filtered and return results don't return an error" ,
2022-06-27 11:23:15 -05:00
[ ] * datasources . DataSource { defaultDs } ,
2022-03-02 04:04:29 -06:00
nil ,
nil ,
} ,
{
"Data sources are filtered but return empty results should return error" ,
nil ,
nil ,
2022-06-27 11:23:15 -05:00
datasources . ErrDataSourceAccessDenied ,
2022-03-02 04:04:29 -06:00
} ,
{
"Using default OSS implementation doesn't return an error" ,
nil ,
permissions . ErrNotImplemented ,
nil ,
} ,
{
"Returning an error different from ErrNotImplemented should fails" ,
nil ,
errors . New ( "random error" ) ,
errors . New ( "random error" ) ,
} ,
}
for _ , test := range tc {
t . Run ( test . name , func ( t * testing . T ) {
dsPermissions . DsResult = test . result
dsPermissions . ErrResult = test . err
_ , err = extractor . GetAlerts ( WithUAEnabled ( context . Background ( ) , true ) , DashAlertInfo {
User : nil ,
Dash : models . NewDashboardFromJson ( dashJSON ) ,
OrgID : 1 ,
} )
assert . Equal ( t , err , test . expectedErr )
} )
}
}
2022-02-28 02:54:56 -06:00
type fakeDatasourceService struct {
2022-06-27 11:23:15 -05:00
ExpectedDatasource * datasources . DataSource
2022-02-28 02:54:56 -06:00
datasources . DataSourceService
}
2022-06-27 11:23:15 -05:00
func ( f * fakeDatasourceService ) GetDefaultDataSource ( ctx context . Context , query * datasources . GetDefaultDataSourceQuery ) error {
2022-02-28 02:54:56 -06:00
query . Result = f . ExpectedDatasource
return nil
}
2022-06-27 11:23:15 -05:00
func ( f * fakeDatasourceService ) GetDataSource ( ctx context . Context , query * datasources . GetDataSourceQuery ) error {
2022-02-28 02:54:56 -06:00
query . Result = f . ExpectedDatasource
return nil
}