-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1282 from seanyinx/master
Lark alerter support
- Loading branch information
Showing
7 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import json | ||
import warnings | ||
|
||
import requests | ||
from elastalert.alerts import Alerter, DateTimeEncoder | ||
from elastalert.util import EAException, elastalert_logger | ||
from requests import RequestException | ||
|
||
|
||
class LarkAlerter(Alerter): | ||
""" Creates a Lark message for each alert """ | ||
required_options = frozenset(['lark_bot_id']) | ||
|
||
def __init__(self, rule): | ||
super(LarkAlerter, self).__init__(rule) | ||
self.lark_bot_id = self.rule.get('lark_bot_id', None) | ||
self.lark_webhook_url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{self.lark_bot_id}' | ||
self.lark_msg_type = self.rule.get('lark_msgtype', 'text') | ||
|
||
def alert(self, matches): | ||
title = self.create_title(matches) | ||
body = self.create_alert_body(matches) | ||
|
||
headers = { | ||
'Content-Type': 'application/json', | ||
'Accept': 'application/json;charset=utf-8' | ||
} | ||
|
||
payload = { | ||
'msg_type': self.lark_msg_type, | ||
"content": { | ||
"title": title, | ||
"text": body | ||
}, | ||
} | ||
|
||
try: | ||
response = requests.post( | ||
self.lark_webhook_url, | ||
data=json.dumps(payload, cls=DateTimeEncoder), | ||
headers=headers) | ||
warnings.resetwarnings() | ||
response.raise_for_status() | ||
except RequestException as e: | ||
raise EAException("Error posting to lark: %s" % e) | ||
|
||
elastalert_logger.info("Trigger sent to lark") | ||
|
||
def get_info(self): | ||
return { | ||
"type": "lark", | ||
"lark_webhook_url": self.lark_webhook_url | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import json | ||
import logging | ||
from unittest import mock | ||
|
||
import pytest | ||
from requests import RequestException | ||
|
||
from elastalert.alerters.lark import LarkAlerter | ||
from elastalert.loaders import FileRulesLoader | ||
from elastalert.util import EAException | ||
|
||
|
||
def test_lark_text(caplog): | ||
caplog.set_level(logging.INFO) | ||
rule = { | ||
'name': 'Test Lark Rule', | ||
'type': 'any', | ||
'lark_bot_id': 'xxxxxxx', | ||
'lark_msgtype': 'text', | ||
'alert': [], | ||
'alert_subject': 'Test Lark' | ||
} | ||
rules_loader = FileRulesLoader({}) | ||
rules_loader.load_modules(rule) | ||
alert = LarkAlerter(rule) | ||
match = { | ||
'@timestamp': '2021-01-01T00:00:00', | ||
'somefield': 'foobarbaz' | ||
} | ||
with mock.patch('requests.post') as mock_post_request: | ||
alert.alert([match]) | ||
|
||
expected_data = { | ||
'msg_type': 'text', | ||
'content': { | ||
'title': 'Test Lark', | ||
'text': 'Test Lark Rule\n\n@timestamp: 2021-01-01T00:00:00\nsomefield: foobarbaz\n' | ||
} | ||
} | ||
|
||
mock_post_request.assert_called_once_with( | ||
'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxx', | ||
data=mock.ANY, | ||
headers={ | ||
'Content-Type': 'application/json', | ||
'Accept': 'application/json;charset=utf-8' | ||
} | ||
) | ||
|
||
actual_data = json.loads(mock_post_request.call_args_list[0][1]['data']) | ||
assert expected_data == actual_data | ||
assert ('elastalert', logging.INFO, 'Trigger sent to lark') == caplog.record_tuples[0] | ||
|
||
|
||
def test_lark_ea_exception(): | ||
with pytest.raises(EAException) as ea: | ||
rule = { | ||
'name': 'Test Lark Rule', | ||
'type': 'any', | ||
'lark_bot_id': 'xxxxxxx', | ||
'lark_msgtype': 'action_card', | ||
'lark_single_title': 'elastalert', | ||
'lark_single_url': 'http://xxxxx2', | ||
'lark_btn_orientation': '1', | ||
'lark_btns': [ | ||
{ | ||
'title': 'test1', | ||
'actionURL': 'https://xxxxx0/' | ||
}, | ||
{ | ||
'title': 'test2', | ||
'actionURL': 'https://xxxxx1/' | ||
} | ||
], | ||
'lark_proxy': 'http://proxy.url', | ||
'lark_proxy_login': 'admin', | ||
'lark_proxy_pass': 'password', | ||
'alert': [], | ||
'alert_subject': 'Test Lark' | ||
} | ||
rules_loader = FileRulesLoader({}) | ||
rules_loader.load_modules(rule) | ||
alert = LarkAlerter(rule) | ||
match = { | ||
'@timestamp': '2021-01-01T00:00:00', | ||
'somefield': 'foobarbaz' | ||
} | ||
mock_run = mock.MagicMock(side_effect=RequestException) | ||
with mock.patch('requests.post', mock_run), pytest.raises(RequestException): | ||
alert.alert([match]) | ||
assert 'Error posting to lark: ' in str(ea) | ||
|
||
|
||
def test_lark_getinfo(): | ||
rule = { | ||
'name': 'Test Lark Rule', | ||
'type': 'any', | ||
'lark_bot_id': 'xxxxxxx', | ||
'alert': [], | ||
'alert_subject': 'Test Lark' | ||
} | ||
rules_loader = FileRulesLoader({}) | ||
rules_loader.load_modules(rule) | ||
alert = LarkAlerter(rule) | ||
|
||
expected_data = { | ||
'type': 'lark', | ||
"lark_webhook_url": 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxx' | ||
} | ||
actual_data = alert.get_info() | ||
assert expected_data == actual_data | ||
|
||
|
||
@pytest.mark.parametrize('lark_bot_id, expected_data', [ | ||
('', 'Missing required option(s): lark_bot_id'), | ||
('xxxxxxx', | ||
{ | ||
'type': 'lark', | ||
"lark_webhook_url": 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxx' | ||
}), | ||
]) | ||
def test_lark_required_error(lark_bot_id, expected_data): | ||
try: | ||
rule = { | ||
'name': 'Test Lark Rule', | ||
'type': 'any', | ||
'alert': [], | ||
'alert_subject': 'Test Lark' | ||
} | ||
|
||
if lark_bot_id: | ||
rule['lark_bot_id'] = lark_bot_id | ||
|
||
rules_loader = FileRulesLoader({}) | ||
rules_loader.load_modules(rule) | ||
alert = LarkAlerter(rule) | ||
|
||
actual_data = alert.get_info() | ||
assert expected_data == actual_data | ||
except Exception as ea: | ||
assert expected_data in str(ea) |