Skip to content

Commit

Permalink
v1.13.4
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyorlando authored Nov 26, 2024
2 parents b72e408 + fb0ede6 commit 66e5e8f
Show file tree
Hide file tree
Showing 47 changed files with 1,137 additions and 480 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,71 @@ def populate_slack_channel(apps, schema_editor):

logger.info("Starting migration to populate slack_channel field.")

sql = f"""
UPDATE {ChannelFilter._meta.db_table} AS cf
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = cf.alert_receive_channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = cf._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET cf.slack_channel_id = sc.id
WHERE cf._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ChannelFilters with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")
# NOTE: the following raw SQL only works on mysql, fall back to the less-efficient (but working) ORM method
# for non-mysql databases
#
# see the following references for more information:
# https://github.com/grafana/oncall/issues/5244#issuecomment-2493688544
# https://github.com/grafana/oncall/pull/5233/files#diff-d03cd69968936ddd363cb81aee15a643e4058d1e34bb191a407a0b8fe5fe0a77
if schema_editor.connection.vendor == "mysql":
sql = f"""
UPDATE {ChannelFilter._meta.db_table} AS cf
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = cf.alert_receive_channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = cf._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET cf.slack_channel_id = sc.id
WHERE cf._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ChannelFilters with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")
else:
queryset = ChannelFilter.objects.filter(
_slack_channel_id__isnull=False,
alert_receive_channel__organization__slack_team_identity__isnull=False,
)
total_channel_filters = queryset.count()
updated_channel_filters = 0
missing_channel_filters = 0
channel_filters_to_update = []

logger.info(f"Total channel filters to process: {total_channel_filters}")

for channel_filter in queryset:
slack_id = channel_filter._slack_channel_id
slack_team_identity = channel_filter.alert_receive_channel.organization.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)
channel_filter.slack_channel = slack_channel
channel_filters_to_update.append(channel_filter)

updated_channel_filters += 1
logger.info(
f"ChannelFilter {channel_filter.id} updated with SlackChannel {slack_channel.id} "
f"(slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_channel_filters += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for ChannelFilter {channel_filter.id}."
)

if channel_filters_to_update:
ChannelFilter.objects.bulk_update(channel_filters_to_update, ["slack_channel"])
logger.info(f"Bulk updated {len(channel_filters_to_update)} ChannelFilters with their Slack channel.")

logger.info(
f"Finished migration. Total channel filters processed: {total_channel_filters}. "
f"Channel filters updated: {updated_channel_filters}. Missing SlackChannels: {missing_channel_filters}."
)


class Migration(migrations.Migration):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,75 @@ def populate_slack_channel(apps, schema_editor):

logger.info("Starting migration to populate slack_channel field.")

sql = f"""
UPDATE {ResolutionNoteSlackMessage._meta.db_table} AS rsm
JOIN {AlertGroup._meta.db_table} AS ag ON ag.id = rsm.alert_group_id
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = ag.channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = rsm._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET rsm.slack_channel_id = sc.id
WHERE rsm._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ResolutionNoteSlackMessage records with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")
# NOTE: the following raw SQL only works on mysql, fall back to the less-efficient (but working) ORM method
# for non-mysql databases
#
# see the following references for more information:
# https://github.com/grafana/oncall/issues/5244#issuecomment-2493688544
# https://github.com/grafana/oncall/pull/5233/files#diff-4ee42d7e773e6116d7c1d0280d2dbb053422ea55bfa5802a1f26ffbf23a28867
if schema_editor.connection.vendor == "mysql":
sql = f"""
UPDATE {ResolutionNoteSlackMessage._meta.db_table} AS rsm
JOIN {AlertGroup._meta.db_table} AS ag ON ag.id = rsm.alert_group_id
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = ag.channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = rsm._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET rsm.slack_channel_id = sc.id
WHERE rsm._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ResolutionNoteSlackMessage records with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")
else:
queryset = ResolutionNoteSlackMessage.objects.filter(
_slack_channel_id__isnull=False,
alert_group__channel__organization__slack_team_identity__isnull=False,
)
total_resolution_notes = queryset.count()
updated_resolution_notes = 0
missing_resolution_notes = 0
resolution_notes_to_update = []

logger.info(f"Total resolution note slack messages to process: {total_resolution_notes}")

for resolution_note in queryset:
slack_id = resolution_note._slack_channel_id
slack_team_identity = resolution_note.alert_group.channel.organization.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)
resolution_note.slack_channel = slack_channel
resolution_notes_to_update.append(resolution_note)

updated_resolution_notes += 1
logger.info(
f"ResolutionNoteSlackMessage {resolution_note.id} updated with SlackChannel {slack_channel.id} "
f"(slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_resolution_notes += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for ResolutionNoteSlackMessage {resolution_note.id}."
)

if resolution_notes_to_update:
ResolutionNoteSlackMessage.objects.bulk_update(resolution_notes_to_update, ["slack_channel"])
logger.info(
f"Bulk updated {len(resolution_notes_to_update)} ResolutionNoteSlackMessage with their Slack channel."
)

logger.info(
f"Finished migration. Total resolution note slack messages processed: {total_resolution_notes}. "
f"Resolution note slack messages updated: {updated_resolution_notes}. "
f"Missing SlackChannels: {missing_resolution_notes}."
)


class Migration(migrations.Migration):
Expand Down
2 changes: 1 addition & 1 deletion engine/apps/alerts/models/alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -1983,7 +1983,7 @@ def slack_channel_id(self) -> str | None:
if not self.channel.organization.slack_team_identity:
return None
elif self.slack_message:
return self.slack_message.channel_id
return self.slack_message.channel.slack_id
elif self.channel_filter:
return self.channel_filter.slack_channel_id_or_org_default_id
return None
Expand Down
1 change: 1 addition & 0 deletions engine/apps/alerts/tasks/notify_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ def perform_notification(log_record_pk, use_default_notification_policy_fallback
if alert_group.slack_message:
alert_group.slack_message.send_slack_notification(user, alert_group, notification_policy)
task_logger.debug(f"Finished send_slack_notification for alert_group {alert_group.pk}.")

# check how much time has passed since log record was created
# to prevent eternal loop of restarting perform_notification task
elif timezone.now() < log_record.created_at + timezone.timedelta(hours=RETRY_TIMEOUT_HOURS):
Expand Down
18 changes: 9 additions & 9 deletions engine/apps/alerts/tests/test_alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
)
from apps.slack.client import SlackClient
from apps.slack.errors import SlackAPIMessageNotFoundError, SlackAPIRatelimitError
from apps.slack.models import SlackMessage
from apps.slack.tests.conftest import build_slack_response


Expand All @@ -24,14 +23,15 @@ def test_render_for_phone_call(
make_alert_receive_channel,
make_alert_group,
make_alert,
make_slack_channel,
make_slack_message,
):
organization, _ = make_organization_with_slack_team_identity()
organization, slack_team_identity = make_organization_with_slack_team_identity()
alert_receive_channel = make_alert_receive_channel(organization)

alert_group = make_alert_group(alert_receive_channel)
SlackMessage.objects.create(channel_id="CWER1ASD", alert_group=alert_group)

alert_group = make_alert_group(alert_receive_channel)
slack_channel = make_slack_channel(slack_team_identity)
make_slack_message(alert_group=alert_group, channel=slack_channel)

make_alert(
alert_group,
Expand Down Expand Up @@ -105,7 +105,7 @@ def test_delete(
make_alert(alert_group, raw_request_data={})

# Create Slack messages
slack_message = make_slack_message(alert_group=alert_group, channel_id="test_channel_id", slack_id="test_slack_id")
slack_message = make_slack_message(alert_group=alert_group, channel=slack_channel1)
resolution_note_1 = make_resolution_note_slack_message(
alert_group=alert_group,
user=user,
Expand Down Expand Up @@ -154,7 +154,7 @@ def test_delete(
assert mock_chat_delete.call_args_list[0] == call(
channel=resolution_note_1.slack_channel_id, ts=resolution_note_1.ts
)
assert mock_chat_delete.call_args_list[1] == call(channel=slack_message.channel_id, ts=slack_message.slack_id)
assert mock_chat_delete.call_args_list[1] == call(channel=slack_message.channel.slack_id, ts=slack_message.slack_id)
mock_reactions_remove.assert_called_once_with(
channel=resolution_note_2.slack_channel_id, name="memo", timestamp=resolution_note_2.ts
)
Expand Down Expand Up @@ -188,7 +188,7 @@ def test_delete_slack_ratelimit(
make_alert(alert_group, raw_request_data={})

# Create Slack messages
make_slack_message(alert_group=alert_group, channel_id="test_channel_id", slack_id="test_slack_id")
make_slack_message(alert_group=alert_group, channel=slack_channel1)
make_resolution_note_slack_message(
alert_group=alert_group,
user=user,
Expand Down Expand Up @@ -259,7 +259,7 @@ def test_delete_slack_api_error_other_than_ratelimit(
make_alert(alert_group, raw_request_data={})

# Create Slack messages
make_slack_message(alert_group=alert_group, channel_id="test_channel_id", slack_id="test_slack_id")
make_slack_message(alert_group=alert_group, channel=slack_channel1)
make_resolution_note_slack_message(
alert_group=alert_group,
user=user,
Expand Down
40 changes: 23 additions & 17 deletions engine/apps/alerts/tests/test_notify_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,39 +232,44 @@ def test_notify_user_perform_notification_skip_if_resolved(
def test_perform_notification_reason_to_skip_escalation_in_slack(
reason_to_skip_escalation,
error_code,
make_organization,
make_slack_team_identity,
make_organization_with_slack_team_identity,
make_user,
make_user_notification_policy,
make_alert_receive_channel,
make_alert_group,
make_user_notification_policy_log_record,
make_slack_channel,
make_slack_message,
):
organization = make_organization()
slack_team_identity = make_slack_team_identity()
organization.slack_team_identity = slack_team_identity
organization.save()
organization, slack_team_identity = make_organization_with_slack_team_identity()

user = make_user(organization=organization)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
)
alert_receive_channel = make_alert_receive_channel(organization=organization)
alert_group = make_alert_group(alert_receive_channel=alert_receive_channel)
alert_group.reason_to_skip_escalation = reason_to_skip_escalation
alert_group.save()

alert_group = make_alert_group(
alert_receive_channel=alert_receive_channel,
reason_to_skip_escalation=reason_to_skip_escalation,
)

log_record = make_user_notification_policy_log_record(
author=user,
alert_group=alert_group,
notification_policy=user_notification_policy,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_TRIGGERED,
)

if not error_code:
make_slack_message(alert_group=alert_group, channel_id="test_channel_id", slack_id="test_slack_id")
slack_channel = make_slack_channel(slack_team_identity=slack_team_identity)
make_slack_message(alert_group=alert_group, channel=slack_channel)

with patch.object(SlackMessage, "send_slack_notification") as mocked_send_slack_notification:
perform_notification(log_record.pk, False)

last_log_record = UserNotificationPolicyLogRecord.objects.last()

if error_code:
Expand All @@ -280,25 +285,24 @@ def test_perform_notification_reason_to_skip_escalation_in_slack(

@pytest.mark.django_db
def test_perform_notification_slack_prevent_posting(
make_organization,
make_slack_team_identity,
make_organization_with_slack_team_identity,
make_user,
make_user_notification_policy,
make_alert_receive_channel,
make_alert_group,
make_user_notification_policy_log_record,
make_slack_channel,
make_slack_message,
):
organization = make_organization()
slack_team_identity = make_slack_team_identity()
organization.slack_team_identity = slack_team_identity
organization.save()
organization, slack_team_identity = make_organization_with_slack_team_identity()

user = make_user(organization=organization)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
)

alert_receive_channel = make_alert_receive_channel(organization=organization)
alert_group = make_alert_group(alert_receive_channel=alert_receive_channel)
log_record = make_user_notification_policy_log_record(
Expand All @@ -308,7 +312,9 @@ def test_perform_notification_slack_prevent_posting(
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_TRIGGERED,
slack_prevent_posting=True,
)
make_slack_message(alert_group=alert_group, channel_id="test_channel_id", slack_id="test_slack_id")

slack_channel = make_slack_channel(slack_team_identity=slack_team_identity)
make_slack_message(alert_group=alert_group, channel=slack_channel)

with patch.object(SlackMessage, "send_slack_notification") as mocked_send_slack_notification:
perform_notification(log_record.pk, False)
Expand Down
Loading

0 comments on commit 66e5e8f

Please sign in to comment.