From e0ba4d397fda5260461f0b856f1495d04c00d662 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 29 Apr 2025 15:29:59 +0200 Subject: [PATCH 01/10] Redirecting from old docs page --- docs/conf.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 40c014dea..907de6fc0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -123,9 +123,54 @@ "catalog/triggers/prometheus.html": "/master/configuration/alertmanager-integration/index.html", "playbook-reference/prometheus-examples/alert-remediation.html": "/master/playbook-reference/automatic-remediation-examples/index.html", "configuration/ai-analysis.html": "/master/configuration/holmesgpt/index.html", - - -} + "coverage.html": "/master/how-it-works/coverage.html", + "tutorials/python-profiling.html": "/master/playbook-reference/actions/python-troubleshooting.html#python-profiler", + "tutorials/more-tutorials.html": "/master/community-tutorials.html", + "common-errors.html": "/master/help.html#common-errors", + "user-guide/defining-playbooks.html": "/master/playbook-reference/defining-playbooks/index.html", + "user-guide/global-config.html": "/master/setup-robusta/additional-settings.html#global-config", + "user-guide/configuration-secrets.html": "master/setup-robusta/configuration-secrets.html", + "user-guide/additional-playbooks.html": "/master/playbook-reference/defining-playbooks/external-playbook-repositories.html", + "user-guide/embedded-prometheus.html": "/master/configuration/alertmanager-integration/embedded-prometheus.html#enabling-the-embedded-prometheus", + "user-guide/node-selector.html": "/master/setup-robusta/node-selector.html", + "user-guide/interactivity.html": "/master/setup-robusta/additional-settings.html#two-way-interactivity", + "user-guide/flow-control.html": "/master/playbook-reference/defining-playbooks/playbook-advanced.html#using-filters-to-restrict-triggers", + "catalog/triggers/index.html": "/master/playbook-reference/triggers/index.html", + "catalog/triggers/kubernetes.html": "/master/playbook-reference/triggers/kubernetes.html", + "catalog/triggers/smart.html": "/master/playbook-reference/triggers/kubernetes.html", + "catalog/triggers/scheduled.html": "/master/playbook-reference/triggers/scheduled.html", + "catalog/triggers/manual-triggers.html": "/master/playbook-reference/triggers/manual-triggers.html", + "catalog/triggers/webhook.html": "/master/playbook-reference/triggers/webhook.html", + "catalog/triggers/elasticsearch.html": "/master/playbook-reference/triggers/elasticsearch.html", + "catalog/actions/remediation.html": "/master/playbook-reference/automatic-remediation-examples/index.html", + "catalog/actions/change-tracking.html": "/master/playbook-reference/actions/change-tracking.html#change-tracking", + "catalog/actions/java-troubleshooting.html": "/master/playbook-reference/actions/java-troubleshooting.html", + "catalog/actions/miscellaneous.html": "/master/playbook-reference/actions/miscellaneous.html", + "catalog/actions/message-formatting.html":"/master/playbook-reference/actions/message-formatting.html", + "catalog/sinks/RobustaUI.html": "/master/configuration/sinks/RobustaUI.html", + "catalog/sinks/discord.html": "/master/configuration/sinks/discord.html", + "catalog/sinks/kafka.html": "/master/configuration/sinks/kafka.html", + "catalog/sinks/DataDog.html": "/master/configuration/sinks/DataDog.html", + "catalog/sinks/ms-teams.html": "/master/configuration/sinks/ms-teams.html", + "catalog/sinks/mattermost.html": "/master/configuration/sinks/mattermost.html", + "catalog/sinks/webex.html": "/master/configuration/sinks/webex.html", + "catalog/sinks/VictorOps.html": "/master/configuration/sinks/VictorOps.html", + "catalog/sinks/file.html": "/master/configuration/sinks/file.html", + "user-guide/trigger-action-binding.html": "/master/playbook-reference/defining-playbooks/playbook-basics.html#understanding-actions", + "advanced/privacy-and-security.html": "/master/setup-robusta/privacy-and-security.html", + "advanced/robusta-ui-triggers.html": "/master/setup-robusta/installation/index.html", + "developer-guide/actions/index.html": "/master/playbook-reference/actions/index.html", + "developer-guide/actions/writing-playbooks.html":"/master/playbook-reference/actions/develop-actions/writing-playbooks.html#the-basics", + "developer-guide/actions/playbook-repositories.html": "/master/playbook-reference/actions/develop-actions/playbook-repositories.html", + "developer-guide/actions/triggers-and-events.html": "/master/playbook-reference/actions/develop-actions/triggers-and-events.html", + "developer-guide/actions/utility-functions.html": "/master/playbook-reference/actions/develop-actions/utility-functions.html", + "developer-guide/actions/documenting-actions.html": "/master/playbook-reference/actions/develop-actions/documenting-actions.html", + "developer-guide/platform/index.html": "/master/contributing.html", + "developer-guide/platform/dev-setup.html": "/master/contributing.html", + "developer-guide/platform/coding-conventions.html": "/master/contributing.html", + "developer-guide/platform/docs-contributions.html": "/master/docs-contributions.html#improving-these-docs", + "developer-guide/platform/sinks-guide.html": "/master/configuration/sinks/sinks-development.html" +} # for sphinx_jinja - see https://github.com/tardyp/sphinx-jinja From d3d9ae584153b7841b25cd9b5ebff965a2610dc8 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 29 Apr 2025 15:54:04 +0200 Subject: [PATCH 02/10] Added some more redirections --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 907de6fc0..8a6a573e6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -169,7 +169,8 @@ "developer-guide/platform/dev-setup.html": "/master/contributing.html", "developer-guide/platform/coding-conventions.html": "/master/contributing.html", "developer-guide/platform/docs-contributions.html": "/master/docs-contributions.html#improving-these-docs", - "developer-guide/platform/sinks-guide.html": "/master/configuration/sinks/sinks-development.html" + "developer-guide/platform/sinks-guide.html": "/master/configuration/sinks/sinks-development.html", + "user-guide/robusta-cli.html": "master/setup-robusta/installation/index.html" } From fcbe55bb4663ad3b6b3a081c3773331b9bef1c75 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 29 Apr 2025 16:06:54 +0200 Subject: [PATCH 03/10] Fixed robusta-cli old docs page redirection --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 8a6a573e6..690ac57ab 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -170,7 +170,7 @@ "developer-guide/platform/coding-conventions.html": "/master/contributing.html", "developer-guide/platform/docs-contributions.html": "/master/docs-contributions.html#improving-these-docs", "developer-guide/platform/sinks-guide.html": "/master/configuration/sinks/sinks-development.html", - "user-guide/robusta-cli.html": "master/setup-robusta/installation/index.html" + "user-guide/robusta-cli.html": "/master/setup-robusta/installation/index.html" } From f9a960111265ecdabe5a6fd731ad7c9ba5d1a022 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 29 Apr 2025 16:20:29 +0200 Subject: [PATCH 04/10] Added redirect for advanced/index.html --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 690ac57ab..948ce3e83 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -170,7 +170,8 @@ "developer-guide/platform/coding-conventions.html": "/master/contributing.html", "developer-guide/platform/docs-contributions.html": "/master/docs-contributions.html#improving-these-docs", "developer-guide/platform/sinks-guide.html": "/master/configuration/sinks/sinks-development.html", - "user-guide/robusta-cli.html": "/master/setup-robusta/installation/index.html" + "user-guide/robusta-cli.html": "/master/setup-robusta/installation/index.html", + "advanced/index.html": "/master/setup-robusta/installation/index.html" } From b7b845c6411f8471d26d0018d3f1202a0d0ff32f Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Mon, 5 May 2025 09:26:55 +0200 Subject: [PATCH 05/10] Passing thread_ts to slack sink --- helm/robusta/values.yaml | 4 ++-- src/robusta/core/model/base_params.py | 5 +++++ .../core/playbooks/internal/ai_integration.py | 12 +++++++++++- src/robusta/core/reporting/base.py | 2 ++ src/robusta/core/reporting/callbacks.py | 3 ++- src/robusta/integrations/receiver.py | 15 +++++++++++++-- src/robusta/integrations/slack/sender.py | 15 ++++++++++++--- 7 files changed, 47 insertions(+), 9 deletions(-) diff --git a/helm/robusta/values.yaml b/helm/robusta/values.yaml index c9f7c6076..e70ecc25a 100644 --- a/helm/robusta/values.yaml +++ b/helm/robusta/values.yaml @@ -23,7 +23,7 @@ automountServiceAccountToken: true enableHolmesGPT: false -# see https://docs.robusta.dev/master/user-guide/configuration.html#global-config and https://docs.robusta.dev/master/configuration/additional-settings.html#global-config +# see https://docs.robusta.dev/master/setup-robusta/additional-settings.html#global-config globalConfig: check_prometheus_flags: true grafana_url: "" @@ -35,7 +35,7 @@ globalConfig: custom_annotations: [] custom_severity_map: {} -# see https://docs.robusta.dev/master/user-guide/configuration/additional-settings.html#relabel-prometheus-alerts +# see https://docs.robusta.dev/master/setup-robusta/additional-settings.html#relabel-prometheus-alerts alertRelabel: [] # safe actions to enable authenticated users to run diff --git a/src/robusta/core/model/base_params.py b/src/robusta/core/model/base_params.py index 93d19ff40..a7e79534b 100644 --- a/src/robusta/core/model/base_params.py +++ b/src/robusta/core/model/base_params.py @@ -84,6 +84,10 @@ class HolmesParams(ActionParams): holmes_url: Optional[str] model: Optional[str] + # Additional internal context that helps runner to send investigation to appropriate sinks + # for now it is used only for passing thread_ts to slack sink internally; + robusta_context: Optional[Dict[str, Any]] = None + @validator("holmes_url", allow_reuse=True) def validate_protocol(cls, v): if v and not v.startswith("http"): # if the user configured url without http(s) @@ -114,6 +118,7 @@ class AIInvestigateParams(HolmesParams): stream: bool = False + class HolmesToolsResult(BaseModel): """ :var name: Name of the tool. diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index 2038e6ccd..0e417c63f 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -44,6 +44,7 @@ def build_investigation_title(params: AIInvestigateParams) -> str: @action def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): + logging.info(f"DIMA Received request to ask holmes with params: {params}") holmes_url = HolmesDiscovery.find_holmes_url(params.holmes_url) if not holmes_url: raise ActionException(ErrorCodes.HOLMES_DISCOVERY_FAILED, "Robusta couldn't connect to the Holmes client.") @@ -101,7 +102,16 @@ def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): finding.add_enrichment( [HolmesResultsBlock(holmes_result=holmes_result)], enrichment_type=EnrichmentType.ai_analysis ) - + runner_context = getattr(params, "robusta_context", None) # Safely get the context dict + if runner_context and "thread_ts" in runner_context: + original_thread_ts = runner_context.get("thread_ts") + if original_thread_ts: + finding.robusta_context["thread_ts"] = original_thread_ts + logging.info(f"Added message_ts={original_thread_ts} to finding {finding.id} annotations.") + else: + logging.warning(f"message_ts found in robusta_context for finding {finding.id} but it is empty.") + else: + logging.debug(f"No message_ts found in robusta_context for finding {finding.id}. Context: {runner_context}") event.add_finding(finding) except Exception as e: diff --git a/src/robusta/core/reporting/base.py b/src/robusta/core/reporting/base.py index 9a53d0e43..b581ae8ba 100644 --- a/src/robusta/core/reporting/base.py +++ b/src/robusta/core/reporting/base.py @@ -9,6 +9,7 @@ from typing import Any, Dict, List, Optional, Union from urllib.parse import urlencode +from pydantic import Field from pydantic.main import BaseModel from strenum import StrEnum @@ -292,6 +293,7 @@ def __init__( self.starts_at = starts_at if starts_at else datetime.now() self.ends_at = ends_at self.dirty = False + self.robusta_context: Dict[str, Any] = {} @property def attribute_map(self) -> Dict[str, Union[str, Dict[str, str]]]: diff --git a/src/robusta/core/reporting/callbacks.py b/src/robusta/core/reporting/callbacks.py index 89f60500a..ae161c46f 100644 --- a/src/robusta/core/reporting/callbacks.py +++ b/src/robusta/core/reporting/callbacks.py @@ -5,7 +5,7 @@ from robusta.core.playbooks.actions_registry import Action from robusta.core.reporting import CallbackChoice from robusta.core.reporting.action_requests import ActionRequestBody, ExternalActionRequest, sign_action_request - +import logging class ExternalActionRequestBuilder(BaseModel): @classmethod @@ -43,6 +43,7 @@ def create_for_func( sinks=[sink], origin="callback", ) + logging.info(f"Created action request body: {body}") return ExternalActionRequest( body=body, signature=sign_action_request(body, signing_key), diff --git a/src/robusta/integrations/receiver.py b/src/robusta/integrations/receiver.py index acb0ee78d..5bdf5aa0a 100644 --- a/src/robusta/integrations/receiver.py +++ b/src/robusta/integrations/receiver.py @@ -49,10 +49,16 @@ class ValidationResponse(BaseModel): error_msg: Optional[str] = None +class SlackContainer(BaseModel): + channel_id: str + message_ts: str + + class SlackExternalActionRequest(ExternalActionRequest): # Optional Slack Params slack_username: Optional[str] = None slack_message: Optional[Any] = None + slack_container: Optional[SlackContainer] = None class SlackActionRequest(BaseModel): @@ -73,6 +79,7 @@ class SlackUserID(BaseModel): class SlackActionsMessage(BaseModel): actions: List[SlackActionRequest] user: Optional[SlackUserID] + container: Optional[SlackContainer] class ActionRequestReceiver: @@ -168,15 +175,18 @@ def __exec_external_request(self, action_request: ExternalActionRequest, validat if hasattr(action_request, 'slack_message'): action_request.body.action_params["slack_message"] = action_request.slack_message + + if action_request.slack_container and action_request.slack_container.message_ts: + thread_ts = action_request.slack_container.message_ts + action_request.body.action_params["robusta_context"] = {"thread_ts": thread_ts} response = self.event_handler.run_external_action( action_request.body.action_name, action_request.body.action_params, - action_request.body.sinks, + action_request.body.sinks, sync_response, action_request.no_sinks, ) - if sync_response: http_code = 200 if response.get("success") else 500 self.ws.send(data=json.dumps(self.__sync_response(http_code, action_request.request_id, response))) @@ -238,6 +248,7 @@ def _parse_slack_message(message: Union[str, bytes, bytearray]) -> SlackActionsM for action in slack_actions_message.actions: action.value.slack_username = slack_actions_message.user.username action.value.slack_message = json_slack_message + action.value.slack_container = slack_actions_message.container return slack_actions_message def on_message(self, ws: websocket.WebSocketApp, message: str) -> None: diff --git a/src/robusta/integrations/slack/sender.py b/src/robusta/integrations/slack/sender.py index da4737e66..3091b753c 100644 --- a/src/robusta/integrations/slack/sender.py +++ b/src/robusta/integrations/slack/sender.py @@ -107,7 +107,8 @@ def __get_action_block_for_choices(self, sink: str, choices: Dict[str, CallbackC ).json(), } ) - + logging.info(f"Created action block: {buttons}") + return [{"type": "actions", "elements": buttons}] def __to_slack_links(self, links: List[LinkProp]) -> List[SlackBlock]: @@ -524,10 +525,18 @@ def send_finding_to_slack( finding.subject.labels, finding.subject.annotations, ) - + robusta_context = getattr(finding, "robusta_context", None) + effective_thread_ts = thread_ts # Default to the one passed in (e.g., from grouping) + if robusta_context: + annotation_ts = robusta_context.get("thread_ts") + if annotation_ts: # Make sure it's not None or empty + effective_thread_ts = annotation_ts # Prioritize the annotation! + logging.info(f"Using thread_ts from annotation for AI analysis finding {finding.id}: {effective_thread_ts}") + else: + logging.warning(f"Found empty message_ts annotation for AI analysis finding {finding.id}, using original thread_ts: {thread_ts}") if finding.finding_type == FindingType.AI_ANALYSIS: # holmes analysis message needs special handling - self.send_holmes_analysis(finding, slack_channel, platform_enabled, thread_ts) + self.send_holmes_analysis(finding, slack_channel, platform_enabled, effective_thread_ts) return "" # [arik] Looks like the return value here is not used, needs to be removed status: FindingStatus = ( From 506209f5ca78b9982a7adb4eb429b9f2f74ada12 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Mon, 12 May 2025 13:59:35 +0200 Subject: [PATCH 06/10] Removed redundant log line from ask_holmes --- src/robusta/core/playbooks/internal/ai_integration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index 66ba1386c..3efb8dedb 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -49,7 +49,6 @@ def build_investigation_title(params: AIInvestigateParams) -> str: @action def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): - logging.info(f"DIMA Received request to ask holmes with params: {params}") holmes_url = HolmesDiscovery.find_holmes_url(params.holmes_url) if not holmes_url: raise ActionException(ErrorCodes.HOLMES_DISCOVERY_FAILED, "Robusta couldn't connect to the Holmes client.") From c0200f0247cfe3c86144df70a270742ed0cc2f88 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Mon, 12 May 2025 17:33:59 +0200 Subject: [PATCH 07/10] Refactored the way variables pass to ask_ai to get to slack channels --- src/robusta/core/playbooks/internal/ai_integration.py | 2 ++ src/robusta/integrations/receiver.py | 3 ++- src/robusta/integrations/slack/sender.py | 8 ++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index 3efb8dedb..c0e24aefc 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -115,8 +115,10 @@ def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): runner_context = getattr(params, "robusta_context", None) # Safely get the context dict if runner_context and "thread_ts" in runner_context: original_thread_ts = runner_context.get("thread_ts") + original_channel_id = runner_context.get("channel_id") if original_thread_ts: finding.robusta_context["thread_ts"] = original_thread_ts + finding.robusta_context["channel_id"] = original_channel_id logging.info(f"Added message_ts={original_thread_ts} to finding {finding.id} annotations.") else: logging.warning(f"message_ts found in robusta_context for finding {finding.id} but it is empty.") diff --git a/src/robusta/integrations/receiver.py b/src/robusta/integrations/receiver.py index 5bdf5aa0a..eb83880db 100644 --- a/src/robusta/integrations/receiver.py +++ b/src/robusta/integrations/receiver.py @@ -178,7 +178,8 @@ def __exec_external_request(self, action_request: ExternalActionRequest, validat if action_request.slack_container and action_request.slack_container.message_ts: thread_ts = action_request.slack_container.message_ts - action_request.body.action_params["robusta_context"] = {"thread_ts": thread_ts} + channel_id = action_request.slack_container.channel_id + action_request.body.action_params["robusta_context"] = {"thread_ts": thread_ts, "channel_id": channel_id} response = self.event_handler.run_external_action( action_request.body.action_name, diff --git a/src/robusta/integrations/slack/sender.py b/src/robusta/integrations/slack/sender.py index 6069c333d..293bed781 100644 --- a/src/robusta/integrations/slack/sender.py +++ b/src/robusta/integrations/slack/sender.py @@ -589,16 +589,16 @@ def send_finding_to_slack( ) robusta_context = getattr(finding, "robusta_context", None) effective_thread_ts = thread_ts # Default to the one passed in (e.g., from grouping) + effective_channel_id = slack_channel if robusta_context: annotation_ts = robusta_context.get("thread_ts") + channel_id = robusta_context.get("channel_id") if annotation_ts: # Make sure it's not None or empty effective_thread_ts = annotation_ts # Prioritize the annotation! - logging.info(f"Using thread_ts from annotation for AI analysis finding {finding.id}: {effective_thread_ts}") - else: - logging.warning(f"Found empty message_ts annotation for AI analysis finding {finding.id}, using original thread_ts: {thread_ts}") + effective_channel_id = channel_id if finding.finding_type == FindingType.AI_ANALYSIS: # holmes analysis message needs special handling - self.send_holmes_analysis(finding, slack_channel, platform_enabled, effective_thread_ts) + self.send_holmes_analysis(finding, effective_channel_id, platform_enabled, effective_thread_ts) return "" # [arik] Looks like the return value here is not used, needs to be removed status: FindingStatus = ( From 38c8ce44ea31154f57a6a3189f2948dcf71edac7 Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 13 May 2025 08:10:02 +0200 Subject: [PATCH 08/10] Refactored getting channel and thread for slack --- .../core/playbooks/internal/ai_integration.py | 8 +--- src/robusta/core/reporting/callbacks.py | 2 +- src/robusta/integrations/slack/sender.py | 47 ++++++++++++------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index c0e24aefc..e049f2cb0 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -112,18 +112,14 @@ def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): finding.add_enrichment( [HolmesResultsBlock(holmes_result=holmes_result)], enrichment_type=EnrichmentType.ai_analysis ) - runner_context = getattr(params, "robusta_context", None) # Safely get the context dict + runner_context = getattr(params, "robusta_context", None) if runner_context and "thread_ts" in runner_context: original_thread_ts = runner_context.get("thread_ts") original_channel_id = runner_context.get("channel_id") if original_thread_ts: finding.robusta_context["thread_ts"] = original_thread_ts finding.robusta_context["channel_id"] = original_channel_id - logging.info(f"Added message_ts={original_thread_ts} to finding {finding.id} annotations.") - else: - logging.warning(f"message_ts found in robusta_context for finding {finding.id} but it is empty.") - else: - logging.debug(f"No message_ts found in robusta_context for finding {finding.id}. Context: {runner_context}") + event.add_finding(finding) except Exception as e: diff --git a/src/robusta/core/reporting/callbacks.py b/src/robusta/core/reporting/callbacks.py index ae161c46f..4010e798e 100644 --- a/src/robusta/core/reporting/callbacks.py +++ b/src/robusta/core/reporting/callbacks.py @@ -43,7 +43,7 @@ def create_for_func( sinks=[sink], origin="callback", ) - logging.info(f"Created action request body: {body}") + return ExternalActionRequest( body=body, signature=sign_action_request(body, signing_key), diff --git a/src/robusta/integrations/slack/sender.py b/src/robusta/integrations/slack/sender.py index 293bed781..0867031a0 100644 --- a/src/robusta/integrations/slack/sender.py +++ b/src/robusta/integrations/slack/sender.py @@ -115,7 +115,6 @@ def __get_action_block_for_choices(self, sink: str, choices: Dict[str, CallbackC ).json(), } ) - logging.info(f"Created action block: {buttons}") return [{"type": "actions", "elements": buttons}] @@ -569,36 +568,48 @@ def send_holmes_analysis( except Exception: logging.exception(f"error sending message to slack. {title}") + + def _resolve_slack_thread( + self, + finding: Finding, + sink_params: SlackSinkParams, + thread_ts: Optional[str] = None, + ) -> tuple[str, Optional[str]]: + + channel = ChannelTransformer.template( + sink_params.channel_override, + sink_params.slack_channel, + self.cluster_name, + finding.subject.labels, + finding.subject.annotations, + ) + + ctx = getattr(finding, "robusta_context", {}) or {} + thread_override = ctx.get("thread_ts") + channel_override = ctx.get("channel_id") + + return ( + channel_override or channel, + thread_override or thread_ts, + ) def send_finding_to_slack( self, finding: Finding, sink_params: SlackSinkParams, platform_enabled: bool, - thread_ts: str = None, + thread_ts: Optional[str] = None, ) -> str: blocks: List[BaseBlock] = [] attachment_blocks: List[BaseBlock] = [] - slack_channel = ChannelTransformer.template( - sink_params.channel_override, - sink_params.slack_channel, - self.cluster_name, - finding.subject.labels, - finding.subject.annotations, + channel_id, thread_ts = self._resolve_slack_thread( + finding, sink_params, thread_ts ) - robusta_context = getattr(finding, "robusta_context", None) - effective_thread_ts = thread_ts # Default to the one passed in (e.g., from grouping) - effective_channel_id = slack_channel - if robusta_context: - annotation_ts = robusta_context.get("thread_ts") - channel_id = robusta_context.get("channel_id") - if annotation_ts: # Make sure it's not None or empty - effective_thread_ts = annotation_ts # Prioritize the annotation! - effective_channel_id = channel_id + if finding.finding_type == FindingType.AI_ANALYSIS: # holmes analysis message needs special handling - self.send_holmes_analysis(finding, effective_channel_id, platform_enabled, effective_thread_ts) + self.send_holmes_analysis(finding, channel_id, platform_enabled, thread_ts) return "" # [arik] Looks like the return value here is not used, needs to be removed status: FindingStatus = ( From 609d8f2e539d6e37c8e4fce52c16eefea5abe56a Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Tue, 13 May 2025 08:24:04 +0200 Subject: [PATCH 09/10] Fixed bug in send_finding_slack --- src/robusta/core/playbooks/internal/ai_integration.py | 11 +++++------ src/robusta/core/reporting/base.py | 1 - src/robusta/core/reporting/callbacks.py | 2 +- src/robusta/integrations/slack/sender.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index e049f2cb0..681771139 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -113,12 +113,11 @@ def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams): [HolmesResultsBlock(holmes_result=holmes_result)], enrichment_type=EnrichmentType.ai_analysis ) runner_context = getattr(params, "robusta_context", None) - if runner_context and "thread_ts" in runner_context: - original_thread_ts = runner_context.get("thread_ts") - original_channel_id = runner_context.get("channel_id") - if original_thread_ts: - finding.robusta_context["thread_ts"] = original_thread_ts - finding.robusta_context["channel_id"] = original_channel_id + if runner_context: + if "thread_ts" in runner_context: + finding.robusta_context["thread_ts"] = runner_context.get("thread_ts") + if "channel_id" in runner_context: + finding.robusta_context["channel_id"] = runner_context.get("channel_id") event.add_finding(finding) diff --git a/src/robusta/core/reporting/base.py b/src/robusta/core/reporting/base.py index b581ae8ba..12fa397cf 100644 --- a/src/robusta/core/reporting/base.py +++ b/src/robusta/core/reporting/base.py @@ -9,7 +9,6 @@ from typing import Any, Dict, List, Optional, Union from urllib.parse import urlencode -from pydantic import Field from pydantic.main import BaseModel from strenum import StrEnum diff --git a/src/robusta/core/reporting/callbacks.py b/src/robusta/core/reporting/callbacks.py index 4010e798e..4c59b8de2 100644 --- a/src/robusta/core/reporting/callbacks.py +++ b/src/robusta/core/reporting/callbacks.py @@ -5,7 +5,7 @@ from robusta.core.playbooks.actions_registry import Action from robusta.core.reporting import CallbackChoice from robusta.core.reporting.action_requests import ActionRequestBody, ExternalActionRequest, sign_action_request -import logging + class ExternalActionRequestBuilder(BaseModel): @classmethod diff --git a/src/robusta/integrations/slack/sender.py b/src/robusta/integrations/slack/sender.py index 0867031a0..535cb84a4 100644 --- a/src/robusta/integrations/slack/sender.py +++ b/src/robusta/integrations/slack/sender.py @@ -603,13 +603,13 @@ def send_finding_to_slack( blocks: List[BaseBlock] = [] attachment_blocks: List[BaseBlock] = [] - channel_id, thread_ts = self._resolve_slack_thread( + slack_channel, thread_ts = self._resolve_slack_thread( finding, sink_params, thread_ts ) if finding.finding_type == FindingType.AI_ANALYSIS: # holmes analysis message needs special handling - self.send_holmes_analysis(finding, channel_id, platform_enabled, thread_ts) + self.send_holmes_analysis(finding, slack_channel, platform_enabled, thread_ts) return "" # [arik] Looks like the return value here is not used, needs to be removed status: FindingStatus = ( From c1042a8e72c3a4ad615e1e628913a4ea68ffa34d Mon Sep 17 00:00:00 2001 From: Dima Chievtaiev Date: Wed, 14 May 2025 18:29:47 +0200 Subject: [PATCH 10/10] Fixed slack_container variable in receiver --- src/robusta/integrations/receiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/robusta/integrations/receiver.py b/src/robusta/integrations/receiver.py index eb83880db..168b398f7 100644 --- a/src/robusta/integrations/receiver.py +++ b/src/robusta/integrations/receiver.py @@ -176,7 +176,7 @@ def __exec_external_request(self, action_request: ExternalActionRequest, validat if hasattr(action_request, 'slack_message'): action_request.body.action_params["slack_message"] = action_request.slack_message - if action_request.slack_container and action_request.slack_container.message_ts: + if hasattr(action_request, 'slack_container'): thread_ts = action_request.slack_container.message_ts channel_id = action_request.slack_container.channel_id action_request.body.action_params["robusta_context"] = {"thread_ts": thread_ts, "channel_id": channel_id}