Skip to content

Commit 9c2c898

Browse files
authored
Merge pull request Yelp#2488 from JeffAshton/slack-kibana-discover-url-attachment
Adding ability to attach the Kibana Discover url in slack alerts
2 parents 872826c + a02b146 commit 9c2c898

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

docs/source/ruletypes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,6 +1781,12 @@ Provide absolute address of the pciture, for example: http://some.address.com/im
17811781

17821782
``slack_timeout``: You can specify a timeout value, in seconds, for making communicating with Slac. The default is 10. If a timeout occurs, the alert will be retried next time elastalert cycles.
17831783

1784+
``slack_attach_kibana_discover_url``: Enables the attachment of the ``kibana_discover_url`` to the slack notification. The config ``generate_kibana_discover_url`` must also be ``True`` in order to generate the url. Defaults to ``False``.
1785+
1786+
``slack_kibana_discover_color``: The color of the Kibana Discover url attachment. Defaults to ``#ec4b98``.
1787+
1788+
``slack_kibana_discover_title``: The title of the Kibana Discover url attachment. Defaults to ``Discover in Kibana``.
1789+
17841790
Mattermost
17851791
~~~~~~~~~~
17861792

elastalert/alerts.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,9 @@ def __init__(self, rule):
11291129
self.slack_ignore_ssl_errors = self.rule.get('slack_ignore_ssl_errors', False)
11301130
self.slack_timeout = self.rule.get('slack_timeout', 10)
11311131
self.slack_ca_certs = self.rule.get('slack_ca_certs')
1132+
self.slack_attach_kibana_discover_url = self.rule.get('slack_attach_kibana_discover_url', False)
1133+
self.slack_kibana_discover_color = self.rule.get('slack_kibana_discover_color', '#ec4b98')
1134+
self.slack_kibana_discover_title = self.rule.get('slack_kibana_discover_title', 'Discover in Kibana')
11321135

11331136
def format_body(self, body):
11341137
# https://api.slack.com/docs/formatting
@@ -1191,6 +1194,15 @@ def alert(self, matches):
11911194
if self.slack_title_link != '':
11921195
payload['attachments'][0]['title_link'] = self.slack_title_link
11931196

1197+
if self.slack_attach_kibana_discover_url:
1198+
kibana_discover_url = lookup_es_key(matches[0], 'kibana_discover_url')
1199+
if kibana_discover_url:
1200+
payload['attachments'].append({
1201+
'color': self.slack_kibana_discover_color,
1202+
'title': self.slack_kibana_discover_title,
1203+
'title_link': kibana_discover_url
1204+
})
1205+
11941206
for url in self.slack_webhook_url:
11951207
for channel_override in self.slack_channel_override:
11961208
try:

elastalert/schema.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ properties:
286286
slack_text_string: {type: string}
287287
slack_ignore_ssl_errors: {type: boolean}
288288
slack_ca_certs: {type: string}
289+
slack_attach_kibana_discover_url {type: boolean}
290+
slack_kibana_discover_color {type: string}
291+
slack_kibana_discover_title {type: string}
289292

290293
### Mattermost
291294
mattermost_webhook_url: *arrayOfString

tests/alerts_test.py

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,206 @@ def test_slack_uses_list_of_custom_slack_channel():
11901190
assert expected_data2 == json.loads(mock_post_request.call_args_list[1][1]['data'])
11911191

11921192

1193+
def test_slack_attach_kibana_discover_url_when_generated():
1194+
rule = {
1195+
'name': 'Test Rule',
1196+
'type': 'any',
1197+
'slack_attach_kibana_discover_url': True,
1198+
'slack_webhook_url': 'http://please.dontgohere.slack',
1199+
'alert': []
1200+
}
1201+
rules_loader = FileRulesLoader({})
1202+
rules_loader.load_modules(rule)
1203+
alert = SlackAlerter(rule)
1204+
match = {
1205+
'@timestamp': '2016-01-01T00:00:00',
1206+
'kibana_discover_url': 'http://kibana#discover'
1207+
}
1208+
with mock.patch('requests.post') as mock_post_request:
1209+
alert.alert([match])
1210+
1211+
expected_data = {
1212+
'username': 'elastalert',
1213+
'parse': 'none',
1214+
'text': '',
1215+
'attachments': [
1216+
{
1217+
'color': 'danger',
1218+
'title': 'Test Rule',
1219+
'text': BasicMatchString(rule, match).__str__(),
1220+
'mrkdwn_in': ['text', 'pretext'],
1221+
'fields': []
1222+
},
1223+
{
1224+
'color': '#ec4b98',
1225+
'title': 'Discover in Kibana',
1226+
'title_link': 'http://kibana#discover'
1227+
}
1228+
],
1229+
'icon_emoji': ':ghost:',
1230+
'channel': ''
1231+
}
1232+
mock_post_request.assert_called_once_with(
1233+
rule['slack_webhook_url'],
1234+
data=mock.ANY,
1235+
headers={'content-type': 'application/json'},
1236+
proxies=None,
1237+
verify=False,
1238+
timeout=10
1239+
)
1240+
actual_data = json.loads(mock_post_request.call_args_list[0][1]['data'])
1241+
assert expected_data == actual_data
1242+
1243+
1244+
def test_slack_attach_kibana_discover_url_when_not_generated():
1245+
rule = {
1246+
'name': 'Test Rule',
1247+
'type': 'any',
1248+
'slack_attach_kibana_discover_url': True,
1249+
'slack_webhook_url': 'http://please.dontgohere.slack',
1250+
'alert': []
1251+
}
1252+
rules_loader = FileRulesLoader({})
1253+
rules_loader.load_modules(rule)
1254+
alert = SlackAlerter(rule)
1255+
match = {
1256+
'@timestamp': '2016-01-01T00:00:00'
1257+
}
1258+
with mock.patch('requests.post') as mock_post_request:
1259+
alert.alert([match])
1260+
1261+
expected_data = {
1262+
'username': 'elastalert',
1263+
'parse': 'none',
1264+
'text': '',
1265+
'attachments': [
1266+
{
1267+
'color': 'danger',
1268+
'title': 'Test Rule',
1269+
'text': BasicMatchString(rule, match).__str__(),
1270+
'mrkdwn_in': ['text', 'pretext'],
1271+
'fields': []
1272+
}
1273+
],
1274+
'icon_emoji': ':ghost:',
1275+
'channel': ''
1276+
}
1277+
mock_post_request.assert_called_once_with(
1278+
rule['slack_webhook_url'],
1279+
data=mock.ANY,
1280+
headers={'content-type': 'application/json'},
1281+
proxies=None,
1282+
verify=False,
1283+
timeout=10
1284+
)
1285+
actual_data = json.loads(mock_post_request.call_args_list[0][1]['data'])
1286+
assert expected_data == actual_data
1287+
1288+
1289+
def test_slack_kibana_discover_title():
1290+
rule = {
1291+
'name': 'Test Rule',
1292+
'type': 'any',
1293+
'slack_attach_kibana_discover_url': True,
1294+
'slack_kibana_discover_title': 'Click to discover in Kibana',
1295+
'slack_webhook_url': 'http://please.dontgohere.slack',
1296+
'alert': []
1297+
}
1298+
rules_loader = FileRulesLoader({})
1299+
rules_loader.load_modules(rule)
1300+
alert = SlackAlerter(rule)
1301+
match = {
1302+
'@timestamp': '2016-01-01T00:00:00',
1303+
'kibana_discover_url': 'http://kibana#discover'
1304+
}
1305+
with mock.patch('requests.post') as mock_post_request:
1306+
alert.alert([match])
1307+
1308+
expected_data = {
1309+
'username': 'elastalert',
1310+
'parse': 'none',
1311+
'text': '',
1312+
'attachments': [
1313+
{
1314+
'color': 'danger',
1315+
'title': 'Test Rule',
1316+
'text': BasicMatchString(rule, match).__str__(),
1317+
'mrkdwn_in': ['text', 'pretext'],
1318+
'fields': []
1319+
},
1320+
{
1321+
'color': '#ec4b98',
1322+
'title': 'Click to discover in Kibana',
1323+
'title_link': 'http://kibana#discover'
1324+
}
1325+
],
1326+
'icon_emoji': ':ghost:',
1327+
'channel': ''
1328+
}
1329+
mock_post_request.assert_called_once_with(
1330+
rule['slack_webhook_url'],
1331+
data=mock.ANY,
1332+
headers={'content-type': 'application/json'},
1333+
proxies=None,
1334+
verify=False,
1335+
timeout=10
1336+
)
1337+
actual_data = json.loads(mock_post_request.call_args_list[0][1]['data'])
1338+
assert expected_data == actual_data
1339+
1340+
1341+
def test_slack_kibana_discover_color():
1342+
rule = {
1343+
'name': 'Test Rule',
1344+
'type': 'any',
1345+
'slack_attach_kibana_discover_url': True,
1346+
'slack_kibana_discover_color': 'blue',
1347+
'slack_webhook_url': 'http://please.dontgohere.slack',
1348+
'alert': []
1349+
}
1350+
rules_loader = FileRulesLoader({})
1351+
rules_loader.load_modules(rule)
1352+
alert = SlackAlerter(rule)
1353+
match = {
1354+
'@timestamp': '2016-01-01T00:00:00',
1355+
'kibana_discover_url': 'http://kibana#discover'
1356+
}
1357+
with mock.patch('requests.post') as mock_post_request:
1358+
alert.alert([match])
1359+
1360+
expected_data = {
1361+
'username': 'elastalert',
1362+
'parse': 'none',
1363+
'text': '',
1364+
'attachments': [
1365+
{
1366+
'color': 'danger',
1367+
'title': 'Test Rule',
1368+
'text': BasicMatchString(rule, match).__str__(),
1369+
'mrkdwn_in': ['text', 'pretext'],
1370+
'fields': []
1371+
},
1372+
{
1373+
'color': 'blue',
1374+
'title': 'Discover in Kibana',
1375+
'title_link': 'http://kibana#discover'
1376+
}
1377+
],
1378+
'icon_emoji': ':ghost:',
1379+
'channel': ''
1380+
}
1381+
mock_post_request.assert_called_once_with(
1382+
rule['slack_webhook_url'],
1383+
data=mock.ANY,
1384+
headers={'content-type': 'application/json'},
1385+
proxies=None,
1386+
verify=False,
1387+
timeout=10
1388+
)
1389+
actual_data = json.loads(mock_post_request.call_args_list[0][1]['data'])
1390+
assert expected_data == actual_data
1391+
1392+
11931393
def test_http_alerter_with_payload():
11941394
rule = {
11951395
'name': 'Test HTTP Post Alerter With Payload',

0 commit comments

Comments
 (0)