Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 1 | import json |
| 2 | from logging.config import dictConfig |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 3 | import time |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 4 | |
| 5 | from flask import Flask, Response, jsonify, request |
Michal Kobus | afbf4d0 | 2018-11-28 14:18:05 +0100 | [diff] [blame] | 6 | from prometheus_client import make_wsgi_app |
Michal Kobus | 4104c10 | 2019-02-22 17:05:11 +0100 | [diff] [blame] | 7 | from requests.exceptions import ConnectionError as RequestsConnectionError |
| 8 | |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 9 | from simple_salesforce.exceptions import SalesforceError |
| 10 | from simple_settings import settings |
| 11 | from werkzeug.wsgi import DispatcherMiddleware |
| 12 | from uwsgidecorators import mulefunc |
| 13 | |
Michal Kobus | 211ee92 | 2019-04-15 17:44:06 +0200 | [diff] [blame] | 14 | from sf_notifier.helpers import alert_fields_and_action, create_file |
| 15 | from sf_notifier.salesforce.client import SESSION_FILE, SalesforceClient |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 16 | from sf_notifier.salesforce.settings import CASE_STATUS |
Michal Kobus | afbf4d0 | 2018-11-28 14:18:05 +0100 | [diff] [blame] | 17 | |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 18 | |
| 19 | dictConfig(settings.LOGGING) |
| 20 | |
Michal Kobus | afbf4d0 | 2018-11-28 14:18:05 +0100 | [diff] [blame] | 21 | app = Flask(__name__) |
| 22 | app_dispatch = DispatcherMiddleware(app, { |
| 23 | '/metrics': make_wsgi_app() |
| 24 | }) |
| 25 | |
Michal Kobus | 211ee92 | 2019-04-15 17:44:06 +0200 | [diff] [blame] | 26 | |
| 27 | create_file(SESSION_FILE) |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 28 | sf_cli = SalesforceClient(settings.SF_CONFIG) |
| 29 | |
| 30 | |
Michal Kobus | 819cf02 | 2018-11-29 16:39:22 +0100 | [diff] [blame] | 31 | @app.route('/info', methods=['GET']) |
| 32 | def info(): |
Michal Kobus | 73d3352 | 2018-12-10 11:41:13 +0100 | [diff] [blame] | 33 | return jsonify({ |
| 34 | 'version': settings.VERSION, |
| 35 | 'hashing': sf_cli.hash_func.__name__ |
| 36 | }) |
Michal Kobus | 819cf02 | 2018-11-29 16:39:22 +0100 | [diff] [blame] | 37 | |
| 38 | |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 39 | @mulefunc |
| 40 | def offload(action, fields): |
| 41 | try: |
| 42 | getattr(sf_cli, action)(*fields) |
| 43 | except (SalesforceError, RequestsConnectionError) as err: |
| 44 | msg = 'Salesforce request failure: {}.'.format(err) |
| 45 | sf_cli.metrics['sf_error_count'].inc() |
| 46 | app.logger.error(msg) |
| 47 | |
| 48 | |
| 49 | def create_case_results(alert_ids): |
| 50 | cases = {} |
| 51 | is_error = False |
| 52 | |
| 53 | while len(alert_ids) > len(cases): |
| 54 | for alert_id in alert_ids: |
| 55 | case = sf_cli._registered_alerts.get(alert_id) |
| 56 | |
| 57 | if case is None: |
| 58 | continue |
| 59 | |
| 60 | if case['id'] not in CASE_STATUS: |
| 61 | cases[alert_id] = {} |
| 62 | cases[alert_id]['case'] = case['id'] |
| 63 | if case['id'] == 'error': |
| 64 | if sf_cli._feed_update_ready(case['last_update']): |
| 65 | continue |
| 66 | is_error = True |
| 67 | cases[alert_id] = {} |
| 68 | cases[alert_id]['error'] = case['error'] |
| 69 | |
| 70 | time.sleep(0.2) |
| 71 | return cases, is_error |
| 72 | |
| 73 | |
| 74 | def close_case_results(alert_ids): |
| 75 | # timeout is implicit error |
| 76 | cases = {} |
| 77 | |
| 78 | while len(alert_ids) > 0: |
| 79 | for alert_id in alert_ids: |
| 80 | case = sf_cli._registered_alerts.get(alert_id) |
| 81 | |
| 82 | if case is None: |
| 83 | cases[alert_id] = {'status': 'closed'} |
| 84 | alert_ids.pop(alert_ids.index(alert_id)) |
| 85 | time.sleep(0.2) |
| 86 | return cases, False |
| 87 | |
| 88 | |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 89 | @app.route('/hook', methods=['POST']) |
| 90 | def webhook_receiver(): |
| 91 | |
| 92 | try: |
| 93 | data = json.loads(request.data) |
| 94 | except ValueError: |
Michal Kobus | 17726ae | 2018-11-27 12:59:55 +0100 | [diff] [blame] | 95 | msg = 'Invalid request data: {}.'.format(request.data) |
| 96 | app.logger.error(msg) |
| 97 | return Response(json.dumps({'error': msg}), |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 98 | status=400, |
| 99 | mimetype='application/json') |
| 100 | |
| 101 | app.logger.info('Received requests: {}'.format(data)) |
| 102 | |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 103 | alert_ids = [] |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 104 | for alert in data['alerts']: |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 105 | alert['labels']['env_id'] = sf_cli.environment |
| 106 | alert_ids.append(sf_cli.get_alert_id(alert['labels'])) |
| 107 | fields, action = alert_fields_and_action(alert) |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 108 | |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 109 | offload(action, fields) |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 110 | |
Michal Kobus | fc1e773 | 2019-05-16 18:30:48 +0200 | [diff] [blame] | 111 | cases, is_error = globals()[action + '_results'](alert_ids) |
| 112 | |
| 113 | if is_error: |
| 114 | return Response(json.dumps(cases), |
| 115 | status=500, |
| 116 | mimetype='application/json') |
| 117 | |
Mateusz Matuszkowiak | 2820c66 | 2018-11-21 12:07:25 +0100 | [diff] [blame] | 118 | return jsonify(cases) |
| 119 | |
| 120 | |
| 121 | if __name__ == '__main__': |
| 122 | app.run() |