-
Notifications
You must be signed in to change notification settings - Fork 323
Add dmesg enricher actions for node and pod events #2151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
ANONYMOUSZED-beep
wants to merge
1
commit into
robusta-dev:master
from
ANONYMOUSZED-beep:feature/dmesg-enricher
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import logging | ||
| from typing import List, Optional | ||
|
|
||
| from pydantic import BaseModel | ||
| from robusta.api import BaseBlock, FileBlock, MarkdownBlock, NodeEvent, PodEvent, RobustaPod, action | ||
|
|
||
|
|
||
| class DmesgParams(BaseModel): | ||
| """ | ||
| :var lines: Number of lines to keep from the end of the dmesg output. If not set, the full output is kept. | ||
|
|
||
| :example lines: 100 | ||
| """ | ||
|
|
||
| lines: Optional[int] = None | ||
|
|
||
|
|
||
| def _build_dmesg_command(params: DmesgParams) -> str: | ||
| command = "dmesg" | ||
| if params.lines is not None: | ||
| command = f"{command} | tail -n {params.lines}" | ||
| return command | ||
|
|
||
|
|
||
| def _dmesg_enrichment_blocks(node_name: str, exec_result: str) -> List[BaseBlock]: | ||
| block_list: List[BaseBlock] = [] | ||
| block_list.append(MarkdownBlock(f"Dmesg results for node *{node_name}:*")) | ||
| block_list.append(FileBlock(f"dmesg-{node_name}.log", exec_result.encode())) | ||
| return block_list | ||
|
|
||
|
|
||
| @action | ||
| def node_dmesg_enricher(event: NodeEvent, params: DmesgParams): | ||
| """ | ||
| Fetch the kernel ring buffer (dmesg) from the target **node**. | ||
| Enrich the finding with the dmesg output, readable as a file. | ||
| """ | ||
| node = event.get_node() | ||
| if not node: | ||
| logging.error(f"cannot run NodeDmesgEnricher on event with no node: {event}") | ||
| return | ||
|
|
||
| exec_result = RobustaPod.exec_in_debugger_pod("node-dmesg-pod", node.metadata.name, _build_dmesg_command(params)) | ||
| event.add_enrichment(_dmesg_enrichment_blocks(node.metadata.name, exec_result)) | ||
|
|
||
|
|
||
| @action | ||
| def pod_dmesg_enricher(event: PodEvent, params: DmesgParams): | ||
| """ | ||
| Fetch the kernel ring buffer (dmesg) from the **node** that the target pod is running on. | ||
| Enrich the finding with the dmesg output, readable as a file. | ||
| """ | ||
| pod = event.get_pod() | ||
| if not pod: | ||
| logging.error(f"cannot run PodDmesgEnricher on event with no pod: {event}") | ||
| return | ||
|
|
||
| node_name = pod.spec.nodeName | ||
| if not node_name: | ||
| logging.error(f"cannot run PodDmesgEnricher on pod {pod.metadata.name} which is not scheduled on a node") | ||
| return | ||
|
|
||
| exec_result = RobustaPod.exec_in_debugger_pod("node-dmesg-pod", node_name, _build_dmesg_command(params)) | ||
| event.add_enrichment(_dmesg_enrichment_blocks(node_name, exec_result)) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| from unittest import mock | ||
|
|
||
| import pytest | ||
| from hikaru.model.rel_1_26 import Node, ObjectMeta, PodSpec | ||
| from robusta.core.reporting import FileBlock, MarkdownBlock | ||
| from robusta.integrations.kubernetes.autogenerated.events import NodeEvent, PodEvent | ||
| from robusta.integrations.kubernetes.custom_models import RobustaPod | ||
|
|
||
| from playbooks.robusta_playbooks.dmesg_enrichments import ( | ||
| DmesgParams, | ||
| _build_dmesg_command, | ||
| node_dmesg_enricher, | ||
| pod_dmesg_enricher, | ||
| ) | ||
|
|
||
| SINK = "test-sink" | ||
|
|
||
|
|
||
| def _get_blocks(event): | ||
| """Return the enrichment blocks added to the event's first finding.""" | ||
| finding = event.sink_findings[SINK][0] | ||
| assert len(finding.enrichments) == 1 | ||
| return finding.enrichments[0].blocks | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "params,expected_command", | ||
| [ | ||
| (DmesgParams(), "dmesg"), | ||
| (DmesgParams(lines=100), "dmesg | tail -n 100"), | ||
| (DmesgParams(lines=1), "dmesg | tail -n 1"), | ||
| ], | ||
| ) | ||
| def test_build_dmesg_command(params, expected_command): | ||
| assert _build_dmesg_command(params) == expected_command | ||
|
|
||
|
|
||
| def test_node_dmesg_enricher_adds_file_block(): | ||
| node = Node(metadata=ObjectMeta(name="test-node")) | ||
| event = NodeEvent(obj=node, named_sinks=[SINK]) | ||
|
|
||
| with mock.patch.object( | ||
| RobustaPod, "exec_in_debugger_pod", return_value="[ 0.000000] Linux version 5.15.0" | ||
| ) as mock_exec: | ||
| node_dmesg_enricher(event, DmesgParams(lines=50)) | ||
|
|
||
| mock_exec.assert_called_once_with("node-dmesg-pod", "test-node", "dmesg | tail -n 50") | ||
| blocks = _get_blocks(event) | ||
| assert len(blocks) == 2 | ||
| assert isinstance(blocks[0], MarkdownBlock) | ||
| assert "test-node" in blocks[0].text | ||
| assert isinstance(blocks[1], FileBlock) | ||
| assert blocks[1].filename == "dmesg-test-node.log" | ||
| assert blocks[1].contents == b"[ 0.000000] Linux version 5.15.0" | ||
|
|
||
|
|
||
| def test_node_dmesg_enricher_no_node(): | ||
| event = NodeEvent(obj=None, named_sinks=[SINK]) | ||
| with mock.patch.object(RobustaPod, "exec_in_debugger_pod") as mock_exec: | ||
| node_dmesg_enricher(event, DmesgParams()) | ||
| mock_exec.assert_not_called() | ||
|
|
||
|
|
||
| def test_pod_dmesg_enricher_uses_pod_node(): | ||
| pod = RobustaPod( | ||
| metadata=ObjectMeta(name="test-pod", namespace="default"), | ||
| spec=PodSpec(containers=[], nodeName="worker-1"), | ||
| ) | ||
| event = PodEvent(obj=pod, named_sinks=[SINK]) | ||
|
|
||
| with mock.patch.object(RobustaPod, "exec_in_debugger_pod", return_value="dmesg output") as mock_exec: | ||
| pod_dmesg_enricher(event, DmesgParams()) | ||
|
|
||
| mock_exec.assert_called_once_with("node-dmesg-pod", "worker-1", "dmesg") | ||
| blocks = _get_blocks(event) | ||
| assert len(blocks) == 2 | ||
| assert isinstance(blocks[1], FileBlock) | ||
| assert blocks[1].filename == "dmesg-worker-1.log" | ||
| assert blocks[1].contents == b"dmesg output" | ||
|
|
||
|
|
||
| def test_pod_dmesg_enricher_no_pod(): | ||
| event = PodEvent(obj=None, named_sinks=[SINK]) | ||
| with mock.patch.object(RobustaPod, "exec_in_debugger_pod") as mock_exec: | ||
| pod_dmesg_enricher(event, DmesgParams()) | ||
| mock_exec.assert_not_called() | ||
|
|
||
|
|
||
| def test_pod_dmesg_enricher_unscheduled_pod(): | ||
| pod = RobustaPod( | ||
| metadata=ObjectMeta(name="pending-pod", namespace="default"), | ||
| spec=PodSpec(containers=[]), | ||
| ) | ||
| event = PodEvent(obj=pod, named_sinks=[SINK]) | ||
| with mock.patch.object(RobustaPod, "exec_in_debugger_pod") as mock_exec: | ||
| pod_dmesg_enricher(event, DmesgParams()) | ||
| mock_exec.assert_not_called() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: robusta-dev/robusta
Length of output: 6207
🏁 Script executed:
Repository: robusta-dev/robusta
Length of output: 15813
🏁 Script executed:
Repository: robusta-dev/robusta
Length of output: 29357
Reject negative
linesvalues.tail -n -1means “all but the last line,” not a negative number of lines to keep. This conflicts withDmesgParamsdocumentation. AddField(default=None, ge=0)and testlines=0andlines=-1.🤖 Prompt for AI Agents