holmesgpt 0.11.5__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.

Files changed (183) hide show
  1. holmes/.git_archival.json +7 -0
  2. holmes/__init__.py +76 -0
  3. holmes/__init__.py.bak +76 -0
  4. holmes/clients/robusta_client.py +24 -0
  5. holmes/common/env_vars.py +47 -0
  6. holmes/config.py +526 -0
  7. holmes/core/__init__.py +0 -0
  8. holmes/core/conversations.py +578 -0
  9. holmes/core/investigation.py +152 -0
  10. holmes/core/investigation_structured_output.py +264 -0
  11. holmes/core/issue.py +54 -0
  12. holmes/core/llm.py +250 -0
  13. holmes/core/models.py +157 -0
  14. holmes/core/openai_formatting.py +51 -0
  15. holmes/core/performance_timing.py +72 -0
  16. holmes/core/prompt.py +42 -0
  17. holmes/core/resource_instruction.py +17 -0
  18. holmes/core/runbooks.py +26 -0
  19. holmes/core/safeguards.py +120 -0
  20. holmes/core/supabase_dal.py +540 -0
  21. holmes/core/tool_calling_llm.py +798 -0
  22. holmes/core/tools.py +566 -0
  23. holmes/core/tools_utils/__init__.py +0 -0
  24. holmes/core/tools_utils/tool_executor.py +65 -0
  25. holmes/core/tools_utils/toolset_utils.py +52 -0
  26. holmes/core/toolset_manager.py +418 -0
  27. holmes/interactive.py +229 -0
  28. holmes/main.py +1041 -0
  29. holmes/plugins/__init__.py +0 -0
  30. holmes/plugins/destinations/__init__.py +6 -0
  31. holmes/plugins/destinations/slack/__init__.py +2 -0
  32. holmes/plugins/destinations/slack/plugin.py +163 -0
  33. holmes/plugins/interfaces.py +32 -0
  34. holmes/plugins/prompts/__init__.py +48 -0
  35. holmes/plugins/prompts/_current_date_time.jinja2 +1 -0
  36. holmes/plugins/prompts/_default_log_prompt.jinja2 +11 -0
  37. holmes/plugins/prompts/_fetch_logs.jinja2 +36 -0
  38. holmes/plugins/prompts/_general_instructions.jinja2 +86 -0
  39. holmes/plugins/prompts/_global_instructions.jinja2 +12 -0
  40. holmes/plugins/prompts/_runbook_instructions.jinja2 +13 -0
  41. holmes/plugins/prompts/_toolsets_instructions.jinja2 +56 -0
  42. holmes/plugins/prompts/generic_ask.jinja2 +36 -0
  43. holmes/plugins/prompts/generic_ask_conversation.jinja2 +32 -0
  44. holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +50 -0
  45. holmes/plugins/prompts/generic_investigation.jinja2 +42 -0
  46. holmes/plugins/prompts/generic_post_processing.jinja2 +13 -0
  47. holmes/plugins/prompts/generic_ticket.jinja2 +12 -0
  48. holmes/plugins/prompts/investigation_output_format.jinja2 +32 -0
  49. holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +84 -0
  50. holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +39 -0
  51. holmes/plugins/runbooks/README.md +22 -0
  52. holmes/plugins/runbooks/__init__.py +100 -0
  53. holmes/plugins/runbooks/catalog.json +14 -0
  54. holmes/plugins/runbooks/jira.yaml +12 -0
  55. holmes/plugins/runbooks/kube-prometheus-stack.yaml +10 -0
  56. holmes/plugins/runbooks/networking/dns_troubleshooting_instructions.md +66 -0
  57. holmes/plugins/runbooks/upgrade/upgrade_troubleshooting_instructions.md +44 -0
  58. holmes/plugins/sources/github/__init__.py +77 -0
  59. holmes/plugins/sources/jira/__init__.py +123 -0
  60. holmes/plugins/sources/opsgenie/__init__.py +93 -0
  61. holmes/plugins/sources/pagerduty/__init__.py +147 -0
  62. holmes/plugins/sources/prometheus/__init__.py +0 -0
  63. holmes/plugins/sources/prometheus/models.py +104 -0
  64. holmes/plugins/sources/prometheus/plugin.py +154 -0
  65. holmes/plugins/toolsets/__init__.py +171 -0
  66. holmes/plugins/toolsets/aks-node-health.yaml +65 -0
  67. holmes/plugins/toolsets/aks.yaml +86 -0
  68. holmes/plugins/toolsets/argocd.yaml +70 -0
  69. holmes/plugins/toolsets/atlas_mongodb/instructions.jinja2 +8 -0
  70. holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +307 -0
  71. holmes/plugins/toolsets/aws.yaml +76 -0
  72. holmes/plugins/toolsets/azure_sql/__init__.py +0 -0
  73. holmes/plugins/toolsets/azure_sql/apis/alert_monitoring_api.py +600 -0
  74. holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +309 -0
  75. holmes/plugins/toolsets/azure_sql/apis/connection_failure_api.py +445 -0
  76. holmes/plugins/toolsets/azure_sql/apis/connection_monitoring_api.py +251 -0
  77. holmes/plugins/toolsets/azure_sql/apis/storage_analysis_api.py +317 -0
  78. holmes/plugins/toolsets/azure_sql/azure_base_toolset.py +55 -0
  79. holmes/plugins/toolsets/azure_sql/azure_sql_instructions.jinja2 +137 -0
  80. holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +183 -0
  81. holmes/plugins/toolsets/azure_sql/install.md +66 -0
  82. holmes/plugins/toolsets/azure_sql/tools/__init__.py +1 -0
  83. holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +324 -0
  84. holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +243 -0
  85. holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +205 -0
  86. holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +249 -0
  87. holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +373 -0
  88. holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +237 -0
  89. holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +172 -0
  90. holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +170 -0
  91. holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +188 -0
  92. holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +180 -0
  93. holmes/plugins/toolsets/azure_sql/utils.py +83 -0
  94. holmes/plugins/toolsets/bash/__init__.py +0 -0
  95. holmes/plugins/toolsets/bash/bash_instructions.jinja2 +14 -0
  96. holmes/plugins/toolsets/bash/bash_toolset.py +208 -0
  97. holmes/plugins/toolsets/bash/common/bash.py +52 -0
  98. holmes/plugins/toolsets/bash/common/config.py +14 -0
  99. holmes/plugins/toolsets/bash/common/stringify.py +25 -0
  100. holmes/plugins/toolsets/bash/common/validators.py +24 -0
  101. holmes/plugins/toolsets/bash/grep/__init__.py +52 -0
  102. holmes/plugins/toolsets/bash/kubectl/__init__.py +100 -0
  103. holmes/plugins/toolsets/bash/kubectl/constants.py +96 -0
  104. holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +66 -0
  105. holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +88 -0
  106. holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +108 -0
  107. holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +20 -0
  108. holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +46 -0
  109. holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +81 -0
  110. holmes/plugins/toolsets/bash/parse_command.py +103 -0
  111. holmes/plugins/toolsets/confluence.yaml +19 -0
  112. holmes/plugins/toolsets/consts.py +5 -0
  113. holmes/plugins/toolsets/coralogix/api.py +158 -0
  114. holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +103 -0
  115. holmes/plugins/toolsets/coralogix/utils.py +181 -0
  116. holmes/plugins/toolsets/datadog.py +153 -0
  117. holmes/plugins/toolsets/docker.yaml +46 -0
  118. holmes/plugins/toolsets/git.py +756 -0
  119. holmes/plugins/toolsets/grafana/__init__.py +0 -0
  120. holmes/plugins/toolsets/grafana/base_grafana_toolset.py +54 -0
  121. holmes/plugins/toolsets/grafana/common.py +68 -0
  122. holmes/plugins/toolsets/grafana/grafana_api.py +31 -0
  123. holmes/plugins/toolsets/grafana/loki_api.py +89 -0
  124. holmes/plugins/toolsets/grafana/tempo_api.py +124 -0
  125. holmes/plugins/toolsets/grafana/toolset_grafana.py +102 -0
  126. holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +102 -0
  127. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +10 -0
  128. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -0
  129. holmes/plugins/toolsets/grafana/trace_parser.py +195 -0
  130. holmes/plugins/toolsets/helm.yaml +42 -0
  131. holmes/plugins/toolsets/internet/internet.py +275 -0
  132. holmes/plugins/toolsets/internet/notion.py +137 -0
  133. holmes/plugins/toolsets/kafka.py +638 -0
  134. holmes/plugins/toolsets/kubernetes.yaml +255 -0
  135. holmes/plugins/toolsets/kubernetes_logs.py +426 -0
  136. holmes/plugins/toolsets/kubernetes_logs.yaml +42 -0
  137. holmes/plugins/toolsets/logging_utils/__init__.py +0 -0
  138. holmes/plugins/toolsets/logging_utils/logging_api.py +217 -0
  139. holmes/plugins/toolsets/logging_utils/types.py +0 -0
  140. holmes/plugins/toolsets/mcp/toolset_mcp.py +135 -0
  141. holmes/plugins/toolsets/newrelic.py +222 -0
  142. holmes/plugins/toolsets/opensearch/__init__.py +0 -0
  143. holmes/plugins/toolsets/opensearch/opensearch.py +245 -0
  144. holmes/plugins/toolsets/opensearch/opensearch_logs.py +151 -0
  145. holmes/plugins/toolsets/opensearch/opensearch_traces.py +211 -0
  146. holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +12 -0
  147. holmes/plugins/toolsets/opensearch/opensearch_utils.py +166 -0
  148. holmes/plugins/toolsets/prometheus/prometheus.py +818 -0
  149. holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +38 -0
  150. holmes/plugins/toolsets/rabbitmq/api.py +398 -0
  151. holmes/plugins/toolsets/rabbitmq/rabbitmq_instructions.jinja2 +37 -0
  152. holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +222 -0
  153. holmes/plugins/toolsets/robusta/__init__.py +0 -0
  154. holmes/plugins/toolsets/robusta/robusta.py +235 -0
  155. holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +24 -0
  156. holmes/plugins/toolsets/runbook/__init__.py +0 -0
  157. holmes/plugins/toolsets/runbook/runbook_fetcher.py +78 -0
  158. holmes/plugins/toolsets/service_discovery.py +92 -0
  159. holmes/plugins/toolsets/servicenow/install.md +37 -0
  160. holmes/plugins/toolsets/servicenow/instructions.jinja2 +3 -0
  161. holmes/plugins/toolsets/servicenow/servicenow.py +198 -0
  162. holmes/plugins/toolsets/slab.yaml +20 -0
  163. holmes/plugins/toolsets/utils.py +137 -0
  164. holmes/plugins/utils.py +14 -0
  165. holmes/utils/__init__.py +0 -0
  166. holmes/utils/cache.py +84 -0
  167. holmes/utils/cert_utils.py +40 -0
  168. holmes/utils/default_toolset_installation_guide.jinja2 +44 -0
  169. holmes/utils/definitions.py +13 -0
  170. holmes/utils/env.py +53 -0
  171. holmes/utils/file_utils.py +56 -0
  172. holmes/utils/global_instructions.py +20 -0
  173. holmes/utils/holmes_status.py +22 -0
  174. holmes/utils/holmes_sync_toolsets.py +80 -0
  175. holmes/utils/markdown_utils.py +55 -0
  176. holmes/utils/pydantic_utils.py +54 -0
  177. holmes/utils/robusta.py +10 -0
  178. holmes/utils/tags.py +97 -0
  179. holmesgpt-0.11.5.dist-info/LICENSE.txt +21 -0
  180. holmesgpt-0.11.5.dist-info/METADATA +400 -0
  181. holmesgpt-0.11.5.dist-info/RECORD +183 -0
  182. holmesgpt-0.11.5.dist-info/WHEEL +4 -0
  183. holmesgpt-0.11.5.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,100 @@
1
+ from typing import Any, Optional
2
+ from holmes.plugins.toolsets.bash.common.config import BashExecutorConfig
3
+ from holmes.plugins.toolsets.bash.kubectl.constants import (
4
+ SAFE_NAME_PATTERN,
5
+ SAFE_NAMESPACE_PATTERN,
6
+ SAFE_SELECTOR_PATTERN,
7
+ VALID_RESOURCE_TYPES,
8
+ )
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
+ )
29
+
30
+
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)
45
+
46
+
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
+ """
52
+
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}")
59
+
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")
66
+
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")
73
+
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")
80
+
81
+
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:
96
+ raise ValueError(
97
+ f"Unsupported {command.tool_name} action {command.action}. Supported actions are: get, describe, events, top, run"
98
+ )
99
+ else:
100
+ raise ValueError(f"Unsupported command {command.tool_name}")
@@ -0,0 +1,96 @@
1
+ import re
2
+
3
+
4
+ SAFE_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9\-_.]*$")
5
+ SAFE_NAMESPACE_PATTERN = re.compile(r"^[a-z0-9][a-z0-9\-]*$")
6
+ SAFE_SELECTOR_PATTERN = re.compile(r"^[a-zA-Z0-9\-_.=,!()]+$")
7
+ SAFE_JQ_PATTERN = re.compile(r"^[a-zA-Z0-9.][a-zA-Z0-9\-_.]*$")
8
+
9
+
10
+ VALID_RESOURCE_TYPES = {
11
+ "pods",
12
+ "pod",
13
+ "po",
14
+ "services",
15
+ "service",
16
+ "svc",
17
+ "deployments",
18
+ "deployment",
19
+ "deploy",
20
+ "replicasets",
21
+ "replicaset",
22
+ "rs",
23
+ "statefulsets",
24
+ "statefulset",
25
+ "sts",
26
+ "daemonsets",
27
+ "daemonset",
28
+ "ds",
29
+ "jobs",
30
+ "job",
31
+ "cronjobs",
32
+ "cronjob",
33
+ "cj",
34
+ "configmaps",
35
+ "configmap",
36
+ "cm",
37
+ "persistentvolumes",
38
+ "persistentvolume",
39
+ "pv",
40
+ "persistentvolumeclaims",
41
+ "persistentvolumeclaim",
42
+ "pvc",
43
+ "nodes",
44
+ "node",
45
+ "no",
46
+ "namespaces",
47
+ "namespace",
48
+ "ns",
49
+ "ingresses",
50
+ "ingress",
51
+ "ing",
52
+ "networkpolicies",
53
+ "networkpolicy",
54
+ "netpol",
55
+ "serviceaccounts",
56
+ "serviceaccount",
57
+ "sa",
58
+ "roles",
59
+ "role",
60
+ "rolebindings",
61
+ "rolebinding",
62
+ "clusterroles",
63
+ "clusterrole",
64
+ "clusterrolebindings",
65
+ "clusterrolebinding",
66
+ "endpoints",
67
+ "endpoint",
68
+ "ep",
69
+ "events",
70
+ "event",
71
+ "ev",
72
+ "horizontalpodautoscalers",
73
+ "horizontalpodautoscaler",
74
+ "hpa",
75
+ "verticalpodautoscalers",
76
+ "verticalpodautoscaler",
77
+ "vpa",
78
+ "poddisruptionbudgets",
79
+ "poddisruptionbudget",
80
+ "pdb",
81
+ "all",
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
+ }
@@ -0,0 +1,66 @@
1
+ from typing import Any
2
+
3
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
4
+ from holmes.plugins.toolsets.bash.common.validators import (
5
+ regex_validator,
6
+ whitelist_validator,
7
+ )
8
+ from holmes.plugins.toolsets.bash.kubectl.constants import (
9
+ SAFE_NAME_PATTERN,
10
+ SAFE_NAMESPACE_PATTERN,
11
+ SAFE_SELECTOR_PATTERN,
12
+ VALID_RESOURCE_TYPES,
13
+ )
14
+
15
+
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))
@@ -0,0 +1,88 @@
1
+ import argparse
2
+ import re
3
+ from typing import Any
4
+
5
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
6
+ from holmes.plugins.toolsets.bash.common.validators import regex_validator
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
+
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)}"
48
+ )
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
+
61
+
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])
81
+
82
+ if cmd.watch:
83
+ parts.append("--watch")
84
+
85
+ if cmd.no_headers:
86
+ parts.append("--no-headers")
87
+
88
+ return " ".join(escape_shell_args(parts))
@@ -0,0 +1,108 @@
1
+ from typing import Any
2
+
3
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
4
+ from holmes.plugins.toolsets.bash.common.validators import (
5
+ regex_validator,
6
+ whitelist_validator,
7
+ )
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
+ VALID_RESOURCE_TYPES,
15
+ )
16
+
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))
@@ -0,0 +1,20 @@
1
+ import argparse
2
+ from typing import Any
3
+
4
+
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
+
16
+
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
+ )
@@ -0,0 +1,46 @@
1
+ import re
2
+ from typing import Optional
3
+
4
+ from holmes.plugins.toolsets.bash.common.config import (
5
+ BashExecutorConfig,
6
+ KubectlImageConfig,
7
+ )
8
+
9
+
10
+ def validate_image_and_commands(
11
+ image: str, container_command: str, config: Optional[BashExecutorConfig]
12
+ ) -> None:
13
+ """
14
+ Validate that the image is in the whitelist and commands are allowed.
15
+ Raises ArgumentTypeError if validation fails.
16
+ """
17
+ if not config or not config.kubectl or not config.kubectl.allowed_images:
18
+ raise ValueError(
19
+ "The command `kubectl run` is not allowed. The user must whitelist specific images and commands but none have been configured."
20
+ )
21
+
22
+ # Find matching image config
23
+ image_config: Optional[KubectlImageConfig] = None
24
+ for img_config in config.kubectl.allowed_images:
25
+ if img_config.image == image:
26
+ image_config = img_config
27
+ break
28
+
29
+ if not image_config:
30
+ allowed_images = [img.image for img in config.kubectl.allowed_images]
31
+ raise ValueError(
32
+ f"Image '{image}' not allowed. Allowed images: {', '.join(allowed_images)}"
33
+ )
34
+
35
+ # Validate commands against allowed patterns
36
+ command_allowed = False
37
+ for allowed_pattern in image_config.allowed_commands:
38
+ if re.match(allowed_pattern, container_command):
39
+ command_allowed = True
40
+ break
41
+
42
+ if not command_allowed:
43
+ raise ValueError(
44
+ f"Command '{container_command}' not allowed for image '{image}'. "
45
+ f"Allowed patterns: {', '.join(image_config.allowed_commands)}"
46
+ )
@@ -0,0 +1,81 @@
1
+ from typing import Any
2
+
3
+ from holmes.plugins.toolsets.bash.common.stringify import escape_shell_args
4
+ from holmes.plugins.toolsets.bash.common.validators import regex_validator
5
+ from holmes.plugins.toolsets.bash.kubectl.constants import (
6
+ SAFE_NAME_PATTERN,
7
+ SAFE_NAMESPACE_PATTERN,
8
+ SAFE_SELECTOR_PATTERN,
9
+ )
10
+
11
+
12
+ def create_kubectl_top_parser(kubectl_parser: Any):
13
+ parser = kubectl_parser.add_parser(
14
+ "top",
15
+ help="Display resource (CPU/memory) usage",
16
+ exit_on_error=False,
17
+ )
18
+ parser.add_argument(
19
+ "resource_type",
20
+ choices=["nodes", "node", "pods", "pod"],
21
+ help="Resource type to get usage for",
22
+ )
23
+ parser.add_argument(
24
+ "resource_name",
25
+ nargs="?",
26
+ default=None,
27
+ type=regex_validator("resource name", SAFE_NAME_PATTERN),
28
+ )
29
+ parser.add_argument(
30
+ "-n", "--namespace", type=regex_validator("namespace", SAFE_NAMESPACE_PATTERN)
31
+ )
32
+ parser.add_argument("-A", "--all-namespaces", action="store_true")
33
+ parser.add_argument(
34
+ "-l", "--selector", type=regex_validator("selector", SAFE_SELECTOR_PATTERN)
35
+ )
36
+ parser.add_argument(
37
+ "--containers",
38
+ action="store_true",
39
+ help="Display containers along with pods (for pods resource type)",
40
+ )
41
+ parser.add_argument(
42
+ "--use-protocol-buffers",
43
+ action="store_true",
44
+ help="Use protocol buffers for fetching metrics",
45
+ )
46
+ parser.add_argument(
47
+ "--sort-by",
48
+ type=regex_validator("sort field", SAFE_NAME_PATTERN),
49
+ help="Sort by cpu or memory",
50
+ )
51
+ parser.add_argument("--no-headers", action="store_true")
52
+
53
+
54
+ def stringify_top_command(cmd: Any) -> str:
55
+ parts = ["kubectl", "top", cmd.resource_type]
56
+
57
+ # Add resource name if specified
58
+ if cmd.resource_name:
59
+ parts.append(cmd.resource_name)
60
+
61
+ if cmd.all_namespaces:
62
+ parts.append("--all-namespaces")
63
+ elif cmd.namespace:
64
+ parts.extend(["--namespace", cmd.namespace])
65
+
66
+ if cmd.selector:
67
+ parts.extend(["--selector", cmd.selector])
68
+
69
+ if cmd.containers:
70
+ parts.append("--containers")
71
+
72
+ if cmd.use_protocol_buffers:
73
+ parts.append("--use-protocol-buffers")
74
+
75
+ if cmd.sort_by:
76
+ parts.extend(["--sort-by", cmd.sort_by])
77
+
78
+ if cmd.no_headers:
79
+ parts.append("--no-headers")
80
+
81
+ return " ".join(escape_shell_args(parts))