Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow run_every to be unique per rule #2593

Merged
merged 3 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions elastalert/elastalert.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
from elasticsearch.exceptions import TransportError

from . import kibana
from .kibana_discover import generate_kibana_discover_url
from .alerts import DebugAlerter
from .config import load_conf
from .enhancements import DropMatchException
from .kibana_discover import generate_kibana_discover_url
from .ruletypes import FlatlineRule
from .util import add_raw_postfix
from .util import cronite_datetime_to_timestamp
Expand Down Expand Up @@ -1022,8 +1022,7 @@ def init_rule(self, new_rule, new=True):
'processed_hits',
'starttime',
'minimum_starttime',
'has_run_once',
'run_every']
'has_run_once']
for prop in copy_properties:
if prop not in rule:
continue
Expand Down Expand Up @@ -1467,8 +1466,8 @@ def send_alert(self, matches, rule, alert_time=None, retried=False):
# Compute top count keys
if rule.get('top_count_keys'):
for match in matches:
if 'query_key' in rule and rule['query_key'] in match:
qk = match[rule['query_key']]
if 'query_key' in rule:
qk = lookup_es_key(match, rule['query_key'])
else:
qk = None

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
'boto3>=1.4.4',
'configparser>=3.5.0',
'croniter>=0.3.16',
'elasticsearch>=7.0.0',
'elasticsearch==7.0.0',
'envparse>=0.2.0',
'exotel>=0.1.3',
'jira>=2.0.0',
Expand Down
29 changes: 20 additions & 9 deletions tests/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_init_rule(ea):
ea.rules[0]['starttime'] = '2014-01-02T00:11:22'
ea.rules[0]['processed_hits'] = ['abcdefg']
new_rule = ea.init_rule(new_rule, False)
for prop in ['starttime', 'agg_matches', 'current_aggregate_id', 'processed_hits', 'minimum_starttime']:
for prop in ['starttime', 'agg_matches', 'current_aggregate_id', 'processed_hits', 'minimum_starttime', 'run_every']:
assert new_rule[prop] == ea.rules[0][prop]

# Properties are fresh
Expand All @@ -84,6 +84,11 @@ def test_init_rule(ea):
assert 'starttime' not in new_rule
assert new_rule['processed_hits'] == {}

# Assert run_every is unique
new_rule['run_every'] = datetime.timedelta(seconds=17)
new_rule = ea.init_rule(new_rule, True)
assert new_rule['run_every'] == datetime.timedelta(seconds=17)


def test_query(ea):
ea.thread_data.current_es.search.return_value = {'hits': {'total': 0, 'hits': []}}
Expand Down Expand Up @@ -989,17 +994,20 @@ def test_kibana_dashboard(ea):
def test_rule_changes(ea):
ea.rule_hashes = {'rules/rule1.yaml': 'ABC',
'rules/rule2.yaml': 'DEF'}
ea.rules = [ea.init_rule(rule, True) for rule in [{'rule_file': 'rules/rule1.yaml', 'name': 'rule1', 'filter': []},
{'rule_file': 'rules/rule2.yaml', 'name': 'rule2', 'filter': []}]]
run_every = datetime.timedelta(seconds=1)
ea.rules = [ea.init_rule(rule, True) for rule in [{'rule_file': 'rules/rule1.yaml', 'name': 'rule1', 'filter': [],
'run_every': run_every},
{'rule_file': 'rules/rule2.yaml', 'name': 'rule2', 'filter': [],
'run_every': run_every}]]
ea.rules[1]['processed_hits'] = ['save me']
new_hashes = {'rules/rule1.yaml': 'ABC',
'rules/rule3.yaml': 'XXX',
'rules/rule2.yaml': '!@#$'}

with mock.patch.object(ea.conf['rules_loader'], 'get_hashes') as mock_hashes:
with mock.patch.object(ea.conf['rules_loader'], 'load_configuration') as mock_load:
mock_load.side_effect = [{'filter': [], 'name': 'rule2', 'rule_file': 'rules/rule2.yaml'},
{'filter': [], 'name': 'rule3', 'rule_file': 'rules/rule3.yaml'}]
mock_load.side_effect = [{'filter': [], 'name': 'rule2', 'rule_file': 'rules/rule2.yaml', 'run_every': run_every},
{'filter': [], 'name': 'rule3', 'rule_file': 'rules/rule3.yaml', 'run_every': run_every}]
mock_hashes.return_value = new_hashes
ea.load_rule_changes()

Expand All @@ -1021,7 +1029,7 @@ def test_rule_changes(ea):
with mock.patch.object(ea.conf['rules_loader'], 'load_configuration') as mock_load:
with mock.patch.object(ea, 'send_notification_email') as mock_send:
mock_load.return_value = {'filter': [], 'name': 'rule3', 'new': 'stuff',
'rule_file': 'rules/rule4.yaml'}
'rule_file': 'rules/rule4.yaml', 'run_every': run_every}
mock_hashes.return_value = new_hashes
ea.load_rule_changes()
mock_send.assert_called_once_with(exception=mock.ANY, rule_file='rules/rule4.yaml')
Expand All @@ -1033,7 +1041,8 @@ def test_rule_changes(ea):
new_hashes.update({'rules/rule4.yaml': 'asdf'})
with mock.patch.object(ea.conf['rules_loader'], 'get_hashes') as mock_hashes:
with mock.patch.object(ea.conf['rules_loader'], 'load_configuration') as mock_load:
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'is_enabled': False, 'rule_file': 'rules/rule4.yaml'}
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'is_enabled': False,
'rule_file': 'rules/rule4.yaml', 'run_every': run_every}
mock_hashes.return_value = new_hashes
ea.load_rule_changes()
assert len(ea.rules) == 3
Expand All @@ -1044,7 +1053,8 @@ def test_rule_changes(ea):
new_hashes['rules/rule4.yaml'] = 'qwerty'
with mock.patch.object(ea.conf['rules_loader'], 'get_hashes') as mock_hashes:
with mock.patch.object(ea.conf['rules_loader'], 'load_configuration') as mock_load:
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'rule_file': 'rules/rule4.yaml'}
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'rule_file': 'rules/rule4.yaml',
'run_every': run_every}
mock_hashes.return_value = new_hashes
ea.load_rule_changes()
assert len(ea.rules) == 4
Expand All @@ -1053,7 +1063,8 @@ def test_rule_changes(ea):
new_hashes.pop('rules/rule4.yaml')
with mock.patch.object(ea.conf['rules_loader'], 'get_hashes') as mock_hashes:
with mock.patch.object(ea.conf['rules_loader'], 'load_configuration') as mock_load:
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'rule_file': 'rules/rule4.yaml'}
mock_load.return_value = {'filter': [], 'name': 'rule4', 'new': 'stuff', 'rule_file': 'rules/rule4.yaml',
'run_every': run_every}
mock_hashes.return_value = new_hashes
ea.load_rule_changes()
ea.scheduler.remove_job.assert_called_with(job_id='rule4')
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ def ea_sixsix():
'index': 'idx',
'filter': [],
'include': ['@timestamp'],
'run_every': datetime.timedelta(seconds=1),
'aggregation': datetime.timedelta(0),
'realert': datetime.timedelta(0),
'processed_hits': {},
Expand Down