Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions helm/robusta/values.yaml
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add tests

Original file line number Diff line number Diff line change
Expand Up @@ -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: ""
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/robusta/core/model/base_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class ResourceInfo(BaseModel):
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking here loud, we already have SlackCallbackParams which being used in other flows. I think it will be good idea to add relevant info there and reuse in relevant flows.
WDYT?


@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)
Expand Down Expand Up @@ -113,6 +117,7 @@ class AIInvestigateParams(HolmesParams):
stream: bool = False



class HolmesToolsResult(BaseModel):
"""
:var name: Name of the tool.
Expand Down
8 changes: 7 additions & 1 deletion src/robusta/core/playbooks/internal/ai_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,13 @@ 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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we are ignoring the sink param in this flow. It might cause issues with sending the finding to other sinks as well. I think we need to support the sink param in this flow as well.

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)

except Exception as e:
Expand Down
1 change: 1 addition & 0 deletions src/robusta/core/reporting/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,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]]]:
Expand Down
1 change: 1 addition & 0 deletions src/robusta/core/reporting/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def create_for_func(
sinks=[sink],
origin="callback",
)

return ExternalActionRequest(
body=body,
signature=sign_action_request(body, signing_key),
Expand Down
16 changes: 14 additions & 2 deletions src/robusta/integrations/receiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -73,6 +79,7 @@ class SlackUserID(BaseModel):
class SlackActionsMessage(BaseModel):
actions: List[SlackActionRequest]
user: Optional[SlackUserID]
container: Optional[SlackContainer]


class ActionRequestReceiver:
Expand Down Expand Up @@ -168,15 +175,19 @@ 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 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}

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)))
Expand Down Expand Up @@ -238,6 +249,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:
Expand Down
38 changes: 29 additions & 9 deletions src/robusta/integrations/slack/sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def __get_action_block_for_choices(self, sink: str, choices: Dict[str, CallbackC
).json(),
}
)

return [{"type": "actions", "elements": buttons}]

def __to_slack_links(self, links: List[LinkProp]) -> List[SlackBlock]:
Expand Down Expand Up @@ -568,25 +568,45 @@ 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(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A. Any reason to call this if channel override was provided?
B. Not sure i fully understand how this will work if ChannelTransformer.template will work correctly if labels & annotations aren't pass in that flow.

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,
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, slack_channel, platform_enabled, thread_ts)
Expand Down