diff --git a/engine/apps/alerts/tasks/call_ack_url.py b/engine/apps/alerts/tasks/call_ack_url.py index a9feeed1..0afa4c62 100644 --- a/engine/apps/alerts/tasks/call_ack_url.py +++ b/engine/apps/alerts/tasks/call_ack_url.py @@ -30,13 +30,34 @@ def call_ack_url(ack_url, alert_group_pk, channel, http_method="GET"): else None ) + text = "{}".format(debug_message) + footer = "{}".format(info_message) + blocks = [ + { + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": text, + }, + }, + {"type": "divider"}, + { + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": footer, + }, + }, + ] + if channel is not None: result = sc.api_call( "chat.postMessage", channel=channel, - attachments=[ - {"callback_id": "alert", "text": "{}".format(debug_message), "footer": "{}".format(info_message)}, - ], + text=text, + blocks=blocks, thread_ts=alert_group.slack_message.slack_id, mrkdwn=True, ) diff --git a/engine/apps/slack/models/slack_message.py b/engine/apps/slack/models/slack_message.py index 4fe6f470..96b3aae8 100644 --- a/engine/apps/slack/models/slack_message.py +++ b/engine/apps/slack/models/slack_message.py @@ -137,8 +137,15 @@ class SlackMessage(models.Model): else: text = "{}\nInviting {} to look at incident.".format(alert_group.long_verbose_name, user_verbal) - attachments = [ - {"color": "#c6c000", "callback_id": "alert", "text": text}, # yellow + blocks = [ + { + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": text, + }, + } ] sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token) channel_id = slack_message.channel_id @@ -147,7 +154,8 @@ class SlackMessage(models.Model): result = sc.api_call( "chat.postMessage", channel=channel_id, - attachments=attachments, + text=text, + blocks=blocks, thread_ts=slack_message.slack_id, unfurl_links=True, ) diff --git a/engine/apps/slack/scenarios/distribute_alerts.py b/engine/apps/slack/scenarios/distribute_alerts.py index 8933e717..2ec3db29 100644 --- a/engine/apps/slack/scenarios/distribute_alerts.py +++ b/engine/apps/slack/scenarios/distribute_alerts.py @@ -192,6 +192,7 @@ class AlertShootingStep(scenario_step.ScenarioStep): self._slack_client.api_call( "chat.postMessage", channel=channel_id, + text=text, attachments=[], thread_ts=alert_group.slack_message.slack_id, mrkdwn=True, @@ -480,10 +481,8 @@ class AttachGroupStep( alert_group = log_record.alert_group if log_record.type == AlertGroupLogRecord.TYPE_ATTACHED and log_record.alert_group.is_maintenance_incident: - attachments = [ - {"callback_id": "alert", "text": "{}".format(log_record.rendered_log_line_action(for_slack=True))}, - ] - self._publish_message_to_thread(alert_group, attachments) + text = f"{log_record.rendered_log_line_action(for_slack=True)}" + self.publish_message_to_thread(alert_group, text=text) if log_record.type == AlertGroupLogRecord.TYPE_FAILED_ATTACHMENT: ephemeral_text = log_record.rendered_log_line_action(for_slack=True) @@ -629,9 +628,9 @@ class CustomButtonProcessStep( f"according to escalation policy with the result `{result_message}`" ) attachments = [ - {"callback_id": "alert", "text": debug_message, "footer": text}, + {"callback_id": "alert", "text": debug_message}, ] - self._publish_message_to_thread(alert_group, attachments) + self.publish_message_to_thread(alert_group, attachments=attachments, text=text) class ResolveGroupStep( @@ -763,23 +762,27 @@ class UnAcknowledgeGroupStep( message_attachments = [ { "callback_id": "alert", - "text": f"{user_verbal} hasn't responded to an acknowledge timeout reminder." - f" Incident is unacknowledged automatically", + "text": "", "footer": "Escalation started again...", }, ] + text = ( + f"{user_verbal} hasn't responded to an acknowledge timeout reminder." + f" Incident is unacknowledged automatically" + ) if alert_group.slack_message.ack_reminder_message_ts: try: self._slack_client.api_call( "chat.update", channel=channel_id, ts=alert_group.slack_message.ack_reminder_message_ts, + text=text, attachments=message_attachments, ) except SlackAPIException as e: # post to thread if ack reminder message was deleted in Slack if e.response["error"] == "message_not_found": - self._publish_message_to_thread(alert_group, message_attachments) + self.publish_message_to_thread(alert_group, attachments=message_attachments, text=text) elif e.response["error"] == "account_inactive": logger.info( f"Skip unacknowledge slack message for alert_group {alert_group.pk} due to account_inactive" @@ -787,7 +790,7 @@ class UnAcknowledgeGroupStep( else: raise else: - self._publish_message_to_thread(alert_group, message_attachments) + self.publish_message_to_thread(alert_group, attachments=message_attachments, text=text) self._update_slack_message(alert_group) logger.debug(f"Finished process_signal in UnAcknowledgeGroupStep for alert_group {alert_group.pk}") @@ -806,18 +809,12 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): if alert_group.acknowledged_by == AlertGroup.USER: if self.user == alert_group.acknowledged_by_user: user_verbal = alert_group.acknowledged_by_user.get_user_verbal_for_team_for_slack() - attachments = [ - { - "color": "#c6c000", - "callback_id": "alert", - "text": f"{user_verbal} is confirmed to be working on this incident", - }, - ] + text = f"{user_verbal} confirmed that the incident is still acknowledged" self._slack_client.api_call( "chat.update", channel=channel, ts=message_ts, - attachments=attachments, + text=text, ) alert_group.acknowledged_by_confirmed = datetime.utcnow() alert_group.save(update_fields=["acknowledged_by_confirmed"]) @@ -830,18 +827,12 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): ) elif alert_group.acknowledged_by == AlertGroup.SOURCE: user_verbal = self.user.get_user_verbal_for_team_for_slack() - attachments = [ - { - "color": "#c6c000", - "callback_id": "alert", - "text": f"{user_verbal} is confirmed to be working on this incident", - }, - ] + text = f"{user_verbal} confirmed that the incident is still acknowledged" self._slack_client.api_call( "chat.update", channel=channel, ts=message_ts, - attachments=attachments, + text=text, ) alert_group.acknowledged_by_confirmed = datetime.utcnow() alert_group.save(update_fields=["acknowledged_by_confirmed"]) @@ -865,12 +856,13 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): alert_group = log_record.alert_group channel_id = alert_group.slack_message.channel_id user_verbal = log_record.author.get_user_verbal_for_team_for_slack(mention=True) + text = f"{user_verbal}, please confirm that you're still working on this incident." if alert_group.channel.organization.unacknowledge_timeout != Organization.UNACKNOWLEDGE_TIMEOUT_NEVER: attachments = [ { "fallback": "Are you still working on this incident?", - "text": f"{user_verbal}, please confirm that you're still working on this incident.", + "text": text, "callback_id": "alert", "attachment_type": "default", "footer": "This is a reminder that the incident is still acknowledged" @@ -896,6 +888,7 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): response = self._slack_client.api_call( "chat.postMessage", channel=channel_id, + text=text, attachments=attachments, thread_ts=alert_group.slack_message.slack_id, ) @@ -932,14 +925,8 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep): alert_group.slack_message.ack_reminder_message_ts = response["ts"] alert_group.slack_message.save(update_fields=["ack_reminder_message_ts"]) else: - attachments = [ - { - "callback_id": "alert", - "text": f"This is a reminder that the incident is still acknowledged by {user_verbal}" - f" and not resolved.", - }, - ] - self._publish_message_to_thread(alert_group, attachments) + text = f"This is a reminder that the incident is still acknowledged by {user_verbal}" + self.publish_message_to_thread(alert_group, text=text) class WipeGroupStep(scenario_step.ScenarioStep): @@ -953,15 +940,8 @@ class WipeGroupStep(scenario_step.ScenarioStep): def process_signal(self, log_record): alert_group = log_record.alert_group user_verbal = log_record.author.get_user_verbal_for_team_for_slack() - attachments = [ - { - "color": "warning", - "callback_id": "alert", - "footer": "Incident wiped", - "text": "Wiped by {}.".format(user_verbal), - }, - ] - self._publish_message_to_thread(alert_group, attachments) + text = f"Wiped by {user_verbal}" + self.publish_message_to_thread(alert_group, text=text) self._update_slack_message(alert_group) @@ -1069,21 +1049,15 @@ class UpdateLogReportMessageStep(scenario_step.ScenarioStep): logger.info(f"Cannot post log message for alert_group {alert_group.pk} because SlackMessage doesn't exist") return None - attachments = [ - { - "text": "Building escalation plan... :thinking_face:", - } - ] + text = ("Building escalation plan... :thinking_face:",) + slack_log_message = alert_group.slack_log_message if slack_log_message is None: logger.debug(f"Start posting new log message for alert_group {alert_group.pk}") try: result = self._slack_client.api_call( - "chat.postMessage", - channel=slack_message.channel_id, - thread_ts=slack_message.slack_id, - attachments=attachments, + "chat.postMessage", channel=slack_message.channel_id, thread_ts=slack_message.slack_id, text=text ) except SlackAPITokenException as e: print(e) @@ -1148,6 +1122,7 @@ class UpdateLogReportMessageStep(scenario_step.ScenarioStep): self._slack_client.api_call( "chat.update", channel=slack_message.channel_id, + text="Alert Group log", ts=slack_log_message.slack_id, attachments=attachments, ) diff --git a/engine/apps/slack/scenarios/escalation_delivery.py b/engine/apps/slack/scenarios/escalation_delivery.py index e999afd3..1c8e7572 100644 --- a/engine/apps/slack/scenarios/escalation_delivery.py +++ b/engine/apps/slack/scenarios/escalation_delivery.py @@ -34,14 +34,3 @@ class EscalationDeliveryStep(scenario_step.ScenarioStep): user_mention_as = user_verbal notify_by = " by {}".format(UserNotificationPolicy.NotificationChannel(notification_channel).label) return "Inviting {}{} to look at incident.".format(user_mention_as, notify_by) - - def notify_thread_about_action(self, alert_group, text, footer=None, color=None): - attachments = [ - { - "callback_id": "alert", - "footer": footer, - "text": text, - "color": color, - }, - ] - self._publish_message_to_thread(alert_group, attachments) diff --git a/engine/apps/slack/scenarios/notification_delivery.py b/engine/apps/slack/scenarios/notification_delivery.py index 3d04a352..056e3ddd 100644 --- a/engine/apps/slack/scenarios/notification_delivery.py +++ b/engine/apps/slack/scenarios/notification_delivery.py @@ -62,16 +62,34 @@ class NotificationDeliveryStep(scenario_step.ScenarioStep): ) def post_message_to_channel(self, text, channel, color=None, footer=None): - color_id = self.get_color_id(color) - attachments = [ - {"color": color_id, "callback_id": "alert", "footer": footer, "text": text}, + # TODO: No color in blocks, use prefix emoji? + # color_id = self.get_color_id(color) + blocks = [ + { + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": text, + }, + }, + {"type": "divider"}, + { + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": footer, + }, + }, ] try: # TODO: slack-onprem, check exceptions self._slack_client.api_call( "chat.postMessage", channel=channel, - attachments=attachments, + text=text, + blocks=blocks, unfurl_links=True, ) except SlackAPITokenException as e: diff --git a/engine/apps/slack/scenarios/scenario_step.py b/engine/apps/slack/scenarios/scenario_step.py index b49fc4c5..007e9535 100644 --- a/engine/apps/slack/scenarios/scenario_step.py +++ b/engine/apps/slack/scenarios/scenario_step.py @@ -287,7 +287,7 @@ class ScenarioStep(object): raise e logger.info(f"Finished _update_slack_message for alert_group {alert_group.pk}") - def _publish_message_to_thread(self, alert_group, attachments, mrkdwn=True, unfurl_links=True): + def publish_message_to_thread(self, alert_group, attachments=[], mrkdwn=True, unfurl_links=True, text=None): # TODO: refactor checking the possibility of sending message to slack # do not try to post message to slack if integration is rate limited if alert_group.channel.is_rate_limited_in_slack: @@ -300,6 +300,7 @@ class ScenarioStep(object): result = self._slack_client.api_call( "chat.postMessage", channel=channel_id, + text=text, attachments=attachments, thread_ts=slack_message.slack_id, mrkdwn=mrkdwn, diff --git a/engine/apps/slack/scenarios/slack_renderer.py b/engine/apps/slack/scenarios/slack_renderer.py index be5f9c6b..975dd215 100644 --- a/engine/apps/slack/scenarios/slack_renderer.py +++ b/engine/apps/slack/scenarios/slack_renderer.py @@ -16,7 +16,7 @@ class AlertGroupLogSlackRenderer: attachments = [] # get rendered logs - result = "Alert Group log:\n\n" + result = "" for log_record in all_log_records: # list of AlertGroupLogRecord and UserNotificationPolicyLogRecord logs if type(log_record) == AlertGroupLogRecord: result += f"{log_record.rendered_incident_log_line(for_slack=True)}\n" diff --git a/engine/apps/slack/scenarios/step_mixins.py b/engine/apps/slack/scenarios/step_mixins.py index ba6217cd..1c7fdf0b 100644 --- a/engine/apps/slack/scenarios/step_mixins.py +++ b/engine/apps/slack/scenarios/step_mixins.py @@ -36,16 +36,24 @@ class IncidentActionsAccessControlMixin(AccessControl): thread_ts = payload["message_ts"] except KeyError: thread_ts = payload["message"]["ts"] + + text = "Attempted to {} by {}, but failed due to a lack of permissions.".format( + self.ACTION_VERBOSE, + self.user.get_user_verbal_for_team_for_slack(), + ) + self._slack_client.api_call( "chat.postMessage", channel=payload["channel"]["id"], - attachments=[ + text=text, + blocks=[ { - "callback_id": "alert", - "text": "Attempted to {} by {}, but failed due to a lack of permissions.".format( - self.ACTION_VERBOSE, - self.user.get_user_verbal_for_team_for_slack(), - ), + "type": "section", + "block_id": "alert", + "text": { + "type": "mrkdwn", + "text": text, + }, }, ], thread_ts=None if self.send_denied_message_to_channel(payload) else thread_ts, diff --git a/engine/apps/slack/tasks.py b/engine/apps/slack/tasks.py index e2c250a0..5b33016f 100644 --- a/engine/apps/slack/tasks.py +++ b/engine/apps/slack/tasks.py @@ -98,9 +98,10 @@ def check_slack_message_exists_before_post_message_to_thread( slack_message = alert_group.get_slack_message() if slack_message is not None: - EscalationDeliveryStep(slack_team_identity, alert_group.channel.organization).notify_thread_about_action( - alert_group, text + EscalationDeliveryStep(slack_team_identity, alert_group.channel.organization).publish_message_to_thread( + alert_group, text=text ) + # check how much time has passed since alert group was created # to prevent eternal loop of restarting check_slack_message_before_post_message_to_thread elif timezone.now() < alert_group.started_at + timezone.timedelta(hours=retry_timeout_hours): @@ -239,12 +240,7 @@ def send_message_to_thread_if_bot_not_in_channel(alert_group_pk, slack_team_iden members = slack_team_identity.get_conversation_members(sc, channel_id) if bot_user_id not in members: text = f"Please invite <@{bot_user_id}> to this channel to make all features " f"available :wink:" - attachments = [ - { - "text": text, - } - ] - ScenarioStep(slack_team_identity)._publish_message_to_thread(alert_group, attachments) + ScenarioStep(slack_team_identity).publish_message_to_thread(alert_group, text=text) @shared_dedicated_queue_retry_task(autoretry_for=(Exception,), retry_backoff=True, max_retries=1) @@ -269,6 +265,7 @@ def send_debug_message_to_thread(alert_group_pk, slack_team_identity_pk): result = sc.api_call( "chat.postMessage", channel=channel_id, + text=text, attachments=[], thread_ts=current_alert_group.slack_message.slack_id, mrkdwn=True, diff --git a/engine/apps/slack/views.py b/engine/apps/slack/views.py index a9818c0c..7cb2e077 100644 --- a/engine/apps/slack/views.py +++ b/engine/apps/slack/views.py @@ -286,7 +286,6 @@ class SlackEventApiEndpointView(APIView): or payload["event"]["subtype"] == EVENT_SUBTYPE_MESSAGE_DELETED ) ): - print("Inside channel.messages event") for route in SCENARIOS_ROUTES: if ( "message_channel_type" in route diff --git a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx index 0f7090ec..2527a509 100644 --- a/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx +++ b/grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx @@ -156,7 +156,7 @@ export const PluginConfigPage = (props: Props) => { setPluginStatusOk(true); } else { setPluginStatusMessage( - `OnCall failed to connect to to this grafana via: ${plugin.meta.jsonData.grafanaUrl} check URL, network, and API key.` + `OnCall failed to connect to this grafana via: ${plugin.meta.jsonData.grafanaUrl} check URL, network, and API key.` ); setRetrySync(true); }