Skip to content

Commit

Permalink
Merge pull request #1539 from lstyles/master
Browse files Browse the repository at this point in the history
Don't overwrite `self.teams` upon parsing responders in OpsGenie integration
  • Loading branch information
jertel authored Sep 27, 2024
2 parents c885e9f + 65b7630 commit 46cb90d
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 44 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

## Other changes
- [Docs] Mention the two available Spike-rule metrics that are add into the match record - [#1542](https://github.com/jertel/elastalert2/pull/1542) - @ulmako
- [OpsGenie] Corrected spelling of the `opsgenie_default_receipients` configuration option to `opsgenie_default_recipients`. Both variations will continue to work and a warning message will notify affected users. [#1539](https://github.com/jertel/elastalert2/pull/1539) - @lstyles
- [OpsGenie] Prevent templated `opsgenie_teams` and `opsgenie_recipients` from being overwritten with evaluated values first time an alert is sent. [#1540](https://github.com/jertel/elastalert2/issues/1540) [#1539](https://github.com/jertel/elastalert2/pull/1539) - @lstyles

# 2.20.0

Expand Down
2 changes: 1 addition & 1 deletion docs/source/alerts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1790,7 +1790,7 @@ Optional:

``opsgenie_recipients_args``: Map of arguments used to format opsgenie_recipients.

``opsgenie_default_receipients``: List of default recipients to notify when the formatting of opsgenie_recipients is unsuccesful.
``opsgenie_default_recipients``: List of default recipients to notify when the formatting of opsgenie_recipients is unsuccesful.

``opsgenie_teams``: A list of OpsGenie teams to notify (useful for schedules with escalation).

Expand Down
61 changes: 35 additions & 26 deletions elastalert/alerters/opsgenie.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ class OpsGenieAlerter(Alerter):

def __init__(self, *args):
super(OpsGenieAlerter, self).__init__(*args)

default_recipients_deprecated = self.rule.get('opsgenie_default_receipients', None)
if default_recipients_deprecated:
elastalert_logger.warning("OpsGenieAlerter: `opsgenie_default_receipients` rule configuration option is deprecated and will be removed in the future. Please use `opsgenie_default_recipients` option instead.")

self.account = self.rule.get('opsgenie_account')
self.api_key = self.rule.get('opsgenie_key', 'key')
self.default_reciepients = self.rule.get('opsgenie_default_receipients', None)
self.default_recipients = self.rule.get('opsgenie_default_recipients', default_recipients_deprecated)
self.recipients = self.rule.get('opsgenie_recipients')
self.recipients_args = self.rule.get('opsgenie_recipients_args')
self.default_teams = self.rule.get('opsgenie_default_teams', None)
Expand All @@ -35,25 +40,29 @@ def __init__(self, *args):
self.source = self.rule.get('opsgenie_source', 'ElastAlert')

def _parse_responders(self, responders, responder_args, matches, default_responders):
if responder_args:
formated_responders = list()
responders_values = dict((k, lookup_es_key(matches[0], v)) for k, v in responder_args.items())
responders_values = dict((k, v) for k, v in responders_values.items() if v)

for responder in responders:
responder = str(responder)
try:
formated_responders.append(responder.format(**responders_values))
except KeyError as error:
elastalert_logger.warning("OpsGenieAlerter: Cannot create responder for OpsGenie Alert. Key not foud: %s. " % (error))
if not formated_responders:
elastalert_logger.warning("OpsGenieAlerter: no responders can be formed. Trying the default responder ")
if not default_responders:
elastalert_logger.warning("OpsGenieAlerter: default responder not set. Falling back")
formated_responders = responders
else:
formated_responders = default_responders
responders = formated_responders
if responders is None:
return None
if responder_args is None:
responder_args = dict()

formated_responders = list()
responders_values = dict((k, lookup_es_key(matches[0], v)) for k, v in responder_args.items())
responders_values = dict((k, v) for k, v in responders_values.items() if v)
for responder in responders:
responder = str(responder)
try:
formated_responders.append(responder.format(**responders_values))
except KeyError as error:
elastalert_logger.warning("OpsGenieAlerter: Cannot create responder for OpsGenie Alert. Key not found: %s. " % (error))
if not formated_responders:
elastalert_logger.warning("OpsGenieAlerter: no responders can be formed. Trying the default responder ")
if not default_responders:
elastalert_logger.warning("OpsGenieAlerter: default responder not set. Falling back")
formated_responders = responders
else:
formated_responders = default_responders
responders = formated_responders

return responders

def alert(self, matches):
Expand All @@ -68,16 +77,16 @@ def alert(self, matches):
self.message = self.create_title(matches)
else:
self.message = self.custom_message.format(**matches[0])
self.recipients = self._parse_responders(self.recipients, self.recipients_args, matches, self.default_reciepients)
self.teams = self._parse_responders(self.teams, self.teams_args, matches, self.default_teams)
recipients = self._parse_responders(self.recipients, self.recipients_args, matches, self.default_recipients)
teams = self._parse_responders(self.teams, self.teams_args, matches, self.default_teams)
post = {}
post['message'] = self.message
if self.account:
post['user'] = self.account
if self.recipients:
post['responders'] = [{'username': r, 'type': 'user'} for r in self.recipients]
if self.teams:
post['teams'] = [{'name': r, 'type': 'team'} for r in self.teams]
if recipients:
post['responders'] = [{'username': r, 'type': 'user'} for r in recipients]
if teams:
post['teams'] = [{'name': r, 'type': 'team'} for r in teams]
if self.description:
post['description'] = self.description.format(**matches[0])
else:
Expand Down
105 changes: 88 additions & 17 deletions tests/alerters/opsgenie_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,21 +105,23 @@ def test_opsgenie_alert_routing():
'opsgenie_key': 'ogkey',
'opsgenie_account': 'genies',
'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts',
'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'},
'opsgenie_recipients': ['{RECIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'},
'type': mock_rule(),
'filter': [{'query': {'query_string': {'query': '*hihi*'}}}],
'alert': 'opsgenie',
'opsgenie_teams': ['{TEAM_PREFIX}-Team'],
'opsgenie_teams_args': {'TEAM_PREFIX': 'team'}
}
with mock.patch('requests.post'):

with mock.patch('requests.post') as mock_post:
alert = OpsGenieAlerter(rule)
alert.alert([{'@timestamp': '2014-10-31T00:00:00', 'team': "Test", 'recipient': "lytics"}])

assert alert.get_info()['teams'] == ['Test-Team']
assert alert.get_info()['recipients'] == ['lytics']
_, kwargs = mock_post.call_args
payload = kwargs['json']

assert payload['teams'][0]['name'] == 'Test-Team'
assert payload['responders'][0]['username'] == 'lytics'


def test_opsgenie_default_alert_routing():
Expand All @@ -128,22 +130,24 @@ def test_opsgenie_default_alert_routing():
'opsgenie_key': 'ogkey',
'opsgenie_account': 'genies',
'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts',
'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'},
'opsgenie_recipients': ['{RECIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'},
'type': mock_rule(),
'filter': [{'query': {'query_string': {'query': '*hihi*'}}}],
'alert': 'opsgenie',
'opsgenie_teams': ['{TEAM_PREFIX}-Team'],
'opsgenie_default_receipients': ["devops@test.com"],
'opsgenie_default_teams': ["Test"]
'opsgenie_default_recipients': ["devops@test.com"],
'opsgenie_default_teams': ["Default-Team"]
}
with mock.patch('requests.post'):
with mock.patch('requests.post') as mock_post:

alert = OpsGenieAlerter(rule)
alert.alert([{'@timestamp': '2014-10-31T00:00:00', 'team': "Test"}])

assert alert.get_info()['teams'] == ['{TEAM_PREFIX}-Team']
assert alert.get_info()['recipients'] == ['devops@test.com']
_, kwargs = mock_post.call_args
payload = kwargs['json']
assert payload['teams'][0]['name'] == 'Default-Team'
assert payload['responders'][0]['username'] == 'devops@test.com'


def test_opsgenie_details_with_constant_value():
Expand Down Expand Up @@ -1006,8 +1010,8 @@ def test_opsgenie_parse_responders(caplog):
'opsgenie_key': 'ogkey',
'opsgenie_account': 'genies',
'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts',
'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'},
'opsgenie_recipients': ['{RECIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'},
'type': mock_rule(),
'filter': [{'query': {'query_string': {'query': '*hihi*'}}}],
'alert': 'opsgenie',
Expand Down Expand Up @@ -1040,7 +1044,7 @@ def test_opsgenie_parse_responders(caplog):
assert expected == actual
user, level, message = caplog.record_tuples[0]
assert logging.WARNING == level
assert "Cannot create responder for OpsGenie Alert. Key not foud: 'RECEIPIENT_PREFIX'." in message
assert "Cannot create responder for OpsGenie Alert. Key not found: 'RECIPIENT_PREFIX'." in message
user, level, message = caplog.record_tuples[1]
assert logging.WARNING == level
assert 'no responders can be formed. Trying the default responder' in message
Expand All @@ -1049,12 +1053,79 @@ def test_opsgenie_parse_responders(caplog):
assert 'default responder not set. Falling back' in message
user, level, message = caplog.record_tuples[3]
assert logging.WARNING == level
assert "Cannot create responder for OpsGenie Alert. Key not foud: 'TEAM_PREFIX'." in message
assert "Cannot create responder for OpsGenie Alert. Key not found: 'TEAM_PREFIX'." in message
user, level, message = caplog.record_tuples[4]
assert logging.WARNING == level
assert 'no responders can be formed. Trying the default responder' in message


def test_opsgenie_parse_opsgenie_teams():
rule = {
'name': 'testOGalert',
'opsgenie_key': 'ogkey',
'opsgenie_account': 'genies',
'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts',
'type': mock_rule(),
'filter': [{'query': {'query_string': {'query': '*hihi*'}}}],
'alert': 'opsgenie',
'opsgenie_teams': ['{TEAM_PREFIX}-Team'],
'opsgenie_teams_args': {'TEAM_PREFIX': 'team'},
'opsgenie_default_teams': ["Test"]
}
match = [
{
'@timestamp': '2014-10-10T00:00:00',
'sender_ip': '1.1.1.1',
'hostname': 'aProbe',
'team': 'Test'
},
{
'@timestamp': '2014-10-10T00:00:00',
'sender_ip': '1.1.1.1',
'hostname2': 'aProbe',
'team': 'Test'
}
]

with mock.patch('requests.post'):
alert = OpsGenieAlerter(rule)
alert.alert(match)
assert alert.teams == rule['opsgenie_teams']


def test_opsgenie_parse_opsgenie_recipients():
rule = {
'name': 'testOGalert',
'opsgenie_key': 'ogkey',
'opsgenie_account': 'genies',
'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts',
'opsgenie_recipients': ['{RECIPIENT_PREFIX}'],
'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'},
'type': mock_rule(),
'filter': [{'query': {'query_string': {'query': '*hihi*'}}}],
'alert': 'opsgenie'
}
match = [
{
'@timestamp': '2014-10-10T00:00:00',
'sender_ip': '1.1.1.1',
'hostname': 'aProbe',
'recipient': 'Test'
},
{
'@timestamp': '2014-10-10T00:00:00',
'sender_ip': '1.1.1.1',
'hostname2': 'aProbe',
'recipient': 'Test'
}
]

with mock.patch('requests.post'):
alert = OpsGenieAlerter(rule)
alert.alert(match)
assert alert.recipients == rule['opsgenie_recipients']


def test_opsgenie_create_custom_title():
rule = {
'name': 'Opsgenie Details',
Expand Down

0 comments on commit 46cb90d

Please sign in to comment.