forked from ansible/awx
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add AWS SNS notification support for webhook (ansible#15184)
Support for AWS SNS notifications. SNS is a widespread service that is used to integrate with other AWS services(EG lambdas). This support would unlock use cases like triggering lambda functions, especially when AWX is deployed on EKS. Decisions: Data Structure - I preferred using the same structure as Webhook for message body data because it contains all job details. For now, I directly linked to Webhook to avoid duplication, but I am open to suggestions. AWS authentication - To support non-AWS native environments, I added configuration options for AWS secret key, ID, and session tokens. When entered, these values are supplied to the underlining boto3 SNS client. If not entered, it falls back to the default authentication chain to support the native AWS environment. Properly configured EKS pods are created with temporary credentials that the default authentication chain can pick automatically. --------- Signed-off-by: Ethem Cem Ozkan <ethemcem.ozkan@gmail.com>
- Loading branch information
1 parent
7845ec7
commit 37ad690
Showing
21 changed files
with
297 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
awx/main/migrations/0193_alter_notification_notification_type_and_more.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Generated by Django 4.2.6 on 2024-05-08 07:29 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('main', '0192_custom_roles'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='notification', | ||
name='notification_type', | ||
field=models.CharField( | ||
choices=[ | ||
('awssns', 'AWS SNS'), | ||
('email', 'Email'), | ||
('grafana', 'Grafana'), | ||
('irc', 'IRC'), | ||
('mattermost', 'Mattermost'), | ||
('pagerduty', 'Pagerduty'), | ||
('rocketchat', 'Rocket.Chat'), | ||
('slack', 'Slack'), | ||
('twilio', 'Twilio'), | ||
('webhook', 'Webhook'), | ||
], | ||
max_length=32, | ||
), | ||
), | ||
migrations.AlterField( | ||
model_name='notificationtemplate', | ||
name='notification_type', | ||
field=models.CharField( | ||
choices=[ | ||
('awssns', 'AWS SNS'), | ||
('email', 'Email'), | ||
('grafana', 'Grafana'), | ||
('irc', 'IRC'), | ||
('mattermost', 'Mattermost'), | ||
('pagerduty', 'Pagerduty'), | ||
('rocketchat', 'Rocket.Chat'), | ||
('slack', 'Slack'), | ||
('twilio', 'Twilio'), | ||
('webhook', 'Webhook'), | ||
], | ||
max_length=32, | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Copyright (c) 2016 Ansible, Inc. | ||
# All Rights Reserved. | ||
import json | ||
import logging | ||
|
||
import boto3 | ||
from botocore.exceptions import ClientError | ||
|
||
from awx.main.notifications.base import AWXBaseEmailBackend | ||
from awx.main.notifications.custom_notification_base import CustomNotificationBase | ||
|
||
logger = logging.getLogger('awx.main.notifications.awssns_backend') | ||
WEBSOCKET_TIMEOUT = 30 | ||
|
||
|
||
class AWSSNSBackend(AWXBaseEmailBackend, CustomNotificationBase): | ||
init_parameters = { | ||
"aws_region": {"label": "AWS Region", "type": "string", "default": ""}, | ||
"aws_access_key_id": {"label": "Access Key ID", "type": "string", "default": ""}, | ||
"aws_secret_access_key": {"label": "Secret Access Key", "type": "password", "default": ""}, | ||
"aws_session_token": {"label": "Session Token", "type": "password", "default": ""}, | ||
"sns_topic_arn": {"label": "SNS Topic ARN", "type": "string", "default": ""}, | ||
} | ||
recipient_parameter = "sns_topic_arn" | ||
sender_parameter = None | ||
|
||
DEFAULT_BODY = "{{ job_metadata }}" | ||
default_messages = CustomNotificationBase.job_metadata_messages | ||
|
||
def __init__(self, aws_region, aws_access_key_id, aws_secret_access_key, aws_session_token, fail_silently=False, **kwargs): | ||
session = boto3.session.Session() | ||
client_config = {"service_name": 'sns'} | ||
if aws_region: | ||
client_config["region_name"] = aws_region | ||
if aws_secret_access_key: | ||
client_config["aws_secret_access_key"] = aws_secret_access_key | ||
if aws_access_key_id: | ||
client_config["aws_access_key_id"] = aws_access_key_id | ||
if aws_session_token: | ||
client_config["aws_session_token"] = aws_session_token | ||
self.client = session.client(**client_config) | ||
super(AWSSNSBackend, self).__init__(fail_silently=fail_silently) | ||
|
||
def _sns_publish(self, topic_arn, message): | ||
self.client.publish(TopicArn=topic_arn, Message=message, MessageAttributes={}) | ||
|
||
def format_body(self, body): | ||
if isinstance(body, str): | ||
try: | ||
body = json.loads(body) | ||
except json.JSONDecodeError: | ||
pass | ||
|
||
if isinstance(body, dict): | ||
body = json.dumps(body) | ||
# convert dict body to json string | ||
return body | ||
|
||
def send_messages(self, messages): | ||
sent_messages = 0 | ||
for message in messages: | ||
sns_topic_arn = str(message.recipients()[0]) | ||
try: | ||
self._sns_publish(topic_arn=sns_topic_arn, message=message.body) | ||
sent_messages += 1 | ||
except ClientError as error: | ||
if not self.fail_silently: | ||
raise error | ||
|
||
return sent_messages |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from unittest import mock | ||
from django.core.mail.message import EmailMessage | ||
|
||
import awx.main.notifications.awssns_backend as awssns_backend | ||
|
||
|
||
def test_send_messages(): | ||
with mock.patch('awx.main.notifications.awssns_backend.AWSSNSBackend._sns_publish') as sns_publish_mock: | ||
aws_region = 'us-east-1' | ||
sns_topic = f"arn:aws:sns:{aws_region}:111111111111:topic-mock" | ||
backend = awssns_backend.AWSSNSBackend(aws_region=aws_region, aws_access_key_id=None, aws_secret_access_key=None, aws_session_token=None) | ||
message = EmailMessage( | ||
'test subject', | ||
{'body': 'test body'}, | ||
[], | ||
[ | ||
sns_topic, | ||
], | ||
) | ||
sent_messages = backend.send_messages( | ||
[ | ||
message, | ||
] | ||
) | ||
sns_publish_mock.assert_called_once_with(topic_arn=sns_topic, message=message.body) | ||
assert sent_messages == 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.