diff --git a/engine/apps/alerts/models/alert_group.py b/engine/apps/alerts/models/alert_group.py index 3db1d9ed..6bcb8d73 100644 --- a/engine/apps/alerts/models/alert_group.py +++ b/engine/apps/alerts/models/alert_group.py @@ -417,6 +417,10 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. ) prevent_posting_alerts = models.BooleanField(default=False) + """ + TODO: this column is no longer used, drop it in a subsequent PR/release + """ + maintenance_uuid = models.CharField(max_length=100, unique=True, null=True, default=None) raw_escalation_snapshot = JSONField(null=True, default=None) @@ -1983,11 +1987,7 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. channel_filter = self.channel_filter if self.slack_message: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # - # return self.slack_message.channel.slack_id - return self.slack_message._channel_id + return self.slack_message.channel.slack_id elif channel_filter and channel_filter.slack_channel_or_org_default: return channel_filter.slack_channel_or_org_default.slack_id return None diff --git a/engine/apps/alerts/tests/test_alert_group.py b/engine/apps/alerts/tests/test_alert_group.py index d4fdbf77..4e21c3e2 100644 --- a/engine/apps/alerts/tests/test_alert_group.py +++ b/engine/apps/alerts/tests/test_alert_group.py @@ -827,8 +827,8 @@ class TestAlertGroupSlackChannelID: slack_channel = make_slack_channel(slack_team_identity) slack_message = make_slack_message(slack_channel, alert_group=alert_group) - # Assert that slack_channel_id returns the _channel_id from slack_message - assert alert_group.slack_channel_id == slack_message._channel_id + # Assert that slack_channel_id returns the channel.slack_id from slack_message + assert alert_group.slack_channel_id == slack_message.channel.slack_id @pytest.mark.django_db def test_slack_channel_id_with_channel_filter( diff --git a/engine/apps/alerts/tests/test_alert_receiver_channel.py b/engine/apps/alerts/tests/test_alert_receiver_channel.py index d690cd2f..93023982 100644 --- a/engine/apps/alerts/tests/test_alert_receiver_channel.py +++ b/engine/apps/alerts/tests/test_alert_receiver_channel.py @@ -6,12 +6,10 @@ import pytest from django.conf import settings from django.db import IntegrityError from django.urls import reverse -from django.utils import timezone from apps.alerts.models import AlertReceiveChannel from common.api_helpers.utils import create_engine_url from common.exceptions import UnableToSendDemoAlert -from engine.management.commands import alertmanager_v2_migrate from settings.base import DatabaseTypes @@ -306,381 +304,3 @@ def test_create_duplicate_direct_paging_integrations(make_organization, make_tea integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, ) super(AlertReceiveChannel, arc).save() # bypass the custom save method, so that IntegrityError is raised - - -@pytest.mark.django_db -def test_alertmanager_v2_migrate_forward(make_organization, make_alert_receive_channel): - organization = make_organization() - - legacy_alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER, - slack_title_template="slack_title_template", - web_title_template="web_title_template", - grouping_id_template="grouping_id_template", - resolve_condition_template="resolve_condition_template", - ) - - alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - legacy_grafana_alerting = make_alert_receive_channel( - organization, integration=AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - ) - grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - alertmanager_v2_migrate.Command().handle(backward=False) - - legacy_alertmanager.refresh_from_db() - alertmanager.refresh_from_db() - legacy_grafana_alerting.refresh_from_db() - grafana_alerting.refresh_from_db() - - assert legacy_alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert legacy_alertmanager.alertmanager_v2_migrated_at is not None - assert legacy_alertmanager.slack_title_template is None - assert legacy_alertmanager.web_title_template is None - assert legacy_alertmanager.grouping_id_template is None - assert legacy_alertmanager.resolve_condition_template is None - assert legacy_alertmanager.alertmanager_v2_backup_templates["slack_title_template"] == "slack_title_template" - assert legacy_alertmanager.alertmanager_v2_backup_templates["web_title_template"] == "web_title_template" - assert legacy_alertmanager.alertmanager_v2_backup_templates["grouping_id_template"] == "grouping_id_template" - assert ( - legacy_alertmanager.alertmanager_v2_backup_templates["resolve_condition_template"] - == "resolve_condition_template" - ) - assert legacy_alertmanager.alertmanager_v2_backup_templates["messaging_backends_templates"] is None - - assert legacy_grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert legacy_grafana_alerting.alertmanager_v2_migrated_at is not None - assert legacy_grafana_alerting.alertmanager_v2_backup_templates is None - - assert alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager.alertmanager_v2_migrated_at is None - assert alertmanager.slack_title_template == "slack_title_template" - assert alertmanager.alertmanager_v2_backup_templates is None - - assert grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting.alertmanager_v2_migrated_at is None - assert grafana_alerting.slack_title_template == "slack_title_template" - assert grafana_alerting.alertmanager_v2_backup_templates is None - - -@pytest.mark.django_db -def test_alertmanager_v2_migrate_backward(make_organization, make_alert_receive_channel): - organization = make_organization() - - migrated_alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - alertmanager_v2_migrated_at=timezone.now(), - alertmanager_v2_backup_templates={ - "slack_title_template": "slack_title_template", - "web_title_template": "web_title_template", - "grouping_id_template": "grouping_id_template", - "resolve_condition_template": "resolve_condition_template", - }, - ) - - alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - migrated_grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - alertmanager_v2_migrated_at=timezone.now(), - ) - grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - alertmanager_v2_migrate.Command().handle(backward=True) - - migrated_alertmanager.refresh_from_db() - alertmanager.refresh_from_db() - migrated_grafana_alerting.refresh_from_db() - grafana_alerting.refresh_from_db() - - assert migrated_alertmanager.integration == AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER - assert migrated_alertmanager.alertmanager_v2_migrated_at is None - assert migrated_alertmanager.slack_title_template == "slack_title_template" - assert migrated_alertmanager.web_title_template == "web_title_template" - assert migrated_alertmanager.grouping_id_template == "grouping_id_template" - assert migrated_alertmanager.resolve_condition_template == "resolve_condition_template" - assert migrated_alertmanager.alertmanager_v2_backup_templates is None - - assert migrated_grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - assert migrated_grafana_alerting.alertmanager_v2_migrated_at is None - assert migrated_grafana_alerting.slack_title_template is None - assert migrated_grafana_alerting.web_title_template is None - assert migrated_grafana_alerting.grouping_id_template is None - assert migrated_grafana_alerting.resolve_condition_template is None - assert migrated_grafana_alerting.alertmanager_v2_backup_templates is None - - assert alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager.alertmanager_v2_migrated_at is None - assert alertmanager.slack_title_template == "slack_title_template" - assert alertmanager.alertmanager_v2_backup_templates is None - - assert grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting.alertmanager_v2_migrated_at is None - assert grafana_alerting.slack_title_template == "slack_title_template" - assert grafana_alerting.alertmanager_v2_backup_templates is None - - -@pytest.mark.django_db -def test_alertmanager_v2_migrate_forward_one(make_organization, make_alert_receive_channel): - organization = make_organization() - # org which is not going to be migrated - organization_2 = make_organization() - - legacy_alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER, - slack_title_template="slack_title_template", - web_title_template="web_title_template", - grouping_id_template="grouping_id_template", - resolve_condition_template="resolve_condition_template", - ) - alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - legacy_grafana_alerting = make_alert_receive_channel( - organization, integration=AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - ) - grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - # set up same set integrations for second org - legacy_alertmanager_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER, - slack_title_template="slack_title_template", - web_title_template="web_title_template", - grouping_id_template="grouping_id_template", - resolve_condition_template="resolve_condition_template", - ) - alertmanager_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - legacy_grafana_alerting_2 = make_alert_receive_channel( - organization_2, integration=AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - ) - grafana_alerting_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - alertmanager_v2_migrate.Command().handle(backward=False, org_id=organization.id) - - legacy_alertmanager.refresh_from_db() - alertmanager.refresh_from_db() - legacy_grafana_alerting.refresh_from_db() - grafana_alerting.refresh_from_db() - - legacy_alertmanager_2.refresh_from_db() - alertmanager_2.refresh_from_db() - legacy_grafana_alerting_2.refresh_from_db() - grafana_alerting_2.refresh_from_db() - - assert legacy_alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert legacy_alertmanager.alertmanager_v2_migrated_at is not None - assert legacy_alertmanager.slack_title_template is None - assert legacy_alertmanager.web_title_template is None - assert legacy_alertmanager.grouping_id_template is None - assert legacy_alertmanager.resolve_condition_template is None - assert legacy_alertmanager.alertmanager_v2_backup_templates["slack_title_template"] == "slack_title_template" - assert legacy_alertmanager.alertmanager_v2_backup_templates["web_title_template"] == "web_title_template" - assert legacy_alertmanager.alertmanager_v2_backup_templates["grouping_id_template"] == "grouping_id_template" - assert ( - legacy_alertmanager.alertmanager_v2_backup_templates["resolve_condition_template"] - == "resolve_condition_template" - ) - assert legacy_alertmanager.alertmanager_v2_backup_templates["messaging_backends_templates"] is None - - assert legacy_grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert legacy_grafana_alerting.alertmanager_v2_migrated_at is not None - assert legacy_grafana_alerting.alertmanager_v2_backup_templates is None - - assert alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager.alertmanager_v2_migrated_at is None - assert alertmanager.slack_title_template == "slack_title_template" - assert alertmanager.alertmanager_v2_backup_templates is None - - assert grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting.alertmanager_v2_migrated_at is None - assert grafana_alerting.slack_title_template == "slack_title_template" - assert grafana_alerting.alertmanager_v2_backup_templates is None - - # check that second org is NOT affected - - # check that legacy alertmanager not affected - assert legacy_alertmanager_2.integration == AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER - assert legacy_alertmanager_2.alertmanager_v2_migrated_at is None - assert legacy_alertmanager_2.slack_title_template == "slack_title_template" - assert legacy_alertmanager_2.web_title_template == "web_title_template" - assert legacy_alertmanager_2.grouping_id_template == "grouping_id_template" - assert legacy_alertmanager_2.resolve_condition_template == "resolve_condition_template" - assert legacy_alertmanager_2.alertmanager_v2_backup_templates is None - - # check that legacy grafana_alerting not affected - assert legacy_grafana_alerting_2.integration == AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - assert legacy_grafana_alerting_2.alertmanager_v2_migrated_at is None - assert legacy_grafana_alerting_2.alertmanager_v2_backup_templates is None - - # check that alertmanager which shouldn't be affected even by migration not touched - assert alertmanager_2.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager_2.alertmanager_v2_migrated_at is None - assert alertmanager_2.slack_title_template == "slack_title_template" - assert alertmanager_2.alertmanager_v2_backup_templates is None - - # same fpr grafana alerting - assert grafana_alerting_2.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting_2.alertmanager_v2_migrated_at is None - assert grafana_alerting_2.slack_title_template == "slack_title_template" - assert grafana_alerting_2.alertmanager_v2_backup_templates is None - - -@pytest.mark.django_db -def test_alertmanager_v2_migrate_backward_one(make_organization, make_alert_receive_channel): - organization = make_organization() - - migrated_alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - alertmanager_v2_migrated_at=timezone.now(), - alertmanager_v2_backup_templates={ - "slack_title_template": "slack_title_template", - "web_title_template": "web_title_template", - "grouping_id_template": "grouping_id_template", - "resolve_condition_template": "resolve_condition_template", - }, - ) - - alertmanager = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - migrated_grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - alertmanager_v2_migrated_at=timezone.now(), - ) - grafana_alerting = make_alert_receive_channel( - organization, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - organization_2 = make_organization() - - migrated_alertmanager_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - alertmanager_v2_migrated_at=timezone.now(), - alertmanager_v2_backup_templates={ - "slack_title_template": "slack_title_template", - "web_title_template": "web_title_template", - "grouping_id_template": "grouping_id_template", - "resolve_condition_template": "resolve_condition_template", - }, - ) - alertmanager_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER, - slack_title_template="slack_title_template", - ) - migrated_grafana_alerting_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - alertmanager_v2_migrated_at=timezone.now(), - ) - grafana_alerting_2 = make_alert_receive_channel( - organization_2, - integration=AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING, - slack_title_template="slack_title_template", - ) - - alertmanager_v2_migrate.Command().handle(backward=True, org_id=organization.id) - - migrated_alertmanager.refresh_from_db() - alertmanager.refresh_from_db() - migrated_grafana_alerting.refresh_from_db() - grafana_alerting.refresh_from_db() - - assert migrated_alertmanager.integration == AlertReceiveChannel.INTEGRATION_LEGACY_ALERTMANAGER - assert migrated_alertmanager.alertmanager_v2_migrated_at is None - assert migrated_alertmanager.slack_title_template == "slack_title_template" - assert migrated_alertmanager.web_title_template == "web_title_template" - assert migrated_alertmanager.grouping_id_template == "grouping_id_template" - assert migrated_alertmanager.resolve_condition_template == "resolve_condition_template" - assert migrated_alertmanager.alertmanager_v2_backup_templates is None - - assert migrated_grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_LEGACY_GRAFANA_ALERTING - assert migrated_grafana_alerting.alertmanager_v2_migrated_at is None - assert migrated_grafana_alerting.slack_title_template is None - assert migrated_grafana_alerting.web_title_template is None - assert migrated_grafana_alerting.grouping_id_template is None - assert migrated_grafana_alerting.resolve_condition_template is None - assert migrated_grafana_alerting.alertmanager_v2_backup_templates is None - - assert alertmanager.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager.alertmanager_v2_migrated_at is None - assert alertmanager.slack_title_template == "slack_title_template" - assert alertmanager.alertmanager_v2_backup_templates is None - - assert grafana_alerting.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting.alertmanager_v2_migrated_at is None - assert grafana_alerting.slack_title_template == "slack_title_template" - assert grafana_alerting.alertmanager_v2_backup_templates is None - - migrated_alertmanager_2.refresh_from_db() - alertmanager_2.refresh_from_db() - migrated_grafana_alerting_2.refresh_from_db() - grafana_alerting_2.refresh_from_db() - - # check that migrated integrations is second org were not touced by backward migration of other org - assert migrated_alertmanager_2.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert migrated_alertmanager_2.alertmanager_v2_migrated_at is not None - assert migrated_alertmanager_2.slack_title_template is None - assert migrated_alertmanager_2.web_title_template is None - assert migrated_alertmanager_2.grouping_id_template is None - assert migrated_alertmanager_2.resolve_condition_template is None - assert migrated_alertmanager_2.alertmanager_v2_backup_templates is not None - - assert migrated_grafana_alerting_2.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert migrated_grafana_alerting_2.alertmanager_v2_migrated_at is not None - assert migrated_grafana_alerting_2.slack_title_template is None - assert migrated_grafana_alerting_2.web_title_template is None - assert migrated_grafana_alerting_2.grouping_id_template is None - assert migrated_grafana_alerting_2.resolve_condition_template is None - assert migrated_grafana_alerting_2.alertmanager_v2_backup_templates is None - - assert alertmanager_2.integration == AlertReceiveChannel.INTEGRATION_ALERTMANAGER - assert alertmanager_2.alertmanager_v2_migrated_at is None - assert alertmanager_2.slack_title_template == "slack_title_template" - assert alertmanager_2.alertmanager_v2_backup_templates is None - - assert grafana_alerting_2.integration == AlertReceiveChannel.INTEGRATION_GRAFANA_ALERTING - assert grafana_alerting_2.alertmanager_v2_migrated_at is None - assert grafana_alerting_2.slack_title_template == "slack_title_template" - assert grafana_alerting_2.alertmanager_v2_backup_templates is None diff --git a/engine/apps/slack/0009_remove_slackmessage_active_update_task_id_db.py b/engine/apps/slack/0009_remove_slackmessage_active_update_task_id_db.py new file mode 100644 index 00000000..15617dfd --- /dev/null +++ b/engine/apps/slack/0009_remove_slackmessage_active_update_task_id_db.py @@ -0,0 +1,22 @@ +# NOTE: this is being left in this directory on purpose, it will be moved to apps/alerts/migrations +# in a separate PR/release +# +# Generated by Django 4.2.16 on 2024-12-04 12:00 + +from django.db import migrations + +import common.migrations.remove_field + + +class Migration(migrations.Migration): + dependencies = [ + ("slack", "0008_remove_slackmessage_active_update_task_id_state"), + ] + + operations = [ + common.migrations.remove_field.RemoveFieldDB( + model_name="SlackMessage", + name="active_update_task_id", + remove_state_migration=("slack", "0008_remove_slackmessage_active_update_task_id_state"), + ), + ] diff --git a/engine/apps/slack/alert_group_slack_service.py b/engine/apps/slack/alert_group_slack_service.py index 019a6e21..dc46873a 100644 --- a/engine/apps/slack/alert_group_slack_service.py +++ b/engine/apps/slack/alert_group_slack_service.py @@ -48,10 +48,7 @@ class AlertGroupSlackService: try: result = self._slack_client.chat_postMessage( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=slack_message.channel.slack_id, - channel=slack_message._channel_id, + channel=slack_message.channel.slack_id, text=text, attachments=attachments, thread_ts=slack_message.slack_id, @@ -66,11 +63,8 @@ class AlertGroupSlackService: ): return - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 alert_group.slack_messages.create( slack_id=result["ts"], organization=alert_group.channel.organization, - _channel_id=slack_message.channel.slack_id, channel=slack_message.channel, ) diff --git a/engine/apps/slack/migrations/0008_remove_slackmessage_active_update_task_id_state.py b/engine/apps/slack/migrations/0008_remove_slackmessage_active_update_task_id_state.py new file mode 100644 index 00000000..eaa7d5be --- /dev/null +++ b/engine/apps/slack/migrations/0008_remove_slackmessage_active_update_task_id_state.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2024-12-04 12:00 + +import common.migrations.remove_field +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('slack', '0007_migrate_slackmessage_channel_id'), + ] + + operations = [ + common.migrations.remove_field.RemoveFieldState( + model_name='SlackMessage', + name='active_update_task_id', + ), + ] diff --git a/engine/apps/slack/models/slack_message.py b/engine/apps/slack/models/slack_message.py index 3355b701..de9eb539 100644 --- a/engine/apps/slack/models/slack_message.py +++ b/engine/apps/slack/models/slack_message.py @@ -89,11 +89,6 @@ class SlackMessage(models.Model): related_name="slack_messages", ) - active_update_task_id = models.CharField(max_length=100, null=True, default=None) - """ - DEPRECATED/TODO: drop this field in a separate PR/release - """ - class Meta: # slack_id is unique within the context of a channel or conversation constraints = [ @@ -112,10 +107,7 @@ class SlackMessage(models.Model): try: result = SlackClient(self.slack_team_identity).chat_getPermalink( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=self.channel.slack_id, - channel=self._channel_id, + channel=self.channel.slack_id, message_ts=self.slack_id, ) except SlackAPIError: @@ -128,9 +120,7 @@ class SlackMessage(models.Model): @property def deep_link(self) -> str: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - return f"https://slack.com/app_redirect?channel={self._channel_id}&team={self.slack_team_identity.slack_id}&message={self.slack_id}" + return f"https://slack.com/app_redirect?channel={self.channel.slack_id}&team={self.slack_team_identity.slack_id}&message={self.slack_id}" @classmethod def send_slack_notification( @@ -232,12 +222,9 @@ class SlackMessage(models.Model): ).save() return else: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 alert_group.slack_messages.create( slack_id=result["ts"], organization=organization, - _channel_id=slack_channel.slack_id, channel=slack_channel, ) diff --git a/engine/apps/slack/models/slack_user_identity.py b/engine/apps/slack/models/slack_user_identity.py index 7d04090b..57882536 100644 --- a/engine/apps/slack/models/slack_user_identity.py +++ b/engine/apps/slack/models/slack_user_identity.py @@ -132,12 +132,9 @@ class SlackUserIdentity(models.Model): "elements": [ { "type": "mrkdwn", - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # f"<#{slack_message.channel.slack_id}>.\n" "text": ( f"You received this message because you're not a member of " - f"<#{slack_message._channel_id}>.\n" + f"<#{slack_message.channel.slack_id}>.\n" "Please join the channel to get notified right in the alert group thread." ), } diff --git a/engine/apps/slack/scenarios/alertgroup_appearance.py b/engine/apps/slack/scenarios/alertgroup_appearance.py index 1a201ecd..2f7a0113 100644 --- a/engine/apps/slack/scenarios/alertgroup_appearance.py +++ b/engine/apps/slack/scenarios/alertgroup_appearance.py @@ -87,10 +87,7 @@ class UpdateAppearanceStep(scenario_step.ScenarioStep): slack_message = alert_group.slack_message self._slack_client.chat_update( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=slack_message.channel.slack_id, - channel=slack_message._channel_id, + channel=slack_message.channel.slack_id, ts=slack_message.slack_id, attachments=alert_group.render_slack_attachments(), blocks=alert_group.render_slack_blocks(), diff --git a/engine/apps/slack/scenarios/distribute_alerts.py b/engine/apps/slack/scenarios/distribute_alerts.py index 8be2095f..b22cc2a3 100644 --- a/engine/apps/slack/scenarios/distribute_alerts.py +++ b/engine/apps/slack/scenarios/distribute_alerts.py @@ -158,12 +158,9 @@ class IncomingAlertStep(scenario_step.ScenarioStep): blocks=alert_group.render_slack_blocks(), ) - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 alert_group.slack_messages.create( slack_id=result["ts"], organization=alert_group.channel.organization, - _channel_id=slack_channel.slack_id, channel=slack_channel, ) @@ -554,10 +551,7 @@ class AttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep): if slack_user_identity: self._slack_client.chat_postEphemeral( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=alert_group.slack_message.channel.slack_id, - channel=alert_group.slack_message._channel_id, + channel=alert_group.slack_message.channel.slack_id, user=slack_user_identity.slack_id, text="{}{}".format(ephemeral_text[:1].upper(), ephemeral_text[1:]), unfurl_links=True, @@ -804,10 +798,7 @@ class UnAcknowledgeGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep) if slack_message.ack_reminder_message_ts: try: self._slack_client.chat_update( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=slack_message.channel.slack_id, - channel=slack_message._channel_id, + channel=slack_message.channel.slack_id, ts=slack_message.ack_reminder_message_ts, text=text, attachments=message_attachments, @@ -921,12 +912,9 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): except (SlackAPITokenError, SlackAPIChannelArchivedError, SlackAPIChannelNotFoundError): pass else: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 alert_group.slack_messages.create( slack_id=response["ts"], organization=organization, - _channel_id=slack_channel.slack_id, channel=slack_channel, ) @@ -981,13 +969,7 @@ class DeleteGroupStep(scenario_step.ScenarioStep): # Remove alert group Slack messages for message in alert_group.slack_messages.all(): try: - self._slack_client.chat_delete( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=message.channel.slack_id, - channel=message._channel_id, - ts=message.slack_id, - ) + self._slack_client.chat_delete(channel=message.channel.slack_id, ts=message.slack_id) except SlackAPIRatelimitError: # retries on ratelimit are handled in apps.alerts.tasks.delete_alert_group.delete_alert_group raise diff --git a/engine/apps/slack/scenarios/notification_delivery.py b/engine/apps/slack/scenarios/notification_delivery.py index fc399231..d77286ad 100644 --- a/engine/apps/slack/scenarios/notification_delivery.py +++ b/engine/apps/slack/scenarios/notification_delivery.py @@ -18,11 +18,7 @@ class NotificationDeliveryStep(scenario_step.ScenarioStep): user = log_record.author alert_group = log_record.alert_group - - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # slack_channel_id = alert_group_slack_message.channel.slack_id - slack_channel_id = alert_group.slack_message._channel_id + slack_channel_id = alert_group.slack_message.channel.slack_id user_verbal_with_mention = user.get_username_with_slack_verbal(mention=True) diff --git a/engine/apps/slack/scenarios/resolution_note.py b/engine/apps/slack/scenarios/resolution_note.py index 9fe95368..63174b08 100644 --- a/engine/apps/slack/scenarios/resolution_note.py +++ b/engine/apps/slack/scenarios/resolution_note.py @@ -92,13 +92,10 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep): return try: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 slack_message = SlackMessage.objects.get( slack_id=payload["message"]["thread_ts"], organization__slack_team_identity=slack_team_identity, - _channel_id=channel_id, - # channel__slack_id=channel_id, + channel__slack_id=channel_id, ) except SlackMessage.DoesNotExist: if settings.UNIFIED_SLACK_APP_ENABLED: @@ -164,14 +161,10 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep): slack_channel = SlackChannel.objects.get( slack_id=channel_id, slack_team_identity=slack_team_identity ) - - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 slack_message = SlackMessage.objects.get( slack_id=thread_ts, organization__slack_team_identity=slack_team_identity, - # channel__slack_id=channel_id, - _channel_id=channel_id, + channel__slack_id=channel_id, ) alert_group = slack_message.alert_group @@ -262,14 +255,10 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep): resolution_note_slack_message = resolution_note.resolution_note_slack_message alert_group = resolution_note.alert_group alert_group_slack_message = alert_group.slack_message + slack_channel_id = alert_group_slack_message.channel.slack_id blocks = self.get_resolution_note_blocks(resolution_note) - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # slack_channel_id = alert_group_slack_message.channel.slack_id - slack_channel_id = alert_group_slack_message._channel_id - slack_channel = SlackChannel.objects.get( slack_id=slack_channel_id, slack_team_identity=self.slack_team_identity ) diff --git a/engine/apps/slack/scenarios/shift_swap_requests.py b/engine/apps/slack/scenarios/shift_swap_requests.py index 48637131..79085c7a 100644 --- a/engine/apps/slack/scenarios/shift_swap_requests.py +++ b/engine/apps/slack/scenarios/shift_swap_requests.py @@ -161,12 +161,9 @@ class BaseShiftSwapRequestStep(scenario_step.ScenarioStep): blocks=self._generate_blocks(shift_swap_request), ) - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 return SlackMessage.objects.create( slack_id=result["ts"], organization=self.organization, - _channel_id=shift_swap_request.slack_channel_id, channel=shift_swap_request.slack_channel, ) @@ -186,11 +183,8 @@ class BaseShiftSwapRequestStep(scenario_step.ScenarioStep): if not shift_swap_request.slack_message: return - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 self._slack_client.chat_postMessage( - # channel=shift_swap_request.slack_message.channel.slack_id, - channel=shift_swap_request.slack_message._channel_id, + channel=shift_swap_request.slack_message.channel.slack_id, thread_ts=shift_swap_request.slack_message.slack_id, reply_broadcast=reply_broadcast, blocks=blocks, diff --git a/engine/apps/slack/scenarios/slack_channel_integration.py b/engine/apps/slack/scenarios/slack_channel_integration.py index 140b2af7..7e04e024 100644 --- a/engine/apps/slack/scenarios/slack_channel_integration.py +++ b/engine/apps/slack/scenarios/slack_channel_integration.py @@ -66,13 +66,10 @@ class SlackChannelMessageEventStep(scenario_step.ScenarioStep): message_ts = payload["event"]["ts"] try: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 slack_message = SlackMessage.objects.get( slack_id=thread_ts, organization__slack_team_identity=self.slack_team_identity, - _channel_id=channel_id, - # channel__slack_id=channel_id, + channel__slack_id=channel_id, ) except SlackMessage.DoesNotExist: return diff --git a/engine/apps/slack/scenarios/step_mixins.py b/engine/apps/slack/scenarios/step_mixins.py index 8813ac32..c8a367b7 100644 --- a/engine/apps/slack/scenarios/step_mixins.py +++ b/engine/apps/slack/scenarios/step_mixins.py @@ -63,12 +63,9 @@ class AlertGroupActionsMixin: slack_team_identity=slack_team_identity, ) - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 SlackMessage.objects.create( slack_id=message_id, organization=alert_group.channel.organization, - _channel_id=slack_channel.slack_id, channel=slack_channel, alert_group=alert_group, ) @@ -179,12 +176,9 @@ class AlertGroupActionsMixin: logger.warning(f"alert_group_pk not found in payload, fetching SlackMessage from DB. message_ts: {message_ts}") # Get SlackMessage from DB - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 slack_message = SlackMessage.objects.get( slack_id=message_ts, organization__slack_team_identity=slack_team_identity, - _channel_id=channel_id, - # channel__slack_id=channel_id, + channel__slack_id=channel_id, ) return slack_message.alert_group diff --git a/engine/apps/slack/tasks.py b/engine/apps/slack/tasks.py index deff2cba..72908179 100644 --- a/engine/apps/slack/tasks.py +++ b/engine/apps/slack/tasks.py @@ -98,10 +98,7 @@ def update_alert_group_slack_message(slack_message_pk: int) -> None: try: slack_client.chat_update( - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p173255546 - # channel=slack_message.channel.slack_id, - channel=slack_message._channel_id, + channel=slack_message.channel.slack_id, ts=slack_message.slack_id, attachments=alert_group.render_slack_attachments(), blocks=alert_group.render_slack_blocks(), diff --git a/engine/apps/slack/tests/scenario_steps/test_slack_channel.py b/engine/apps/slack/tests/scenario_steps/test_slack_channel.py index a0205e7e..4ca4b27a 100644 --- a/engine/apps/slack/tests/scenario_steps/test_slack_channel.py +++ b/engine/apps/slack/tests/scenario_steps/test_slack_channel.py @@ -3,7 +3,7 @@ from unittest.mock import patch import pytest from django.utils import timezone -from apps.slack.models import SlackChannel +from apps.slack.models import SlackChannel, SlackMessage from apps.slack.scenarios import slack_channel as slack_channel_scenarios @@ -84,6 +84,7 @@ class TestSlackChannelDeletedEventStep: self, make_organization_and_user_with_slack_identities, make_slack_channel, + make_slack_message, ) -> None: ( organization, @@ -92,6 +93,7 @@ class TestSlackChannelDeletedEventStep: slack_user_identity, ) = make_organization_and_user_with_slack_identities() slack_channel = make_slack_channel(slack_team_identity) + make_slack_message(slack_channel, organization=organization) slack_channel_id = slack_channel.slack_id # Ensure the SlackChannel exists @@ -100,6 +102,8 @@ class TestSlackChannelDeletedEventStep: slack_team_identity=slack_team_identity, ).exists() + assert SlackMessage.objects.count() == 1 + step = slack_channel_scenarios.SlackChannelDeletedEventStep(slack_team_identity, organization, user) step.process_scenario(slack_user_identity, slack_team_identity, {"event": {"channel": slack_channel_id}}) @@ -109,6 +113,9 @@ class TestSlackChannelDeletedEventStep: slack_team_identity=slack_team_identity, ).exists() + # Slack messages should be cascade deleted when their channel is deleted + assert SlackMessage.objects.count() == 0 + def test_process_scenario_channel_does_not_exist( self, make_organization_and_user_with_slack_identities, diff --git a/engine/apps/slack/tests/tasks/test_update_alert_group_slack_message.py b/engine/apps/slack/tests/tasks/test_update_alert_group_slack_message.py index ee038332..1cd42345 100644 --- a/engine/apps/slack/tests/tasks/test_update_alert_group_slack_message.py +++ b/engine/apps/slack/tests/tasks/test_update_alert_group_slack_message.py @@ -190,7 +190,7 @@ class TestUpdateAlertGroupSlackMessageTask: # Assert that SlackClient.chat_update was called with correct parameters mock_chat_update.assert_called_once_with( - channel=slack_message._channel_id, + channel=slack_message.channel.slack_id, ts=slack_message.slack_id, attachments=alert_group.render_slack_attachments(), blocks=alert_group.render_slack_blocks(), diff --git a/engine/apps/slack/views.py b/engine/apps/slack/views.py index e64ea157..681640c7 100644 --- a/engine/apps/slack/views.py +++ b/engine/apps/slack/views.py @@ -516,13 +516,10 @@ class SlackEventApiEndpointView(APIView): return None try: - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 slack_message = SlackMessage.objects.get( slack_id=message_ts, organization__slack_team_identity=slack_team_identity, - _channel_id=channel_id, - # channel__slack_id=channel_id, + channel__slack_id=channel_id, ) except SlackMessage.DoesNotExist: return None diff --git a/engine/conftest.py b/engine/conftest.py index bcd7c47c..5fcd1da6 100644 --- a/engine/conftest.py +++ b/engine/conftest.py @@ -528,12 +528,9 @@ def make_slack_user_identity(): @pytest.fixture def make_slack_message(): def _make_slack_message(channel, alert_group=None, organization=None, **kwargs): - # TODO: once _channel_id has been fully migrated to channel, remove _channel_id - # see https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099 return SlackMessageFactory( alert_group=alert_group, organization=organization or alert_group.channel.organization, - _channel_id=channel.slack_id, channel=channel, **kwargs, ) diff --git a/engine/engine/management/commands/alertmanager_v2_migrate.py b/engine/engine/management/commands/alertmanager_v2_migrate.py deleted file mode 100644 index c5f10c5d..00000000 --- a/engine/engine/management/commands/alertmanager_v2_migrate.py +++ /dev/null @@ -1,168 +0,0 @@ -from django.core.management.base import BaseCommand -from django.db import transaction -from django.db.models import Q -from django.utils import timezone - -from apps.alerts.models import AlertReceiveChannel - -ALERTMANAGER = "alertmanager" -LEGACY_ALERTMANAGER = "legacy_alertmanager" -GRAFANA_ALERTING = "grafana_alerting" -LEGACY_GRAFANA_ALERTING = "legacy_grafana_alerting" -TEMPLATE_FIELDS = [ - "web_title_template", - "web_message_template", - "web_image_url_template", - "sms_title_template", - "phone_call_title_template", - "source_link_template", - "grouping_id_template", - "resolve_condition_template", - "acknowledge_condition_template", - "slack_title_template", - "slack_message_template", - "slack_image_url_template", - "telegram_title_template", - "telegram_message_template", - "telegram_image_url_template", - "messaging_backends_templates", -] - - -class Command(BaseCommand): - def add_arguments(self, parser): - parser.add_argument("--backward", action="store_true", help="Run the migration backward.") - parser.add_argument( - "--org_id", type=int, help="Org id to perform the migration. " "If not present will migrate all." - ) - - def handle(self, *args, **options): - org_id = options.get("org_id", None) - if options["backward"]: - self.migrate_backward(org_id) - else: - self.migrate_forward(org_id) - - @transaction.atomic - def migrate_forward(self, org_id=None): - now = timezone.now() - self.stdout.write(f"Forward migration started at {now}.") - - self.stdout.write( - "Migrating legacy Alertmanager integrations " - "(updating fields 'integration' and 'alertmanager_v2_migrated_at')." - ) - alertmanager_to_update = AlertReceiveChannel.objects.filter(integration=LEGACY_ALERTMANAGER) - if org_id: - alertmanager_to_update = alertmanager_to_update.filter(organization_id=org_id) - num_updated = alertmanager_to_update.update(integration=ALERTMANAGER, alertmanager_v2_migrated_at=now) - self.stdout.write(f"Migrated {num_updated} legacy Alertmanager integrations.") - - self.stdout.write( - "Migrating legacy Grafana Alerting integrations " - "(updating fields 'integration' and 'alertmanager_v2_migrated_at')." - ) - alerting_to_update = AlertReceiveChannel.objects.filter(integration=LEGACY_GRAFANA_ALERTING) - if org_id: - alerting_to_update = alerting_to_update.filter(organization_id=org_id) - num_updated = alerting_to_update.update(integration=GRAFANA_ALERTING, alertmanager_v2_migrated_at=now) - - self.stdout.write(f"Migrated {num_updated} legacy Grafana Alerting integrations.") - - self.stdout.write("Fetching integrations to back up & reset templates.") - alert_receive_channels = AlertReceiveChannel.objects.filter( - Q( - **{f"{field}__isnull": False for field in TEMPLATE_FIELDS}, - _connector=Q.OR, - ), - integration__in=[ALERTMANAGER, GRAFANA_ALERTING], - alertmanager_v2_migrated_at__isnull=False, - ) - if org_id: - alert_receive_channels = alert_receive_channels.filter(organization_id=org_id) - - self.stdout.write(f"Backing up & resetting templates for {len(alert_receive_channels)} integrations.") - - for alert_receive_channel in alert_receive_channels: - self.stdout.write( - f"Backing up & resetting templates for integration {alert_receive_channel.public_primary_key}." - ) - alert_receive_channel.alertmanager_v2_backup_templates = { - field: getattr(alert_receive_channel, field) for field in TEMPLATE_FIELDS - } - for field in TEMPLATE_FIELDS: - setattr(alert_receive_channel, field, None) - - self.stdout.write(f"Bulk updating templates for {len(alert_receive_channels)} integrations.") - num_updated = AlertReceiveChannel.objects.bulk_update( - alert_receive_channels, - fields=[ - *TEMPLATE_FIELDS, - "alertmanager_v2_backup_templates", - ], - batch_size=1000, - ) - self.stdout.write(f"Bulk updated templates for {num_updated} integrations.") - - self.stdout.write("Forward migration finished.") - - @transaction.atomic - def migrate_backward(self, org_id=None): - now = timezone.now() - self.stdout.write(f"Backward migration started at {now}.") - - self.stdout.write( - "Backward migrating Alertmanager integrations " - "(updating fields 'integration' and 'alertmanager_v2_migrated_at')." - ) - - alertmanagers_to_restore = AlertReceiveChannel.objects.filter( - integration=ALERTMANAGER, alertmanager_v2_migrated_at__isnull=False - ) - if org_id: - alertmanagers_to_restore = alertmanagers_to_restore.filter(organization_id=org_id) - num_updated = alertmanagers_to_restore.update(integration=LEGACY_ALERTMANAGER, alertmanager_v2_migrated_at=None) - self.stdout.write(f"Backward migrated {num_updated} Alertmanager integrations.") - - self.stdout.write( - "Backward migrating Grafana Alerting integrations " - "(updating fields 'integration' and 'alertmanager_v2_migrated_at')." - ) - - alerting_to_restore = AlertReceiveChannel.objects.filter( - integration=GRAFANA_ALERTING, alertmanager_v2_migrated_at__isnull=False - ) - if org_id: - alerting_to_restore = alerting_to_restore.filter(organization_id=org_id) - num_updated = alerting_to_restore.update(integration=LEGACY_GRAFANA_ALERTING, alertmanager_v2_migrated_at=None) - self.stdout.write(f"Backward migrated {num_updated} Grafana Alerting integrations.") - - self.stdout.write("Fetching integrations to restore templates from backup.") - alert_receive_channels = AlertReceiveChannel.objects.filter( - integration__in=[LEGACY_ALERTMANAGER, LEGACY_GRAFANA_ALERTING], - alertmanager_v2_backup_templates__isnull=False, - ) - if org_id: - alert_receive_channels = alert_receive_channels.filter(organization_id=org_id) - self.stdout.write(f"Restoring templates for {len(alert_receive_channels)} integrations.") - - for alert_receive_channel in alert_receive_channels: - self.stdout.write(f"Restoring templates for integration {alert_receive_channel.public_primary_key}.") - if alert_receive_channel.alertmanager_v2_backup_templates is None: - continue - for field in TEMPLATE_FIELDS: - setattr(alert_receive_channel, field, alert_receive_channel.alertmanager_v2_backup_templates.get(field)) - alert_receive_channel.alertmanager_v2_backup_templates = None - - self.stdout.write(f"Bulk updating templates for {len(alert_receive_channels)} integrations.") - num_updated = AlertReceiveChannel.objects.bulk_update( - alert_receive_channels, - fields=[ - *TEMPLATE_FIELDS, - "alertmanager_v2_backup_templates", - ], - batch_size=1000, - ) - self.stdout.write(f"Bulk updated templates for {num_updated} integrations.") - - self.stdout.write("Backward migration finished.") diff --git a/engine/engine/management/commands/batch_migrate_slack_message_channel.py b/engine/engine/management/commands/batch_migrate_slack_message_channel.py deleted file mode 100644 index 2c302b94..00000000 --- a/engine/engine/management/commands/batch_migrate_slack_message_channel.py +++ /dev/null @@ -1,99 +0,0 @@ -import time - -from django.core.management.base import BaseCommand -from django.db import connection, transaction - -from apps.slack.models import SlackChannel, SlackMessage -from apps.user_management.models import Organization - - -class Command(BaseCommand): - help = "Batch updates SlackMessage.channel_id in chunks to avoid locking the table." - - def handle(self, *args, **options): - start_time = time.time() - self.stdout.write("Starting batch update of SlackMessage.channel_id...") - - # Step 1: Determine the queryset to update - # qs is ordered by id to ensure consistent batching - # since id is indexed, this ordering operation "should" be more efficient (as opposed to say created_at - # which we don't have an index on) - qs = SlackMessage.objects.filter( - _channel_id__isnull=False, # old column - organization__isnull=False, - channel_id__isnull=True, # new column - ).order_by("id") - - total_records = qs.count() - if total_records == 0: - self.stdout.write("No records to update.") - return - - self.stdout.write(f"Total records to update: {total_records}") - - # some considerations here.. - # - # Large IN clauses can be inefficient. Keep BATCH_SIZE reasonable (e.g., 1000) - # Fetching large batches of IDs consumes memory. With a BATCH_SIZE of 1000, this "should" be manageable - # - # references - # https://stackoverflow.com/a/5919165 - BATCH_SIZE = 1000 - - total_batches = (total_records + BATCH_SIZE - 1) // BATCH_SIZE - self.stdout.write(f"Batch size: {BATCH_SIZE}") - self.stdout.write(f"Total batches: {total_batches}") - - records_updated = 0 - batch_number = 1 - - # Process updates in batches - while True: - # Get the next batch of IDs - batch_qs = qs[:BATCH_SIZE] - - # collect the IDs to be updated - batch_ids = list(batch_qs.values_list("id", flat=True)) - - if not batch_ids: - break # No more records to process - - placeholders = ", ".join(["%s"] * len(batch_ids)) - update_query = f""" - UPDATE - {SlackMessage._meta.db_table} AS sm - INNER JOIN {Organization._meta.db_table} AS org - ON org.id = sm.organization_id - INNER JOIN {SlackChannel._meta.db_table} AS sc - ON sc.slack_id = sm._channel_id - AND sc.slack_team_identity_id = org.slack_team_identity_id - SET - sm.channel_id = sc.id - WHERE - sm.id IN ({placeholders}) - """ - params = batch_ids - - try: - # Execute the update - with transaction.atomic(): - with connection.cursor() as cursor: - cursor.execute(update_query, params) - batch_records_updated = cursor.rowcount - records_updated += batch_records_updated - - self.stdout.write(f"Batch {batch_number}/{total_batches}: Updated {batch_records_updated} records") - except Exception as e: - self.stderr.write(f"Error updating batch {batch_number}: {e}") - # Optionally, decide whether to continue or abort - continue - - # Remove processed records from queryset for next batch - qs = qs.exclude(id__in=batch_ids) - - batch_number += 1 - - end_time = time.time() - total_time = end_time - start_time - self.stdout.write(f"Batch update completed successfully. Total records updated: {records_updated}") - self.stdout.write(f"Total time taken: {total_time:.2f} seconds")