|
| 1 | +import boto3 |
| 2 | +import json |
| 3 | +from urllib.parse import urlparse |
| 4 | + |
| 5 | +from elastalert.alerts import Alerter |
| 6 | +from elastalert.util import elastalert_logger, EAException |
| 7 | + |
| 8 | + |
| 9 | +def _get_region_from_sqs_url(queue_url, default_region="us-east-1"): |
| 10 | + """Infer the AWS region from an SQS queue URL like |
| 11 | + https://sqs.us-east-1.amazonaws.com/123456789012/my-queue. |
| 12 | + Falls back to default_region if it cannot be determined. |
| 13 | + """ |
| 14 | + host = urlparse(queue_url).hostname or "" |
| 15 | + parts = host.split(".") |
| 16 | + if len(parts) >= 3 and parts[0] == "sqs": |
| 17 | + return parts[1] |
| 18 | + return default_region |
| 19 | + |
| 20 | + |
| 21 | +class SqsAlerter(Alerter): |
| 22 | + """Send alert using AWS SQS service""" |
| 23 | + |
| 24 | + required_options = frozenset(["sqs_queue_url"]) |
| 25 | + |
| 26 | + def __init__(self, *args): |
| 27 | + super(SqsAlerter, self).__init__(*args) |
| 28 | + self.sqs_queue_url = self.rule.get("sqs_queue_url", None) |
| 29 | + self.sqs_aws_access_key_id = self.rule.get("sqs_aws_access_key_id") |
| 30 | + self.sqs_aws_secret_access_key = self.rule.get("sqs_aws_secret_access_key") |
| 31 | + explicit_region = self.rule.get("sqs_aws_region") |
| 32 | + if explicit_region: |
| 33 | + self.sqs_aws_region = explicit_region |
| 34 | + else: |
| 35 | + # If no region is configured explicitly, derive it from the queue URL. |
| 36 | + self.sqs_aws_region = _get_region_from_sqs_url(self.sqs_queue_url or "") |
| 37 | + self.profile = self.rule.get("sqs_aws_profile", None) |
| 38 | + |
| 39 | + def alert(self, matches): |
| 40 | + # Create the alert as a JSON object |
| 41 | + alert_data = { |
| 42 | + "rule_name": self.rule["name"], |
| 43 | + "matches": matches, |
| 44 | + } |
| 45 | + alert_text = self.create_alert_body(matches) |
| 46 | + # SQS message body limit is 1 MB; crop text at ~800KB to be safe |
| 47 | + if len(alert_text) > 800000: |
| 48 | + alert_text = alert_text[:800000] |
| 49 | + alert_text += "\n*message was cropped according to SQS limits!*" |
| 50 | + alert_data["text"] = alert_text |
| 51 | + body = json.dumps(alert_data, default=str) |
| 52 | + |
| 53 | + # If the body is still too long, remove the text field |
| 54 | + if len(body) > 1048576: |
| 55 | + alert_data["text"] = "Text message omitted due to SQS size limit." |
| 56 | + body = json.dumps(alert_data, default=str) |
| 57 | + try: |
| 58 | + # Always create the session in the configured region. SQS does not |
| 59 | + # infer the region from the queue URL; the client region must match. |
| 60 | + if self.profile is None: |
| 61 | + session = boto3.Session( |
| 62 | + aws_access_key_id=self.sqs_aws_access_key_id, |
| 63 | + aws_secret_access_key=self.sqs_aws_secret_access_key, |
| 64 | + region_name=self.sqs_aws_region, |
| 65 | + ) |
| 66 | + else: |
| 67 | + session = boto3.Session( |
| 68 | + profile_name=self.profile, |
| 69 | + region_name=self.sqs_aws_region, |
| 70 | + ) |
| 71 | + |
| 72 | + sqs_client = session.client("sqs") |
| 73 | + |
| 74 | + response = sqs_client.send_message( |
| 75 | + QueueUrl=self.sqs_queue_url, |
| 76 | + MessageBody=body, |
| 77 | + ) |
| 78 | + except Exception as e: |
| 79 | + raise EAException("Error sending Amazon SQS: %s" % e) |
| 80 | + elastalert_logger.info("Sent Amazon SQS message to %s, MessageId: %s" % (self.sqs_queue_url, response.get("MessageId"))) |
| 81 | + |
| 82 | + def get_info(self): |
| 83 | + return {"type": "sqs"} |
0 commit comments