Skip to content

Commit b7b845c

Browse files
committed
Passing thread_ts to slack sink
1 parent f9a9601 commit b7b845c

7 files changed

Lines changed: 47 additions & 9 deletions

File tree

helm/robusta/values.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ automountServiceAccountToken: true
2323

2424
enableHolmesGPT: false
2525

26-
# see https://docs.robusta.dev/master/user-guide/configuration.html#global-config and https://docs.robusta.dev/master/configuration/additional-settings.html#global-config
26+
# see https://docs.robusta.dev/master/setup-robusta/additional-settings.html#global-config
2727
globalConfig:
2828
check_prometheus_flags: true
2929
grafana_url: ""
@@ -35,7 +35,7 @@ globalConfig:
3535
custom_annotations: []
3636
custom_severity_map: {}
3737

38-
# see https://docs.robusta.dev/master/user-guide/configuration/additional-settings.html#relabel-prometheus-alerts
38+
# see https://docs.robusta.dev/master/setup-robusta/additional-settings.html#relabel-prometheus-alerts
3939
alertRelabel: []
4040

4141
# safe actions to enable authenticated users to run

src/robusta/core/model/base_params.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ class HolmesParams(ActionParams):
8484

8585
holmes_url: Optional[str]
8686
model: Optional[str]
87+
# Additional internal context that helps runner to send investigation to appropriate sinks
88+
# for now it is used only for passing thread_ts to slack sink internally;
89+
robusta_context: Optional[Dict[str, Any]] = None
90+
8791
@validator("holmes_url", allow_reuse=True)
8892
def validate_protocol(cls, v):
8993
if v and not v.startswith("http"): # if the user configured url without http(s)
@@ -114,6 +118,7 @@ class AIInvestigateParams(HolmesParams):
114118
stream: bool = False
115119

116120

121+
117122
class HolmesToolsResult(BaseModel):
118123
"""
119124
:var name: Name of the tool.

src/robusta/core/playbooks/internal/ai_integration.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def build_investigation_title(params: AIInvestigateParams) -> str:
4444

4545
@action
4646
def ask_holmes(event: ExecutionBaseEvent, params: AIInvestigateParams):
47+
logging.info(f"DIMA Received request to ask holmes with params: {params}")
4748
holmes_url = HolmesDiscovery.find_holmes_url(params.holmes_url)
4849
if not holmes_url:
4950
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):
101102
finding.add_enrichment(
102103
[HolmesResultsBlock(holmes_result=holmes_result)], enrichment_type=EnrichmentType.ai_analysis
103104
)
104-
105+
runner_context = getattr(params, "robusta_context", None) # Safely get the context dict
106+
if runner_context and "thread_ts" in runner_context:
107+
original_thread_ts = runner_context.get("thread_ts")
108+
if original_thread_ts:
109+
finding.robusta_context["thread_ts"] = original_thread_ts
110+
logging.info(f"Added message_ts={original_thread_ts} to finding {finding.id} annotations.")
111+
else:
112+
logging.warning(f"message_ts found in robusta_context for finding {finding.id} but it is empty.")
113+
else:
114+
logging.debug(f"No message_ts found in robusta_context for finding {finding.id}. Context: {runner_context}")
105115
event.add_finding(finding)
106116

107117
except Exception as e:

src/robusta/core/reporting/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Any, Dict, List, Optional, Union
1010
from urllib.parse import urlencode
1111

12+
from pydantic import Field
1213
from pydantic.main import BaseModel
1314
from strenum import StrEnum
1415

@@ -292,6 +293,7 @@ def __init__(
292293
self.starts_at = starts_at if starts_at else datetime.now()
293294
self.ends_at = ends_at
294295
self.dirty = False
296+
self.robusta_context: Dict[str, Any] = {}
295297

296298
@property
297299
def attribute_map(self) -> Dict[str, Union[str, Dict[str, str]]]:

src/robusta/core/reporting/callbacks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from robusta.core.playbooks.actions_registry import Action
66
from robusta.core.reporting import CallbackChoice
77
from robusta.core.reporting.action_requests import ActionRequestBody, ExternalActionRequest, sign_action_request
8-
8+
import logging
99

1010
class ExternalActionRequestBuilder(BaseModel):
1111
@classmethod
@@ -43,6 +43,7 @@ def create_for_func(
4343
sinks=[sink],
4444
origin="callback",
4545
)
46+
logging.info(f"Created action request body: {body}")
4647
return ExternalActionRequest(
4748
body=body,
4849
signature=sign_action_request(body, signing_key),

src/robusta/integrations/receiver.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,16 @@ class ValidationResponse(BaseModel):
4949
error_msg: Optional[str] = None
5050

5151

52+
class SlackContainer(BaseModel):
53+
channel_id: str
54+
message_ts: str
55+
56+
5257
class SlackExternalActionRequest(ExternalActionRequest):
5358
# Optional Slack Params
5459
slack_username: Optional[str] = None
5560
slack_message: Optional[Any] = None
61+
slack_container: Optional[SlackContainer] = None
5662

5763

5864
class SlackActionRequest(BaseModel):
@@ -73,6 +79,7 @@ class SlackUserID(BaseModel):
7379
class SlackActionsMessage(BaseModel):
7480
actions: List[SlackActionRequest]
7581
user: Optional[SlackUserID]
82+
container: Optional[SlackContainer]
7683

7784

7885
class ActionRequestReceiver:
@@ -168,15 +175,18 @@ def __exec_external_request(self, action_request: ExternalActionRequest, validat
168175

169176
if hasattr(action_request, 'slack_message'):
170177
action_request.body.action_params["slack_message"] = action_request.slack_message
178+
179+
if action_request.slack_container and action_request.slack_container.message_ts:
180+
thread_ts = action_request.slack_container.message_ts
181+
action_request.body.action_params["robusta_context"] = {"thread_ts": thread_ts}
171182

172183
response = self.event_handler.run_external_action(
173184
action_request.body.action_name,
174185
action_request.body.action_params,
175-
action_request.body.sinks,
186+
action_request.body.sinks,
176187
sync_response,
177188
action_request.no_sinks,
178189
)
179-
180190
if sync_response:
181191
http_code = 200 if response.get("success") else 500
182192
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
238248
for action in slack_actions_message.actions:
239249
action.value.slack_username = slack_actions_message.user.username
240250
action.value.slack_message = json_slack_message
251+
action.value.slack_container = slack_actions_message.container
241252
return slack_actions_message
242253

243254
def on_message(self, ws: websocket.WebSocketApp, message: str) -> None:

src/robusta/integrations/slack/sender.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def __get_action_block_for_choices(self, sink: str, choices: Dict[str, CallbackC
107107
).json(),
108108
}
109109
)
110-
110+
logging.info(f"Created action block: {buttons}")
111+
111112
return [{"type": "actions", "elements": buttons}]
112113

113114
def __to_slack_links(self, links: List[LinkProp]) -> List[SlackBlock]:
@@ -524,10 +525,18 @@ def send_finding_to_slack(
524525
finding.subject.labels,
525526
finding.subject.annotations,
526527
)
527-
528+
robusta_context = getattr(finding, "robusta_context", None)
529+
effective_thread_ts = thread_ts # Default to the one passed in (e.g., from grouping)
530+
if robusta_context:
531+
annotation_ts = robusta_context.get("thread_ts")
532+
if annotation_ts: # Make sure it's not None or empty
533+
effective_thread_ts = annotation_ts # Prioritize the annotation!
534+
logging.info(f"Using thread_ts from annotation for AI analysis finding {finding.id}: {effective_thread_ts}")
535+
else:
536+
logging.warning(f"Found empty message_ts annotation for AI analysis finding {finding.id}, using original thread_ts: {thread_ts}")
528537
if finding.finding_type == FindingType.AI_ANALYSIS:
529538
# holmes analysis message needs special handling
530-
self.send_holmes_analysis(finding, slack_channel, platform_enabled, thread_ts)
539+
self.send_holmes_analysis(finding, slack_channel, platform_enabled, effective_thread_ts)
531540
return "" # [arik] Looks like the return value here is not used, needs to be removed
532541

533542
status: FindingStatus = (

0 commit comments

Comments
 (0)