holmesgpt 0.13.0__py3-none-any.whl → 0.13.2__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.
Files changed (118) hide show
  1. holmes/__init__.py +1 -1
  2. holmes/common/env_vars.py +11 -0
  3. holmes/config.py +3 -1
  4. holmes/core/conversations.py +0 -11
  5. holmes/core/investigation.py +0 -6
  6. holmes/core/llm.py +63 -2
  7. holmes/core/prompt.py +0 -2
  8. holmes/core/supabase_dal.py +2 -2
  9. holmes/core/todo_tasks_formatter.py +51 -0
  10. holmes/core/tool_calling_llm.py +277 -101
  11. holmes/core/tools.py +20 -4
  12. holmes/core/toolset_manager.py +1 -5
  13. holmes/core/tracing.py +1 -1
  14. holmes/interactive.py +63 -2
  15. holmes/main.py +7 -2
  16. holmes/plugins/prompts/_fetch_logs.jinja2 +4 -0
  17. holmes/plugins/prompts/_general_instructions.jinja2 +3 -1
  18. holmes/plugins/prompts/investigation_procedure.jinja2 +3 -13
  19. holmes/plugins/runbooks/CLAUDE.md +85 -0
  20. holmes/plugins/runbooks/README.md +24 -0
  21. holmes/plugins/toolsets/__init__.py +5 -1
  22. holmes/plugins/toolsets/argocd.yaml +1 -1
  23. holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +18 -6
  24. holmes/plugins/toolsets/aws.yaml +9 -5
  25. holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +3 -1
  26. holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +3 -1
  27. holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +3 -1
  28. holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +3 -1
  29. holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +3 -1
  30. holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +3 -1
  31. holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +3 -1
  32. holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +3 -1
  33. holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +3 -1
  34. holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +3 -1
  35. holmes/plugins/toolsets/bash/argocd/__init__.py +65 -0
  36. holmes/plugins/toolsets/bash/argocd/constants.py +120 -0
  37. holmes/plugins/toolsets/bash/aws/__init__.py +66 -0
  38. holmes/plugins/toolsets/bash/aws/constants.py +529 -0
  39. holmes/plugins/toolsets/bash/azure/__init__.py +56 -0
  40. holmes/plugins/toolsets/bash/azure/constants.py +339 -0
  41. holmes/plugins/toolsets/bash/bash_instructions.jinja2 +6 -7
  42. holmes/plugins/toolsets/bash/bash_toolset.py +62 -17
  43. holmes/plugins/toolsets/bash/common/bash_command.py +131 -0
  44. holmes/plugins/toolsets/bash/common/stringify.py +14 -1
  45. holmes/plugins/toolsets/bash/common/validators.py +91 -0
  46. holmes/plugins/toolsets/bash/docker/__init__.py +59 -0
  47. holmes/plugins/toolsets/bash/docker/constants.py +255 -0
  48. holmes/plugins/toolsets/bash/helm/__init__.py +61 -0
  49. holmes/plugins/toolsets/bash/helm/constants.py +92 -0
  50. holmes/plugins/toolsets/bash/kubectl/__init__.py +80 -79
  51. holmes/plugins/toolsets/bash/kubectl/constants.py +0 -14
  52. holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +38 -56
  53. holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +28 -76
  54. holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +39 -99
  55. holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +34 -15
  56. holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +1 -1
  57. holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +38 -77
  58. holmes/plugins/toolsets/bash/parse_command.py +106 -32
  59. holmes/plugins/toolsets/bash/utilities/__init__.py +0 -0
  60. holmes/plugins/toolsets/bash/utilities/base64_util.py +12 -0
  61. holmes/plugins/toolsets/bash/utilities/cut.py +12 -0
  62. holmes/plugins/toolsets/bash/utilities/grep/__init__.py +10 -0
  63. holmes/plugins/toolsets/bash/utilities/head.py +12 -0
  64. holmes/plugins/toolsets/bash/utilities/jq.py +79 -0
  65. holmes/plugins/toolsets/bash/utilities/sed.py +164 -0
  66. holmes/plugins/toolsets/bash/utilities/sort.py +15 -0
  67. holmes/plugins/toolsets/bash/utilities/tail.py +12 -0
  68. holmes/plugins/toolsets/bash/utilities/tr.py +57 -0
  69. holmes/plugins/toolsets/bash/utilities/uniq.py +12 -0
  70. holmes/plugins/toolsets/bash/utilities/wc.py +12 -0
  71. holmes/plugins/toolsets/confluence.yaml +1 -1
  72. holmes/plugins/toolsets/coralogix/api.py +3 -1
  73. holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +4 -4
  74. holmes/plugins/toolsets/coralogix/utils.py +41 -14
  75. holmes/plugins/toolsets/datadog/datadog_api.py +45 -2
  76. holmes/plugins/toolsets/datadog/datadog_general_instructions.jinja2 +208 -0
  77. holmes/plugins/toolsets/datadog/datadog_logs_instructions.jinja2 +43 -0
  78. holmes/plugins/toolsets/datadog/datadog_metrics_instructions.jinja2 +12 -9
  79. holmes/plugins/toolsets/datadog/toolset_datadog_general.py +722 -0
  80. holmes/plugins/toolsets/datadog/toolset_datadog_logs.py +17 -6
  81. holmes/plugins/toolsets/datadog/toolset_datadog_metrics.py +15 -7
  82. holmes/plugins/toolsets/datadog/toolset_datadog_rds.py +6 -2
  83. holmes/plugins/toolsets/datadog/toolset_datadog_traces.py +9 -3
  84. holmes/plugins/toolsets/docker.yaml +1 -1
  85. holmes/plugins/toolsets/git.py +15 -5
  86. holmes/plugins/toolsets/grafana/toolset_grafana.py +25 -4
  87. holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +4 -4
  88. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +5 -3
  89. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -32
  90. holmes/plugins/toolsets/helm.yaml +1 -1
  91. holmes/plugins/toolsets/internet/internet.py +4 -2
  92. holmes/plugins/toolsets/internet/notion.py +4 -2
  93. holmes/plugins/toolsets/investigator/core_investigation.py +5 -17
  94. holmes/plugins/toolsets/investigator/investigator_instructions.jinja2 +1 -5
  95. holmes/plugins/toolsets/kafka.py +19 -7
  96. holmes/plugins/toolsets/kubernetes.yaml +5 -5
  97. holmes/plugins/toolsets/kubernetes_logs.py +4 -4
  98. holmes/plugins/toolsets/kubernetes_logs.yaml +1 -1
  99. holmes/plugins/toolsets/logging_utils/logging_api.py +15 -2
  100. holmes/plugins/toolsets/mcp/toolset_mcp.py +3 -1
  101. holmes/plugins/toolsets/newrelic.py +8 -4
  102. holmes/plugins/toolsets/opensearch/opensearch.py +13 -5
  103. holmes/plugins/toolsets/opensearch/opensearch_logs.py +4 -4
  104. holmes/plugins/toolsets/opensearch/opensearch_traces.py +9 -6
  105. holmes/plugins/toolsets/prometheus/prometheus.py +198 -57
  106. holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +7 -3
  107. holmes/plugins/toolsets/robusta/robusta.py +10 -4
  108. holmes/plugins/toolsets/runbook/runbook_fetcher.py +4 -2
  109. holmes/plugins/toolsets/servicenow/servicenow.py +9 -3
  110. holmes/plugins/toolsets/slab.yaml +1 -1
  111. holmes/utils/console/logging.py +6 -1
  112. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.2.dist-info}/METADATA +3 -2
  113. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.2.dist-info}/RECORD +116 -90
  114. holmes/core/todo_manager.py +0 -88
  115. holmes/plugins/toolsets/bash/grep/__init__.py +0 -52
  116. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.2.dist-info}/LICENSE.txt +0 -0
  117. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.2.dist-info}/WHEEL +0 -0
  118. {holmesgpt-0.13.0.dist-info → holmesgpt-0.13.2.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
- create_kubectl_describe_parser,
11
- stringify_describe_command,
12
- )
13
- from holmes.plugins.toolsets.bash.kubectl.kubectl_events import (
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
- def create_kubectl_parser(parent_parser: Any):
32
- kubectl_parser = parent_parser.add_parser(
33
- "kubectl", help="Kubernetes command-line tool", exit_on_error=False
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 validate_kubectl_command(cmd: Any) -> None:
48
- """
49
- Validate common kubectl command fields to prevent injection attacks.
50
- Raises ValueError if validation fails.
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
- # Validate resource type
54
- if (
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
- # Validate resource name if provided
61
- if hasattr(cmd, "resource_name") and cmd.resource_name:
62
- if not SAFE_NAME_PATTERN.match(cmd.resource_name):
63
- raise ValueError(f"Invalid resource name: {cmd.resource_name}")
64
- if len(cmd.resource_name) > 253:
65
- raise ValueError("Resource name too long")
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
- # Validate namespace if provided
68
- if hasattr(cmd, "namespace") and cmd.namespace:
69
- if not SAFE_NAMESPACE_PATTERN.match(cmd.namespace):
70
- raise ValueError(f"Invalid namespace: {cmd.namespace}")
71
- if len(cmd.namespace) > 63:
72
- raise ValueError("Namespace name too long")
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
- # Validate selectors if provided
75
- if hasattr(cmd, "selector") and cmd.selector:
76
- if not SAFE_SELECTOR_PATTERN.match(cmd.selector):
77
- raise ValueError(f"Invalid label selector: {cmd.selector}")
78
- if len(cmd.selector) > 1000:
79
- raise ValueError("Label selector too long")
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
- def stringify_kubectl_command(command: Any, config: Optional[BashExecutorConfig]):
83
- if command.cmd == "kubectl":
84
- validate_kubectl_command(command)
85
- if command.action == "get":
86
- return stringify_get_command(command)
87
- elif command.action == "describe":
88
- return stringify_describe_command(command)
89
- elif command.action == "top":
90
- return stringify_top_command(command)
91
- elif command.action == "events":
92
- return stringify_events_command(command)
93
- elif command.action == "logs":
94
- return stringify_logs_command(command)
95
- else:
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
- else:
100
- raise ValueError(f"Unsupported command {command.tool_name}")
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
- from typing import Any
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
- def create_kubectl_describe_parser(kubectl_parser: Any):
17
- parser = kubectl_parser.add_parser(
18
- "describe",
19
- help="Show details of a specific resource or group of resources",
20
- exit_on_error=False, # Important for library use
21
- )
22
- parser.add_argument(
23
- "resource_type", type=whitelist_validator("resource type", VALID_RESOURCE_TYPES)
24
- )
25
- parser.add_argument(
26
- "resource_name",
27
- nargs="?",
28
- default=None,
29
- type=regex_validator("resource name", SAFE_NAME_PATTERN),
30
- )
31
- parser.add_argument(
32
- "-n", "--namespace", type=regex_validator("namespace", SAFE_NAMESPACE_PATTERN)
33
- )
34
- parser.add_argument("-A", "--all-namespaces", action="store_true")
35
- parser.add_argument(
36
- "-l", "--selector", type=regex_validator("selector", SAFE_SELECTOR_PATTERN)
37
- )
38
- parser.add_argument(
39
- "--field-selector",
40
- type=regex_validator("field selector", SAFE_SELECTOR_PATTERN),
41
- )
42
- parser.add_argument("--include-uninitialized", action="store_true")
43
-
44
-
45
- def stringify_describe_command(cmd: Any) -> str:
46
- parts = ["kubectl", "describe", cmd.resource_type]
47
-
48
- # Add resource name if specified
49
- if cmd.resource_name:
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 re
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 create_kubectl_events_parser(kubectl_parser: Any):
14
- parser = kubectl_parser.add_parser(
15
- "events",
16
- help="List events",
17
- exit_on_error=False, # Important for library use
18
- )
19
- parser.add_argument(
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 stringify_events_command(cmd: Any) -> str:
63
- parts = ["kubectl", "events"]
64
-
65
- if cmd.all_namespaces:
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
- if cmd.watch:
83
- parts.append("--watch")
33
+ def stringify_command(
34
+ self, command: Any, original_command: str, config: Optional[BashExecutorConfig]
35
+ ) -> str:
36
+ parts = ["kubectl", "events"]
84
37
 
85
- if cmd.no_headers:
86
- parts.append("--no-headers")
38
+ parts += command.options
87
39
 
88
- return " ".join(escape_shell_args(parts))
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
- def create_kubectl_get_parser(kubectl_parser: Any):
19
- parser = kubectl_parser.add_parser(
20
- "get",
21
- help="Display one or many resources",
22
- exit_on_error=False, # Important for library use
23
- )
24
- parser.add_argument(
25
- "resource_type", type=whitelist_validator("resource type", VALID_RESOURCE_TYPES)
26
- )
27
- parser.add_argument(
28
- "resource_name",
29
- nargs="?",
30
- default=None,
31
- type=regex_validator("resource_name", SAFE_NAME_PATTERN),
32
- )
33
- parser.add_argument(
34
- "-n", "--namespace", type=regex_validator("namespace", SAFE_NAMESPACE_PATTERN)
35
- )
36
- parser.add_argument("-A", "--all-namespaces", action="store_true")
37
- parser.add_argument(
38
- "-l", "--selector", type=regex_validator("selector", SAFE_SELECTOR_PATTERN)
39
- )
40
- parser.add_argument(
41
- "--field-selector",
42
- type=regex_validator("field-selector", SAFE_SELECTOR_PATTERN),
43
- )
44
- parser.add_argument(
45
- "-o", "--output", type=whitelist_validator("output", VALID_OUTPUT_FORMATS)
46
- )
47
- parser.add_argument("--sort-by", type=regex_validator("sort-by", SAFE_JQ_PATTERN))
48
- parser.add_argument(
49
- "--show-labels",
50
- action="store_true",
51
- )
52
- parser.add_argument(
53
- "--show-managed-fields",
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 stringify_logs_command(cmd: Any) -> str:
18
- raise ValueError(
19
- "Use the tool `fetch_pod_logs` to fetch logs instead of running `kubectl logs` commands"
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 ArgumentTypeError if validation fails.
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(