From 4122b1447175caf3247bdf3508deb8615e612d35 Mon Sep 17 00:00:00 2001 From: alonelish Date: Tue, 24 Mar 2026 11:26:13 +0200 Subject: [PATCH 1/4] Add holmes_validate_toolset action for toolset validation API Adds a new action that calls the HolmesGPT POST /api/toolsets/validate endpoint, allowing validation of toolset YAML configurations. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/robusta/core/model/base_params.py | 8 ++++++ .../core/playbooks/internal/ai_integration.py | 25 +++++++++++++++++++ src/robusta/core/reporting/holmes.py | 17 +++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/robusta/core/model/base_params.py b/src/robusta/core/model/base_params.py index 63f2eda75..34984bcca 100644 --- a/src/robusta/core/model/base_params.py +++ b/src/robusta/core/model/base_params.py @@ -232,6 +232,14 @@ 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 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..bd72507c0 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -12,6 +12,7 @@ HolmesChatParams, HolmesConversationParams, HolmesIssueChatParams, + HolmesValidateToolsetParams, ResourceInfo, ) from robusta.core.model.events import ExecutionBaseEvent @@ -33,6 +34,8 @@ HolmesRequest, HolmesResult, HolmesResultsBlock, + HolmesValidateToolsetRequest, + HolmesValidateToolsetResponse, ) from robusta.core.reporting.utils import convert_svg_to_png from robusta.core.stream.utils import ( @@ -385,6 +388,28 @@ 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.ws(data=json.dumps(holmes_response.dict())) + + except Exception as e: + logging.exception("Failed to validate toolset 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..66650fc2b 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: 'enabled' or 'failed'") + error: Optional[str] = None + description: Optional[str] = None + + +class HolmesValidateToolsetResponse(BaseModel): + results: List[HolmesValidateToolsetResult] From e4ec0315f8c47c0a943a58ebbc99b027421e03aa Mon Sep 17 00:00:00 2001 From: alonelish Date: Tue, 24 Mar 2026 15:17:42 +0200 Subject: [PATCH 2/4] Fix validate_toolset: use event.response, add light action, update status field - Fix AttributeError by using event.response instead of event.ws() - Add holmes_validate_toolset to lightActions in Helm values - Update status field description to 'valid'/'invalid' Co-Authored-By: Claude Opus 4.6 (1M context) --- helm/robusta/values.yaml | 1 + src/robusta/core/playbooks/internal/ai_integration.py | 2 +- src/robusta/core/reporting/holmes.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/helm/robusta/values.yaml b/helm/robusta/values.yaml index 4a0b2cda3..86deb525f 100644 --- a/helm/robusta/values.yaml +++ b/helm/robusta/values.yaml @@ -97,6 +97,7 @@ lightActions: - holmes_conversation - holmes_issue_chat - holmes_chat +- holmes_validate_toolset - list_pods - kubectl_describe - fetch_resource_yaml diff --git a/src/robusta/core/playbooks/internal/ai_integration.py b/src/robusta/core/playbooks/internal/ai_integration.py index bd72507c0..34942647f 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -403,7 +403,7 @@ def holmes_validate_toolset(event: ExecutionBaseEvent, params: HolmesValidateToo result = requests.post(url, data=holmes_req.json()) result.raise_for_status() holmes_response = HolmesValidateToolsetResponse(**json.loads(result.text)) - event.ws(data=json.dumps(holmes_response.dict())) + event.response = {"success": True, **holmes_response.dict()} except Exception as e: logging.exception("Failed to validate toolset via Holmes", exc_info=True) diff --git a/src/robusta/core/reporting/holmes.py b/src/robusta/core/reporting/holmes.py index 66650fc2b..2bdd71c42 100644 --- a/src/robusta/core/reporting/holmes.py +++ b/src/robusta/core/reporting/holmes.py @@ -103,7 +103,7 @@ class HolmesValidateToolsetRequest(BaseModel): class HolmesValidateToolsetResult(BaseModel): toolset_name: str - status: str = Field(description="Toolset status: 'enabled' or 'failed'") + status: str = Field(description="Toolset status: 'valid' or 'invalid'") error: Optional[str] = None description: Optional[str] = None From d2c54126fbea406620f68f703feaf701ec36be86 Mon Sep 17 00:00:00 2001 From: alonelish Date: Wed, 25 Mar 2026 12:28:44 +0200 Subject: [PATCH 3/4] Add holmes_refresh_toolsets action for toolset refresh API Adds a new light action that calls HolmesGPT's POST /api/toolsets/refresh endpoint to trigger toolset status refresh and DB reporting. Co-Authored-By: Claude Opus 4.6 (1M context) --- helm/robusta/values.yaml | 1 + src/robusta/core/model/base_params.py | 6 ++++++ .../core/playbooks/internal/ai_integration.py | 21 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/helm/robusta/values.yaml b/helm/robusta/values.yaml index 86deb525f..5defa1b0a 100644 --- a/helm/robusta/values.yaml +++ b/helm/robusta/values.yaml @@ -98,6 +98,7 @@ lightActions: - 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 34984bcca..1ae1cc8b3 100644 --- a/src/robusta/core/model/base_params.py +++ b/src/robusta/core/model/base_params.py @@ -240,6 +240,12 @@ class HolmesValidateToolsetParams(HolmesParams): 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 34942647f..383cd61a5 100644 --- a/src/robusta/core/playbooks/internal/ai_integration.py +++ b/src/robusta/core/playbooks/internal/ai_integration.py @@ -12,6 +12,7 @@ HolmesChatParams, HolmesConversationParams, HolmesIssueChatParams, + HolmesRefreshToolsetsParams, HolmesValidateToolsetParams, ResourceInfo, ) @@ -410,6 +411,26 @@ def holmes_validate_toolset(event: ExecutionBaseEvent, params: HolmesValidateToo 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, From 3bf63c693994fb96a622a95dbdc7895ba3a83efb Mon Sep 17 00:00:00 2001 From: alonelish Date: Wed, 25 Mar 2026 15:41:13 +0200 Subject: [PATCH 4/4] Fix Windows compatibility: handle missing os.killpg os.killpg is not available on Windows, so fall back to os.kill with the current process ID. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/robusta/runner/config_loader.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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(