holmesgpt 0.13.0__py3-none-any.whl → 0.13.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of holmesgpt might be problematic. Click here for more details.
- holmes/__init__.py +1 -1
- holmes/common/env_vars.py +4 -0
- holmes/core/llm.py +3 -1
- holmes/core/tool_calling_llm.py +112 -11
- holmes/core/toolset_manager.py +1 -5
- holmes/core/tracing.py +1 -1
- holmes/main.py +7 -1
- holmes/plugins/prompts/_fetch_logs.jinja2 +4 -0
- holmes/plugins/runbooks/CLAUDE.md +85 -0
- holmes/plugins/runbooks/README.md +24 -0
- holmes/plugins/toolsets/bash/argocd/__init__.py +65 -0
- holmes/plugins/toolsets/bash/argocd/constants.py +120 -0
- holmes/plugins/toolsets/bash/aws/__init__.py +66 -0
- holmes/plugins/toolsets/bash/aws/constants.py +529 -0
- holmes/plugins/toolsets/bash/azure/__init__.py +56 -0
- holmes/plugins/toolsets/bash/azure/constants.py +339 -0
- holmes/plugins/toolsets/bash/bash_instructions.jinja2 +6 -7
- holmes/plugins/toolsets/bash/bash_toolset.py +47 -13
- holmes/plugins/toolsets/bash/common/bash_command.py +131 -0
- holmes/plugins/toolsets/bash/common/stringify.py +14 -1
- holmes/plugins/toolsets/bash/common/validators.py +91 -0
- holmes/plugins/toolsets/bash/docker/__init__.py +59 -0
- holmes/plugins/toolsets/bash/docker/constants.py +255 -0
- holmes/plugins/toolsets/bash/helm/__init__.py +61 -0
- holmes/plugins/toolsets/bash/helm/constants.py +92 -0
- holmes/plugins/toolsets/bash/kubectl/__init__.py +80 -79
- holmes/plugins/toolsets/bash/kubectl/constants.py +0 -14
- holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +38 -56
- holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +28 -76
- holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +39 -99
- holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +34 -15
- holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +1 -1
- holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +38 -77
- holmes/plugins/toolsets/bash/parse_command.py +106 -32
- holmes/plugins/toolsets/bash/utilities/__init__.py +0 -0
- holmes/plugins/toolsets/bash/utilities/base64_util.py +12 -0
- holmes/plugins/toolsets/bash/utilities/cut.py +12 -0
- holmes/plugins/toolsets/bash/utilities/grep/__init__.py +10 -0
- holmes/plugins/toolsets/bash/utilities/head.py +12 -0
- holmes/plugins/toolsets/bash/utilities/jq.py +79 -0
- holmes/plugins/toolsets/bash/utilities/sed.py +164 -0
- holmes/plugins/toolsets/bash/utilities/sort.py +15 -0
- holmes/plugins/toolsets/bash/utilities/tail.py +12 -0
- holmes/plugins/toolsets/bash/utilities/tr.py +57 -0
- holmes/plugins/toolsets/bash/utilities/uniq.py +12 -0
- holmes/plugins/toolsets/bash/utilities/wc.py +12 -0
- holmes/plugins/toolsets/prometheus/prometheus.py +42 -12
- holmes/utils/console/logging.py +6 -1
- {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/METADATA +1 -1
- {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/RECORD +53 -30
- holmes/plugins/toolsets/bash/grep/__init__.py +0 -52
- {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/LICENSE.txt +0 -0
- {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/WHEEL +0 -0
- {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Any, Optional
|
|
2
|
+
from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
|
|
2
3
|
from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
|
|
3
4
|
from holmes.plugins.toolsets.bash.kubectl.constants import (
|
|
4
5
|
SAFE_NAME_PATTERN,
|
|
@@ -6,95 +7,95 @@ from holmes.plugins.toolsets.bash.kubectl.constants import (
|
|
|
6
7
|
SAFE_SELECTOR_PATTERN,
|
|
7
8
|
VALID_RESOURCE_TYPES,
|
|
8
9
|
)
|
|
9
|
-
from holmes.plugins.toolsets.bash.kubectl.kubectl_describe import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from holmes.plugins.toolsets.bash.kubectl.
|
|
14
|
-
create_kubectl_events_parser,
|
|
15
|
-
stringify_events_command,
|
|
16
|
-
)
|
|
17
|
-
from holmes.plugins.toolsets.bash.kubectl.kubectl_logs import (
|
|
18
|
-
create_kubectl_logs_parser,
|
|
19
|
-
stringify_logs_command,
|
|
20
|
-
)
|
|
21
|
-
from holmes.plugins.toolsets.bash.kubectl.kubectl_top import (
|
|
22
|
-
create_kubectl_top_parser,
|
|
23
|
-
stringify_top_command,
|
|
24
|
-
)
|
|
25
|
-
from holmes.plugins.toolsets.bash.kubectl.kubectl_get import (
|
|
26
|
-
create_kubectl_get_parser,
|
|
27
|
-
stringify_get_command,
|
|
28
|
-
)
|
|
10
|
+
from holmes.plugins.toolsets.bash.kubectl.kubectl_describe import KubectlDescribeCommand
|
|
11
|
+
from holmes.plugins.toolsets.bash.kubectl.kubectl_events import KubectlEventsCommand
|
|
12
|
+
from holmes.plugins.toolsets.bash.kubectl.kubectl_logs import KubectlLogsCommand
|
|
13
|
+
from holmes.plugins.toolsets.bash.kubectl.kubectl_top import KubectlTopCommand
|
|
14
|
+
from holmes.plugins.toolsets.bash.kubectl.kubectl_get import KubectlGetCommand
|
|
29
15
|
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"kubectl"
|
|
34
|
-
)
|
|
35
|
-
action_subparsers = kubectl_parser.add_subparsers(
|
|
36
|
-
dest="action",
|
|
37
|
-
required=True,
|
|
38
|
-
help="Action to perform (e.g., get, apply, delete)",
|
|
39
|
-
)
|
|
40
|
-
create_kubectl_get_parser(action_subparsers)
|
|
41
|
-
create_kubectl_describe_parser(action_subparsers)
|
|
42
|
-
create_kubectl_top_parser(action_subparsers)
|
|
43
|
-
create_kubectl_events_parser(action_subparsers)
|
|
44
|
-
create_kubectl_logs_parser(action_subparsers)
|
|
17
|
+
class KubectlCommand(BashCommand):
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__("kubectl")
|
|
45
20
|
|
|
21
|
+
self.sub_commands = [
|
|
22
|
+
KubectlDescribeCommand(),
|
|
23
|
+
KubectlEventsCommand(),
|
|
24
|
+
KubectlLogsCommand(),
|
|
25
|
+
KubectlTopCommand(),
|
|
26
|
+
KubectlGetCommand(),
|
|
27
|
+
]
|
|
46
28
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
29
|
+
def add_parser(self, parent_parser: Any):
|
|
30
|
+
kubectl_parser = parent_parser.add_parser(
|
|
31
|
+
"kubectl", help="Kubernetes command-line tool", exit_on_error=False
|
|
32
|
+
)
|
|
33
|
+
action_subparsers = kubectl_parser.add_subparsers(
|
|
34
|
+
dest="action",
|
|
35
|
+
required=True,
|
|
36
|
+
help="Action to perform (e.g., get, apply, delete)",
|
|
37
|
+
)
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
hasattr(cmd, "resource_type")
|
|
56
|
-
and cmd.resource_type.lower() not in VALID_RESOURCE_TYPES
|
|
57
|
-
):
|
|
58
|
-
raise ValueError(f"Invalid resource type: {cmd.resource_type}")
|
|
39
|
+
for sub_command in self.sub_commands:
|
|
40
|
+
sub_command.add_parser(action_subparsers)
|
|
59
41
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
42
|
+
def validate_command(
|
|
43
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
44
|
+
) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Validate common kubectl command fields to prevent injection attacks.
|
|
47
|
+
Raises ValueError if validation fails.
|
|
48
|
+
"""
|
|
66
49
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
raise ValueError("
|
|
50
|
+
# Validate resource type
|
|
51
|
+
if (
|
|
52
|
+
hasattr(command, "resource_type")
|
|
53
|
+
and command.resource_type.lower() not in VALID_RESOURCE_TYPES
|
|
54
|
+
):
|
|
55
|
+
raise ValueError(f"Invalid resource type: {command.resource_type}")
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
57
|
+
# Validate resource name if provided
|
|
58
|
+
if hasattr(command, "resource_name") and command.resource_name:
|
|
59
|
+
if not SAFE_NAME_PATTERN.match(command.resource_name):
|
|
60
|
+
raise ValueError(f"Invalid resource name: {command.resource_name}")
|
|
61
|
+
if len(command.resource_name) > 253:
|
|
62
|
+
raise ValueError("Resource name too long")
|
|
80
63
|
|
|
64
|
+
# Validate namespace if provided
|
|
65
|
+
if hasattr(command, "namespace") and command.namespace:
|
|
66
|
+
if not SAFE_NAMESPACE_PATTERN.match(command.namespace):
|
|
67
|
+
raise ValueError(f"Invalid namespace: {command.namespace}")
|
|
68
|
+
if len(command.namespace) > 63:
|
|
69
|
+
raise ValueError("Namespace name too long")
|
|
81
70
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
# Validate selectors if provided
|
|
72
|
+
if hasattr(command, "selector") and command.selector:
|
|
73
|
+
if not SAFE_SELECTOR_PATTERN.match(command.selector):
|
|
74
|
+
raise ValueError(f"Invalid label selector: {command.selector}")
|
|
75
|
+
if len(command.selector) > 1000:
|
|
76
|
+
raise ValueError("Label selector too long")
|
|
77
|
+
|
|
78
|
+
# Delegate to sub-command-specific validation
|
|
79
|
+
if hasattr(command, "action"):
|
|
80
|
+
for sub_command in self.sub_commands:
|
|
81
|
+
if command.action == sub_command.name:
|
|
82
|
+
if hasattr(sub_command, "validate_command"):
|
|
83
|
+
sub_command.validate_command(command, original_command, config)
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
def stringify_command(
|
|
87
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
88
|
+
) -> str:
|
|
89
|
+
if command.cmd == "kubectl":
|
|
90
|
+
for sub_command in self.sub_commands:
|
|
91
|
+
if command.action == sub_command.name:
|
|
92
|
+
return sub_command.stringify_command(
|
|
93
|
+
command=command,
|
|
94
|
+
original_command=original_command,
|
|
95
|
+
config=config,
|
|
96
|
+
)
|
|
96
97
|
raise ValueError(
|
|
97
98
|
f"Unsupported {command.tool_name} action {command.action}. Supported actions are: get, describe, events, top, run"
|
|
98
99
|
)
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError(f"Unsupported command {command.tool_name}")
|
|
@@ -80,17 +80,3 @@ VALID_RESOURCE_TYPES = {
|
|
|
80
80
|
"pdb",
|
|
81
81
|
"all",
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
VALID_OUTPUT_FORMATS = {
|
|
86
|
-
"yaml",
|
|
87
|
-
"json",
|
|
88
|
-
"wide",
|
|
89
|
-
"name",
|
|
90
|
-
"custom-columns",
|
|
91
|
-
"custom-columns-file",
|
|
92
|
-
"go-template",
|
|
93
|
-
"go-template-file",
|
|
94
|
-
"jsonpath",
|
|
95
|
-
"jsonpath-file",
|
|
96
|
-
}
|
|
@@ -1,66 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import Any, Optional
|
|
2
3
|
|
|
4
|
+
from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
|
|
5
|
+
from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
|
|
3
6
|
from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
|
|
4
7
|
from holmes.plugins.toolsets.bash.common.validators import (
|
|
5
|
-
regex_validator,
|
|
6
8
|
whitelist_validator,
|
|
7
9
|
)
|
|
8
10
|
from holmes.plugins.toolsets.bash.kubectl.constants import (
|
|
9
|
-
SAFE_NAME_PATTERN,
|
|
10
|
-
SAFE_NAMESPACE_PATTERN,
|
|
11
|
-
SAFE_SELECTOR_PATTERN,
|
|
12
11
|
VALID_RESOURCE_TYPES,
|
|
13
12
|
)
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"describe"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
parts.append(cmd.resource_name)
|
|
51
|
-
|
|
52
|
-
if cmd.all_namespaces:
|
|
53
|
-
parts.append("--all-namespaces")
|
|
54
|
-
elif cmd.namespace:
|
|
55
|
-
parts.extend(["--namespace", cmd.namespace])
|
|
56
|
-
|
|
57
|
-
if cmd.selector:
|
|
58
|
-
parts.extend(["--selector", cmd.selector])
|
|
59
|
-
|
|
60
|
-
if cmd.field_selector:
|
|
61
|
-
parts.extend(["--field-selector", cmd.field_selector])
|
|
62
|
-
|
|
63
|
-
if cmd.include_uninitialized:
|
|
64
|
-
parts.append("--include-uninitialized")
|
|
65
|
-
|
|
66
|
-
return " ".join(escape_shell_args(parts))
|
|
15
|
+
class KubectlDescribeCommand(BashCommand):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__("describe")
|
|
18
|
+
|
|
19
|
+
def add_parser(self, parent_parser: Any):
|
|
20
|
+
parser = parent_parser.add_parser(
|
|
21
|
+
"describe",
|
|
22
|
+
help="Show details of a specific resource or group of resources",
|
|
23
|
+
exit_on_error=False, # Important for library use
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"resource_type",
|
|
27
|
+
type=whitelist_validator("resource type", VALID_RESOURCE_TYPES),
|
|
28
|
+
)
|
|
29
|
+
parser.add_argument(
|
|
30
|
+
"options",
|
|
31
|
+
nargs=argparse.REMAINDER, # Captures all remaining arguments
|
|
32
|
+
default=[], # Default to an empty list
|
|
33
|
+
)
|
|
34
|
+
return parser
|
|
35
|
+
|
|
36
|
+
def validate_command(
|
|
37
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
38
|
+
) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def stringify_command(
|
|
42
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
43
|
+
) -> str:
|
|
44
|
+
parts = ["kubectl", "describe", command.resource_type]
|
|
45
|
+
|
|
46
|
+
parts += command.options
|
|
47
|
+
|
|
48
|
+
return " ".join(escape_shell_args(parts))
|
|
@@ -1,88 +1,40 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import
|
|
3
|
-
from typing import Any
|
|
2
|
+
from typing import Any, Optional
|
|
4
3
|
|
|
4
|
+
from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
|
|
5
|
+
from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
|
|
5
6
|
from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
|
|
6
|
-
from holmes.plugins.toolsets.bash.common.validators import regex_validator
|
|
7
7
|
|
|
8
|
-
MAX_FOR_OBJ_SIZE = 253
|
|
9
|
-
for_object_pattern = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9\-_./:]*$")
|
|
10
|
-
VALID_EVENT_TYPES = {"Normal", "Warning"}
|
|
11
8
|
|
|
9
|
+
class KubectlEventsCommand(BashCommand):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__("events")
|
|
12
12
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"-n",
|
|
21
|
-
"--namespace",
|
|
22
|
-
type=regex_validator("namespace", re.compile(r"^[a-z0-9][a-z0-9\-]*$")),
|
|
23
|
-
)
|
|
24
|
-
parser.add_argument("-A", "--all-namespaces", action="store_true")
|
|
25
|
-
parser.add_argument(
|
|
26
|
-
"-l",
|
|
27
|
-
"--selector",
|
|
28
|
-
type=regex_validator("selector", re.compile(r"^[a-zA-Z0-9\-_.=,!()]+$")),
|
|
29
|
-
)
|
|
30
|
-
parser.add_argument(
|
|
31
|
-
"--field-selector",
|
|
32
|
-
type=regex_validator("field selector", re.compile(r"^[a-zA-Z0-9\-_.=,!()]+$")),
|
|
33
|
-
)
|
|
34
|
-
parser.add_argument("--for", dest="for_object", type=_validate_for_object)
|
|
35
|
-
parser.add_argument("--types", type=_validate_event_types)
|
|
36
|
-
parser.add_argument("-w", "--watch", action="store_true")
|
|
37
|
-
parser.add_argument("--no-headers", action="store_true")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _validate_for_object(value: str) -> str:
|
|
41
|
-
"""Validate the --for object parameter."""
|
|
42
|
-
|
|
43
|
-
if not for_object_pattern.match(value):
|
|
44
|
-
raise argparse.ArgumentTypeError(f"Invalid for_object: {value}")
|
|
45
|
-
if len(value) > MAX_FOR_OBJ_SIZE:
|
|
46
|
-
raise argparse.ArgumentTypeError(
|
|
47
|
-
f"for_object too long. Max allowed size is {MAX_FOR_OBJ_SIZE} but received {len(value)}"
|
|
13
|
+
def add_parser(self, parent_parser: Any):
|
|
14
|
+
parser = parent_parser.add_parser(
|
|
15
|
+
"events",
|
|
16
|
+
help="List events",
|
|
17
|
+
exit_on_error=False, # Important for library use
|
|
18
|
+
add_help=False,
|
|
19
|
+
prefix_chars="\x00", # Use null character as prefix to disable option parsing
|
|
48
20
|
)
|
|
49
|
-
return value
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def _validate_event_types(value: str) -> str:
|
|
53
|
-
"""Validate the --types parameter."""
|
|
54
|
-
type_list = [t.strip() for t in value.split(",")]
|
|
55
|
-
|
|
56
|
-
for event_type in type_list:
|
|
57
|
-
if event_type not in VALID_EVENT_TYPES:
|
|
58
|
-
raise argparse.ArgumentTypeError(f"Invalid event type: {event_type}")
|
|
59
|
-
return value
|
|
60
21
|
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"options",
|
|
24
|
+
nargs=argparse.REMAINDER, # Now REMAINDER works because it comes after a positional
|
|
25
|
+
default=[],
|
|
26
|
+
)
|
|
61
27
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
parts.append("--all-namespaces")
|
|
67
|
-
elif cmd.namespace:
|
|
68
|
-
parts.extend(["--namespace", cmd.namespace])
|
|
69
|
-
|
|
70
|
-
if cmd.selector:
|
|
71
|
-
parts.extend(["--selector", cmd.selector])
|
|
72
|
-
|
|
73
|
-
if cmd.field_selector:
|
|
74
|
-
parts.extend(["--field-selector", cmd.field_selector])
|
|
75
|
-
|
|
76
|
-
if cmd.for_object:
|
|
77
|
-
parts.extend(["--for", cmd.for_object])
|
|
78
|
-
|
|
79
|
-
if cmd.types:
|
|
80
|
-
parts.extend(["--types", cmd.types])
|
|
28
|
+
def validate_command(
|
|
29
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
30
|
+
) -> None:
|
|
31
|
+
pass
|
|
81
32
|
|
|
82
|
-
|
|
83
|
-
|
|
33
|
+
def stringify_command(
|
|
34
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
35
|
+
) -> str:
|
|
36
|
+
parts = ["kubectl", "events"]
|
|
84
37
|
|
|
85
|
-
|
|
86
|
-
parts.append("--no-headers")
|
|
38
|
+
parts += command.options
|
|
87
39
|
|
|
88
|
-
|
|
40
|
+
return " ".join(escape_shell_args(parts))
|
|
@@ -1,108 +1,48 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, Optional
|
|
2
2
|
|
|
3
|
+
import argparse
|
|
3
4
|
from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
|
|
4
5
|
from holmes.plugins.toolsets.bash.common.validators import (
|
|
5
|
-
regex_validator,
|
|
6
6
|
whitelist_validator,
|
|
7
7
|
)
|
|
8
8
|
from holmes.plugins.toolsets.bash.kubectl.constants import (
|
|
9
|
-
SAFE_JQ_PATTERN,
|
|
10
|
-
SAFE_NAME_PATTERN,
|
|
11
|
-
SAFE_NAMESPACE_PATTERN,
|
|
12
|
-
SAFE_SELECTOR_PATTERN,
|
|
13
|
-
VALID_OUTPUT_FORMATS,
|
|
14
9
|
VALID_RESOURCE_TYPES,
|
|
15
10
|
)
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
action="store_true",
|
|
55
|
-
)
|
|
56
|
-
parser.add_argument(
|
|
57
|
-
"--no-headers",
|
|
58
|
-
action="store_false",
|
|
59
|
-
)
|
|
60
|
-
parser.add_argument(
|
|
61
|
-
"--include-uninitialized",
|
|
62
|
-
action="store_true",
|
|
63
|
-
)
|
|
64
|
-
parser.add_argument(
|
|
65
|
-
"--ignore-not-found",
|
|
66
|
-
action="store_true",
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def stringify_get_command(cmd: Any) -> str:
|
|
71
|
-
parts = ["kubectl", "get", cmd.resource_type]
|
|
72
|
-
|
|
73
|
-
# Add resource name if specified
|
|
74
|
-
if cmd.resource_name:
|
|
75
|
-
parts.append(cmd.resource_name)
|
|
76
|
-
|
|
77
|
-
if cmd.all_namespaces:
|
|
78
|
-
parts.append("--all-namespaces")
|
|
79
|
-
if cmd.namespace:
|
|
80
|
-
parts.extend(["--namespace", cmd.namespace])
|
|
81
|
-
|
|
82
|
-
if cmd.selector:
|
|
83
|
-
parts.extend(["--selector", cmd.selector])
|
|
84
|
-
|
|
85
|
-
if cmd.field_selector:
|
|
86
|
-
parts.extend(["--field-selector", cmd.field_selector])
|
|
87
|
-
|
|
88
|
-
if cmd.output:
|
|
89
|
-
parts.extend(["--output", cmd.output])
|
|
90
|
-
if cmd.sort_by:
|
|
91
|
-
parts.extend(["--sort-by", cmd.sort_by])
|
|
92
|
-
|
|
93
|
-
if cmd.show_labels:
|
|
94
|
-
parts.append("--show-labels")
|
|
95
|
-
|
|
96
|
-
if cmd.show_managed_fields:
|
|
97
|
-
parts.append("--show-managed-fields")
|
|
98
|
-
|
|
99
|
-
if cmd.no_headers is not None and not cmd.no_headers:
|
|
100
|
-
parts.append("--no-headers")
|
|
101
|
-
|
|
102
|
-
if cmd.include_uninitialized:
|
|
103
|
-
parts.append("--include-uninitialized")
|
|
104
|
-
|
|
105
|
-
if cmd.ignore_not_found:
|
|
106
|
-
parts.append("--ignore-not-found")
|
|
107
|
-
|
|
108
|
-
return " ".join(escape_shell_args(parts))
|
|
12
|
+
from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
|
|
13
|
+
from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class KubectlGetCommand(BashCommand):
|
|
17
|
+
def __init__(self):
|
|
18
|
+
super().__init__("get")
|
|
19
|
+
|
|
20
|
+
def add_parser(self, parent_parser: Any):
|
|
21
|
+
parser = parent_parser.add_parser(
|
|
22
|
+
"get",
|
|
23
|
+
help="Display one or many resources",
|
|
24
|
+
exit_on_error=False, # Important for library use
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"resource_type",
|
|
28
|
+
type=whitelist_validator("resource type", VALID_RESOURCE_TYPES),
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"options",
|
|
32
|
+
nargs=argparse.REMAINDER, # Captures all remaining arguments
|
|
33
|
+
default=[], # Default to an empty list
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def validate_command(
|
|
37
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
38
|
+
) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def stringify_command(
|
|
42
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
43
|
+
) -> str:
|
|
44
|
+
parts = ["kubectl", "get", command.resource_type]
|
|
45
|
+
|
|
46
|
+
parts += command.options
|
|
47
|
+
|
|
48
|
+
return " ".join(escape_shell_args(parts))
|
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any, Optional
|
|
3
3
|
|
|
4
|
+
from holmes.plugins.toolsets.bash.common.bash_command import BashCommand
|
|
5
|
+
from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
|
|
6
|
+
from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
|
|
4
7
|
|
|
5
|
-
def create_kubectl_logs_parser(kubectl_parser: Any):
|
|
6
|
-
parser = kubectl_parser.add_parser(
|
|
7
|
-
"logs",
|
|
8
|
-
exit_on_error=False,
|
|
9
|
-
)
|
|
10
|
-
parser.add_argument(
|
|
11
|
-
"options",
|
|
12
|
-
nargs=argparse.REMAINDER, # Captures all remaining arguments
|
|
13
|
-
default=[], # Default to an empty list
|
|
14
|
-
)
|
|
15
8
|
|
|
9
|
+
class KubectlLogsCommand(BashCommand):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__("logs")
|
|
16
12
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
def add_parser(self, parent_parser: Any):
|
|
14
|
+
parser = parent_parser.add_parser(
|
|
15
|
+
"logs",
|
|
16
|
+
exit_on_error=False,
|
|
17
|
+
add_help=False, # Disable help to avoid conflicts
|
|
18
|
+
prefix_chars="\x00", # Use null character as prefix to disable option parsing
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"options",
|
|
23
|
+
nargs=argparse.REMAINDER,
|
|
24
|
+
default=[],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def validate_command(
|
|
28
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
29
|
+
) -> None:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def stringify_command(
|
|
33
|
+
self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
|
|
34
|
+
) -> str:
|
|
35
|
+
parts = ["kubectl", "logs"]
|
|
36
|
+
|
|
37
|
+
parts += command.options
|
|
38
|
+
|
|
39
|
+
return " ".join(escape_shell_args(parts))
|
|
@@ -12,7 +12,7 @@ def validate_image_and_commands(
|
|
|
12
12
|
) -> None:
|
|
13
13
|
"""
|
|
14
14
|
Validate that the image is in the whitelist and commands are allowed.
|
|
15
|
-
Raises
|
|
15
|
+
Raises ValueError if validation fails.
|
|
16
16
|
"""
|
|
17
17
|
if not config or not config.kubectl or not config.kubectl.allowed_images:
|
|
18
18
|
raise ValueError(
|