diff --git a/helm/robusta/values.yaml b/helm/robusta/values.yaml index 4a0b2cda3..5defa1b0a 100644 --- a/helm/robusta/values.yaml +++ b/helm/robusta/values.yaml @@ -97,6 +97,8 @@ lightActions: - holmes_conversation - holmes_issue_chat - holmes_chat +- holmes_validate_toolset +- holmes_refresh_toolsets - list_pods - kubectl_describe - fetch_resource_yaml diff --git a/src/robusta/core/model/base_params.py b/src/robusta/core/model/base_params.py index 63f2eda75..1ae1cc8b3 100644 --- a/src/robusta/core/model/base_params.py +++ b/src/robusta/core/model/base_params.py @@ -232,6 +232,20 @@ class HolmesConversationParams(HolmesParams): include_tool_call_results: bool = True +class HolmesValidateToolsetParams(HolmesParams): + """ + :var yaml_config: Raw YAML string containing the toolset configuration, including the 'holmes:' wrapper. + """ + + yaml_config: str + + +class HolmesRefreshToolsetsParams(HolmesParams): + """Params for triggering a toolset status refresh via Holmes.""" + + pass + + class NamespacedResourcesParams(ActionParams): """ :var name: Resource name diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index 1c6e5d519..383cd61a5 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -12,6 +12,8 @@ HolmesChatParams, HolmesConversationParams, HolmesIssueChatParams, + HolmesRefreshToolsetsParams, + HolmesValidateToolsetParams, ResourceInfo, ) from robusta.core.model.events import ExecutionBaseEvent @@ -33,6 +35,8 @@ HolmesRequest, HolmesResult, HolmesResultsBlock, + HolmesValidateToolsetRequest, + HolmesValidateToolsetResponse, ) from robusta.core.reporting.utils import convert_svg_to_png from robusta.core.stream.utils import ( @@ -385,6 +389,48 @@ def holmes_chat(event: ExecutionBaseEvent, params: HolmesChatParams): handle_holmes_error(e) +@action +def holmes_validate_toolset(event: ExecutionBaseEvent, params: HolmesValidateToolsetParams): + 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.", + ) + + try: + holmes_req = HolmesValidateToolsetRequest(yaml_config=params.yaml_config) + url = f"{holmes_url}/api/toolsets/validate" + result = requests.post(url, data=holmes_req.json()) + result.raise_for_status() + holmes_response = HolmesValidateToolsetResponse(**json.loads(result.text)) + event.response = {"success": True, **holmes_response.dict()} + + except Exception as e: + logging.exception("Failed to validate toolset via Holmes", exc_info=True) + handle_holmes_error(e) + + +@action +def holmes_refresh_toolsets(event: ExecutionBaseEvent, params: HolmesRefreshToolsetsParams): + 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.", + ) + + try: + url = f"{holmes_url}/api/toolsets/refresh" + result = requests.post(url) + result.raise_for_status() + event.response = {"success": True} + + except Exception as e: + logging.exception("Failed to refresh toolsets via Holmes", exc_info=True) + handle_holmes_error(e) + + def stream_and_render_graphs(url, holmes_req, event): with requests.post( url, diff --git a/src/robusta/core/reporting/holmes.py b/src/robusta/core/reporting/holmes.py index 385fa89a5..2bdd71c42 100644 --- a/src/robusta/core/reporting/holmes.py +++ b/src/robusta/core/reporting/holmes.py @@ -93,3 +93,20 @@ class HolmesChatResult(BaseModel): class HolmesChatResultsBlock(BaseBlock): holmes_result: Optional[HolmesChatResult] + + +class HolmesValidateToolsetRequest(BaseModel): + yaml_config: str = Field( + description="Raw YAML string containing the toolset configuration, including the 'holmes:' wrapper." + ) + + +class HolmesValidateToolsetResult(BaseModel): + toolset_name: str + status: str = Field(description="Toolset status: 'valid' or 'invalid'") + error: Optional[str] = None + description: Optional[str] = None + + +class HolmesValidateToolsetResponse(BaseModel): + results: List[HolmesValidateToolsetResult] diff --git a/src/robusta/runner/config_loader.py b/src/robusta/runner/config_loader.py index cbf72b8c1..db5e2fb53 100644 --- a/src/robusta/runner/config_loader.py +++ b/src/robusta/runner/config_loader.py @@ -281,7 +281,12 @@ def __reload_playbook_packages(self, change_name): ) # Kill the whole process group (which means this process and all of its descendant # processes). The rest of the runner shutdown happens in robusta.runner.process_setup. - os.killpg(os.getpgid(0), signal.SIGTERM) + # os.killpg(os.getpgid(0), signal.SIGTERM) + if hasattr(os, "killpg"): + os.killpg(os.getpgid(0), signal.SIGTERM) + else: + os.kill(os.getpid(), signal.SIGTERM) + @classmethod def __prepare_runtime_config(