# What this PR does Fixes https://github.com/grafana/oncall-private/issues/2889 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
152 lines
6.1 KiB
Python
152 lines
6.1 KiB
Python
from socket import gaierror
|
|
|
|
from celery.utils.log import get_task_logger
|
|
from django.conf import settings
|
|
from django.core.mail import BadHeaderError, get_connection, send_mail
|
|
from django.utils.html import strip_tags
|
|
|
|
from apps.alerts.models import AlertGroup
|
|
from apps.base.utils import live_settings
|
|
from apps.email.alert_rendering import build_subject_and_message
|
|
from apps.email.models import EmailMessage
|
|
from apps.user_management.models import User
|
|
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
|
|
|
|
MAX_RETRIES = 1 if settings.DEBUG else 10
|
|
logger = get_task_logger(__name__)
|
|
|
|
|
|
def get_from_email(user):
|
|
if live_settings.EMAIL_FROM_ADDRESS:
|
|
return live_settings.EMAIL_FROM_ADDRESS
|
|
|
|
if settings.LICENSE == settings.CLOUD_LICENSE_NAME:
|
|
return "oncall@{}.{}".format(user.organization.stack_slug, settings.EMAIL_FROM_DOMAIN)
|
|
|
|
return live_settings.EMAIL_HOST_USER
|
|
|
|
|
|
@shared_dedicated_queue_retry_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=MAX_RETRIES)
|
|
def notify_user_async(user_pk, alert_group_pk, notification_policy_pk):
|
|
# imported here to avoid circular import error
|
|
from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
|
|
|
|
try:
|
|
user = User.objects.get(pk=user_pk)
|
|
except User.DoesNotExist:
|
|
logger.warning(f"User {user_pk} does not exist")
|
|
return
|
|
|
|
try:
|
|
alert_group = AlertGroup.objects.get(pk=alert_group_pk)
|
|
except AlertGroup.DoesNotExist:
|
|
logger.warning(f"Alert group {alert_group_pk} does not exist")
|
|
return
|
|
|
|
using_fallback_default_notification_policy_step = False
|
|
|
|
if notification_policy_pk is None:
|
|
# NOTE: `notification_policy_pk` may be None if the user has no notification policies defined, as
|
|
# email is the default backend used. see `UserNotificationPolicy.get_default_fallback_policy` for more details
|
|
notification_policy = UserNotificationPolicy.get_default_fallback_policy(user)
|
|
using_fallback_default_notification_policy_step = True
|
|
else:
|
|
try:
|
|
notification_policy = UserNotificationPolicy.objects.get(pk=notification_policy_pk)
|
|
except UserNotificationPolicy.DoesNotExist:
|
|
logger.warning(f"User notification policy {notification_policy_pk} does not exist")
|
|
return
|
|
|
|
def _create_user_notification_policy_log_record(**kwargs):
|
|
return UserNotificationPolicyLogRecord.objects.create(
|
|
**kwargs, using_fallback_default_notification_policy_step=using_fallback_default_notification_policy_step
|
|
)
|
|
|
|
def _create_email_message(**kwargs):
|
|
return EmailMessage.objects.create(
|
|
**kwargs, using_fallback_default_notification_policy_step=using_fallback_default_notification_policy_step
|
|
)
|
|
|
|
# create an error log in case EMAIL_HOST is not specified
|
|
if not live_settings.EMAIL_HOST:
|
|
_create_user_notification_policy_log_record(
|
|
author=user,
|
|
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
|
|
notification_policy=notification_policy,
|
|
alert_group=alert_group,
|
|
reason="Error while sending email",
|
|
notification_step=notification_policy.step,
|
|
notification_channel=notification_policy.notify_by,
|
|
)
|
|
logger.error("Error while sending email: empty EMAIL_HOST env variable")
|
|
return
|
|
|
|
emails_left = user.organization.emails_left(user)
|
|
if emails_left <= 0:
|
|
_create_user_notification_policy_log_record(
|
|
author=user,
|
|
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
|
|
notification_policy=notification_policy,
|
|
alert_group=alert_group,
|
|
reason="Error while sending email",
|
|
notification_step=notification_policy.step,
|
|
notification_channel=notification_policy.notify_by,
|
|
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_LIMIT_EXCEEDED,
|
|
)
|
|
_create_email_message(
|
|
represents_alert_group=alert_group,
|
|
notification_policy=notification_policy,
|
|
receiver=user,
|
|
exceeded_limit=True,
|
|
)
|
|
return
|
|
|
|
subject, html_message = build_subject_and_message(alert_group, emails_left)
|
|
|
|
message = strip_tags(html_message)
|
|
from_email = get_from_email(user)
|
|
recipient_list = [user.email]
|
|
|
|
connection = get_connection(
|
|
host=live_settings.EMAIL_HOST,
|
|
port=live_settings.EMAIL_PORT,
|
|
username=live_settings.EMAIL_HOST_USER,
|
|
password=live_settings.EMAIL_HOST_PASSWORD,
|
|
use_tls=live_settings.EMAIL_USE_TLS,
|
|
use_ssl=live_settings.EMAIL_USE_SSL,
|
|
fail_silently=False,
|
|
timeout=5,
|
|
)
|
|
|
|
try:
|
|
send_mail(subject, message, from_email, recipient_list, html_message=html_message, connection=connection)
|
|
_create_email_message(
|
|
represents_alert_group=alert_group,
|
|
notification_policy=notification_policy,
|
|
receiver=user,
|
|
exceeded_limit=False,
|
|
)
|
|
except (gaierror, BadHeaderError) as e:
|
|
# gaierror is raised when EMAIL_HOST is invalid
|
|
# BadHeaderError is raised when there's newlines in the subject
|
|
_create_user_notification_policy_log_record(
|
|
author=user,
|
|
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
|
|
notification_policy=notification_policy,
|
|
alert_group=alert_group,
|
|
reason="Error while sending email",
|
|
notification_step=notification_policy.step,
|
|
notification_channel=notification_policy.notify_by,
|
|
)
|
|
logger.error(f"Error while sending email: {e}")
|
|
return
|
|
|
|
# record success log
|
|
_create_user_notification_policy_log_record(
|
|
author=user,
|
|
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS,
|
|
notification_policy=notification_policy,
|
|
alert_group=alert_group,
|
|
notification_step=notification_policy.step,
|
|
notification_channel=notification_policy.notify_by,
|
|
)
|