diff --git a/.travis.yml b/.travis.yml index f21b7e5..e57b1e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,14 @@ language: python python: - "3.5" - "3.6" -install: "pip install -r requirements.txt" -script: python -m unittest discover tests +before_install: + - sudo apt-get install -y pandoc +install: + - pip install -r requirements.txt +script: + - python -m unittest discover tests +deploy: + provider: pypi + user: "idealista" + password: + secure: L80/PUp/eGj6+OPzGfv0vO5V+5V5dIuaw4qRHlVDgEsZaFlJPsEL62x8CYv6DXewaYIBKhMk2cXWMkHuWFKjTcJ+Vjsqx6jIUwBLI48eKAWMxEP3ypJf3gqLeJmYFO6QPUGu1pOuOiFjBeV+yXNz7+C7ZY04cAcvgBGgRs+Tw6Se+AgfWNFXZZDX7N9/eWzFktqRpisWLNUJBBl6JnRcYhiadD5PvqS8eTsI/hiewMWijzlbfzZJ4PEFLJ7g2r3LL2OgHp1FR4zliXo0tMtFSyCFHAYz7vIcGDVlp1GmPSD0Me9h+LnjArK1LwotSA6DdwIVVrS+/ZR3ocHETbUxROrHLLZMN+tE6gcJ+ANq4HTqt4r7WVGO10IOYLqaZmNxazoyYilJRORvpYd6CUpR+8pNybG54YrR4qc4FBoWTob8T1+BGUN6K/R1M2vgZC6adlMmNguNMJ/wX5GzBO7nUju6TcehYJfZQGc3WYQuV/WpE1IHMJSJJwMwAkvJ7yiEM5nj5WDLsrPU6ViBc/konM1Bl/loZ7YhyaKtr9ypqMxkHsnECpAt3gI2Btu4Az8JQd3ZqHgmTmI399E4k1qoRgv0zPjFDDzOM7QB5Q7YZ0jzRBJqFlaII2wZC3iNHfxWSW/bjq9OOwwQkuti/WeyqG2XHg8gwpyixUUqLUw9Wy8= diff --git a/CHANGELOG.md b/CHANGELOG.md index 27adc46..3469d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a ch ## [Unreleased](https://github.com/idealista/prom2teams/tree/develop) +## [1.1.3](https://github.com/idealista/prom2teams/tree/1.1.3) +[Full Changelog](https://github.com/idealista/prom2teams/compare/1.1.2...1.1.3) +### Fixed +- *[#21](https://github.com/idealista/prom2teams/issues/21) Made some JSON fields optional, avoiding later crashes* @jnogol +- *[#23](https://github.com/idealista/prom2teams/issues/23) Deleted redundant log message* @jnogol +- *[#27](https://github.com/idealista/prom2teams/issues/27) Automatic deploy to PyPi via Travis* @jnogol + ## [1.1.2](https://github.com/idealista/prom2teams/tree/1.1.2) [Full Changelog](https://github.com/idealista/prom2teams/compare/1.1.1...1.1.2) ### Fixed diff --git a/prom2teams/message/parser.py b/prom2teams/message/parser.py index f568903..2a87727 100644 --- a/prom2teams/message/parser.py +++ b/prom2teams/message/parser.py @@ -4,21 +4,35 @@ logger = logging.getLogger() +def check_fields(json_alerts_attr, json_alerts_labels_attr, json_alerts_annotations_attr): + mandatory_fields = ['alertname', 'status', 'instance', 'summary'] + optional_fields = ['severity', 'description'] + fields = mandatory_fields + optional_fields -def parse(json_str): - logger.debug('received: %s', json_str) + alert_fields = {} + + for field in fields: + alert_field_key = 'alert_' + field + if field in json_alerts_attr: + alert_fields[alert_field_key] = json_alerts_attr[field] + elif field in json_alerts_labels_attr: + alert_fields[alert_field_key] = json_alerts_labels_attr[field] + elif field in json_alerts_annotations_attr: + alert_fields[alert_field_key] = json_alerts_annotations_attr[field] + # If the field isn't in the JSON but it's a mandatory field, then we send an error message + elif field in mandatory_fields: + alert_fields['alert_severity'] = 'severe' + alert_fields['alert_status'] = 'incorrect' + alert_fields['alert_summary'] = 'Incorrect JSON received. At least one mandatory field ('+field+') is absent.' + return alert_fields + return alert_fields + +def parse(json_str): json_values = json.loads(json_str) json_alerts_attr = json_values['alerts'][0] json_alerts_labels_attr = json_alerts_attr['labels'] json_alerts_annotations_attr = json_alerts_attr['annotations'] - return { - 'alert_name': json_alerts_labels_attr['alertname'], - 'alert_instance': json_alerts_labels_attr['instance'], - 'alert_severity': json_alerts_labels_attr['severity'], - 'alert_summary': json_alerts_annotations_attr['summary'], - 'alert_description': json_alerts_annotations_attr['description'], - 'alert_status': json_alerts_attr['status'] - } + return check_fields(json_alerts_attr, json_alerts_labels_attr, json_alerts_annotations_attr) diff --git a/prom2teams/teams/template.j2 b/prom2teams/teams/template.j2 index ff92a14..39fef2e 100644 --- a/prom2teams/teams/template.j2 +++ b/prom2teams/teams/template.j2 @@ -12,25 +12,25 @@ "@context": "http://schema.org/extensions", "themeColor": "{% if alert_status=='resolved' %} {{ theme_colors.resolved }} {% else %} {{ theme_colors[msg_text.alert_severity] }} {% endif %}", "summary": "{{ msg_text.alert_summary }}", - "title": "Prometheus alarm {% if alert_status=='resolved' %} (Resolved) {% endif %}", + "title": "Prometheus alarm {% if alert_status=='resolved' %} (Resolved) {% elif alert_status=='incorrect' %} (Incorrect) {% endif %}", "sections": [{ "activityTitle": "{{ msg_text.alert_summary }}", - "facts": [{ + "facts": [{% if msg_text.alert_alertname %}{ "name": "Alarm", - "value": "{{ msg_text.alert_name }}" - }, { + "value": "{{ msg_text.alert_alertname }}" + },{% endif %}{% if msg_text.alert_instance %}{ "name": "In host", "value": "{{ msg_text.alert_instance }}" - }, { + },{% endif %}{% if msg_text.alert_severity %}{ "name": "Severity", "value": "{{ msg_text.alert_severity }}" - }, { + },{% endif %}{% if msg_text.alert_description %}{ "name": "Description", "value": "{{ msg_text.alert_description }}" - }, { + },{% endif %}{ "name": "Status", "value": "{{ msg_text.alert_status }}" }], "markdown": true - }] + }] } diff --git a/setup.py b/setup.py index ed3bd69..efbfc3b 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def read_requirements_file(): setup(name='prom2teams', - version='1.1.2', + version='1.1.3', description='Project that redirects Prometheus Alert Manager ' 'notifications to Microsoft Teams', long_description=readme, diff --git a/tests/context.py b/tests/context.py index dd02872..8595e3b 100644 --- a/tests/context.py +++ b/tests/context.py @@ -4,3 +4,4 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../prom2teams'))) from prom2teams import server, exceptions +from prom2teams.message.parser import parse diff --git a/tests/data/jsons/all_ok.json b/tests/data/jsons/all_ok.json new file mode 100644 index 0000000..2ef68e5 --- /dev/null +++ b/tests/data/jsons/all_ok.json @@ -0,0 +1,27 @@ +{ + "receiver": "test_webhook", + "status": "resolved", + "alerts": [ + { + "status": "resolved", + "labels": { + "alertname": "DiskSpace", + "device": "rootfs", + "fstype": "rootfs", + "instance": "cs30.evilcorp", + "job": "fsociety", + "mountpoint": "/", + "severity": "severe" + }, + "annotations": { + "description": "disk usage 73% on rootfs device", + "summary": "Disk usage alert on CS30.evilcorp" + }, + "startsAt": "2017-05-09T07:01:37.803Z", + "endsAt": "2017-05-09T07:08:37.818278068Z", + "generatorURL": "my.prometheusserver.url" + } + ], + "externalURL": "my.prometheusalertmanager.url", + "version": "4" +} diff --git a/tests/data/jsons/without_mandatory_field.json b/tests/data/jsons/without_mandatory_field.json new file mode 100644 index 0000000..87b4f30 --- /dev/null +++ b/tests/data/jsons/without_mandatory_field.json @@ -0,0 +1,25 @@ +{ + "receiver": "test_webhook", + "alerts": [ + { + "labels": { + "alertname": "DiskSpace", + "device": "rootfs", + "fstype": "rootfs", + "instance": "cs30.evilcorp", + "job": "fsociety", + "mountpoint": "/", + "severity": "severe" + }, + "annotations": { + "description": "disk usage 73% on rootfs device", + "summary": "Disk usage alert on CS30.evilcorp" + }, + "startsAt": "2017-05-09T07:01:37.803Z", + "endsAt": "2017-05-09T07:08:37.818278068Z", + "generatorURL": "my.prometheusserver.url" + } + ], + "externalURL": "my.prometheusalertmanager.url", + "version": "4" +} diff --git a/tests/data/jsons/without_optional_field.json b/tests/data/jsons/without_optional_field.json new file mode 100644 index 0000000..c713131 --- /dev/null +++ b/tests/data/jsons/without_optional_field.json @@ -0,0 +1,26 @@ +{ + "receiver": "test_webhook", + "status": "resolved", + "alerts": [ + { + "status": "resolved", + "labels": { + "alertname": "DiskSpace", + "device": "rootfs", + "fstype": "rootfs", + "instance": "cs30.evilcorp", + "job": "fsociety", + "mountpoint": "/", + "severity": "severe" + }, + "annotations": { + "summary": "Disk usage alert on CS30.evilcorp" + }, + "startsAt": "2017-05-09T07:01:37.803Z", + "endsAt": "2017-05-09T07:08:37.818278068Z", + "generatorURL": "my.prometheusserver.url" + } + ], + "externalURL": "my.prometheusalertmanager.url", + "version": "4" +} diff --git a/tests/test_json_fields.py b/tests/test_json_fields.py new file mode 100644 index 0000000..dce2dde --- /dev/null +++ b/tests/test_json_fields.py @@ -0,0 +1,31 @@ +import unittest +import json + +from context import parse + + +class TestJSONFields(unittest.TestCase): + + TEST_CONFIG_FILES_PATH = 'tests/data/jsons/' + + def test_json_with_all_fields(self): + with open(self.TEST_CONFIG_FILES_PATH + 'all_ok.json') as json_data: + json_received = json.load(json_data) + alert_fields = parse(json.dumps(json_received)) + self.assertNotIn('Incorrect',str(alert_fields)) + + def test_json_without_mandatory_field(self): + with open(self.TEST_CONFIG_FILES_PATH + 'without_mandatory_field.json') as json_data: + json_received = json.load(json_data) + alert_fields = parse(json.dumps(json_received)) + self.assertIn('Incorrect',str(alert_fields)) + + def test_json_without_optional_field(self): + with open(self.TEST_CONFIG_FILES_PATH + 'without_optional_field.json') as json_data: + json_received = json.load(json_data) + alert_fields = parse(json.dumps(json_received)) + self.assertNotIn('Incorrect',str(alert_fields)) + + +if __name__ == '__main__': + unittest.main()