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.
- holmes/.git_archival.json +7 -0
- holmes/__init__.py +76 -0
- holmes/__init__.py.bak +76 -0
- holmes/clients/robusta_client.py +24 -0
- holmes/common/env_vars.py +47 -0
- holmes/config.py +526 -0
- holmes/core/__init__.py +0 -0
- holmes/core/conversations.py +578 -0
- holmes/core/investigation.py +152 -0
- holmes/core/investigation_structured_output.py +264 -0
- holmes/core/issue.py +54 -0
- holmes/core/llm.py +250 -0
- holmes/core/models.py +157 -0
- holmes/core/openai_formatting.py +51 -0
- holmes/core/performance_timing.py +72 -0
- holmes/core/prompt.py +42 -0
- holmes/core/resource_instruction.py +17 -0
- holmes/core/runbooks.py +26 -0
- holmes/core/safeguards.py +120 -0
- holmes/core/supabase_dal.py +540 -0
- holmes/core/tool_calling_llm.py +798 -0
- holmes/core/tools.py +566 -0
- holmes/core/tools_utils/__init__.py +0 -0
- holmes/core/tools_utils/tool_executor.py +65 -0
- holmes/core/tools_utils/toolset_utils.py +52 -0
- holmes/core/toolset_manager.py +418 -0
- holmes/interactive.py +229 -0
- holmes/main.py +1041 -0
- holmes/plugins/__init__.py +0 -0
- holmes/plugins/destinations/__init__.py +6 -0
- holmes/plugins/destinations/slack/__init__.py +2 -0
- holmes/plugins/destinations/slack/plugin.py +163 -0
- holmes/plugins/interfaces.py +32 -0
- holmes/plugins/prompts/__init__.py +48 -0
- holmes/plugins/prompts/_current_date_time.jinja2 +1 -0
- holmes/plugins/prompts/_default_log_prompt.jinja2 +11 -0
- holmes/plugins/prompts/_fetch_logs.jinja2 +36 -0
- holmes/plugins/prompts/_general_instructions.jinja2 +86 -0
- holmes/plugins/prompts/_global_instructions.jinja2 +12 -0
- holmes/plugins/prompts/_runbook_instructions.jinja2 +13 -0
- holmes/plugins/prompts/_toolsets_instructions.jinja2 +56 -0
- holmes/plugins/prompts/generic_ask.jinja2 +36 -0
- holmes/plugins/prompts/generic_ask_conversation.jinja2 +32 -0
- holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +50 -0
- holmes/plugins/prompts/generic_investigation.jinja2 +42 -0
- holmes/plugins/prompts/generic_post_processing.jinja2 +13 -0
- holmes/plugins/prompts/generic_ticket.jinja2 +12 -0
- holmes/plugins/prompts/investigation_output_format.jinja2 +32 -0
- holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +84 -0
- holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +39 -0
- holmes/plugins/runbooks/README.md +22 -0
- holmes/plugins/runbooks/__init__.py +100 -0
- holmes/plugins/runbooks/catalog.json +14 -0
- holmes/plugins/runbooks/jira.yaml +12 -0
- holmes/plugins/runbooks/kube-prometheus-stack.yaml +10 -0
- holmes/plugins/runbooks/networking/dns_troubleshooting_instructions.md +66 -0
- holmes/plugins/runbooks/upgrade/upgrade_troubleshooting_instructions.md +44 -0
- holmes/plugins/sources/github/__init__.py +77 -0
- holmes/plugins/sources/jira/__init__.py +123 -0
- holmes/plugins/sources/opsgenie/__init__.py +93 -0
- holmes/plugins/sources/pagerduty/__init__.py +147 -0
- holmes/plugins/sources/prometheus/__init__.py +0 -0
- holmes/plugins/sources/prometheus/models.py +104 -0
- holmes/plugins/sources/prometheus/plugin.py +154 -0
- holmes/plugins/toolsets/__init__.py +171 -0
- holmes/plugins/toolsets/aks-node-health.yaml +65 -0
- holmes/plugins/toolsets/aks.yaml +86 -0
- holmes/plugins/toolsets/argocd.yaml +70 -0
- holmes/plugins/toolsets/atlas_mongodb/instructions.jinja2 +8 -0
- holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +307 -0
- holmes/plugins/toolsets/aws.yaml +76 -0
- holmes/plugins/toolsets/azure_sql/__init__.py +0 -0
- holmes/plugins/toolsets/azure_sql/apis/alert_monitoring_api.py +600 -0
- holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +309 -0
- holmes/plugins/toolsets/azure_sql/apis/connection_failure_api.py +445 -0
- holmes/plugins/toolsets/azure_sql/apis/connection_monitoring_api.py +251 -0
- holmes/plugins/toolsets/azure_sql/apis/storage_analysis_api.py +317 -0
- holmes/plugins/toolsets/azure_sql/azure_base_toolset.py +55 -0
- holmes/plugins/toolsets/azure_sql/azure_sql_instructions.jinja2 +137 -0
- holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +183 -0
- holmes/plugins/toolsets/azure_sql/install.md +66 -0
- holmes/plugins/toolsets/azure_sql/tools/__init__.py +1 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +324 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +243 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +205 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +249 -0
- holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +373 -0
- holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +237 -0
- holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +172 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +170 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +188 -0
- holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +180 -0
- holmes/plugins/toolsets/azure_sql/utils.py +83 -0
- holmes/plugins/toolsets/bash/__init__.py +0 -0
- holmes/plugins/toolsets/bash/bash_instructions.jinja2 +14 -0
- holmes/plugins/toolsets/bash/bash_toolset.py +208 -0
- holmes/plugins/toolsets/bash/common/bash.py +52 -0
- holmes/plugins/toolsets/bash/common/config.py +14 -0
- holmes/plugins/toolsets/bash/common/stringify.py +25 -0
- holmes/plugins/toolsets/bash/common/validators.py +24 -0
- holmes/plugins/toolsets/bash/grep/__init__.py +52 -0
- holmes/plugins/toolsets/bash/kubectl/__init__.py +100 -0
- holmes/plugins/toolsets/bash/kubectl/constants.py +96 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +66 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +88 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +108 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +20 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +46 -0
- holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +81 -0
- holmes/plugins/toolsets/bash/parse_command.py +103 -0
- holmes/plugins/toolsets/confluence.yaml +19 -0
- holmes/plugins/toolsets/consts.py +5 -0
- holmes/plugins/toolsets/coralogix/api.py +158 -0
- holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +103 -0
- holmes/plugins/toolsets/coralogix/utils.py +181 -0
- holmes/plugins/toolsets/datadog.py +153 -0
- holmes/plugins/toolsets/docker.yaml +46 -0
- holmes/plugins/toolsets/git.py +756 -0
- holmes/plugins/toolsets/grafana/__init__.py +0 -0
- holmes/plugins/toolsets/grafana/base_grafana_toolset.py +54 -0
- holmes/plugins/toolsets/grafana/common.py +68 -0
- holmes/plugins/toolsets/grafana/grafana_api.py +31 -0
- holmes/plugins/toolsets/grafana/loki_api.py +89 -0
- holmes/plugins/toolsets/grafana/tempo_api.py +124 -0
- holmes/plugins/toolsets/grafana/toolset_grafana.py +102 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +102 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +10 -0
- holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -0
- holmes/plugins/toolsets/grafana/trace_parser.py +195 -0
- holmes/plugins/toolsets/helm.yaml +42 -0
- holmes/plugins/toolsets/internet/internet.py +275 -0
- holmes/plugins/toolsets/internet/notion.py +137 -0
- holmes/plugins/toolsets/kafka.py +638 -0
- holmes/plugins/toolsets/kubernetes.yaml +255 -0
- holmes/plugins/toolsets/kubernetes_logs.py +426 -0
- holmes/plugins/toolsets/kubernetes_logs.yaml +42 -0
- holmes/plugins/toolsets/logging_utils/__init__.py +0 -0
- holmes/plugins/toolsets/logging_utils/logging_api.py +217 -0
- holmes/plugins/toolsets/logging_utils/types.py +0 -0
- holmes/plugins/toolsets/mcp/toolset_mcp.py +135 -0
- holmes/plugins/toolsets/newrelic.py +222 -0
- holmes/plugins/toolsets/opensearch/__init__.py +0 -0
- holmes/plugins/toolsets/opensearch/opensearch.py +245 -0
- holmes/plugins/toolsets/opensearch/opensearch_logs.py +151 -0
- holmes/plugins/toolsets/opensearch/opensearch_traces.py +211 -0
- holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +12 -0
- holmes/plugins/toolsets/opensearch/opensearch_utils.py +166 -0
- holmes/plugins/toolsets/prometheus/prometheus.py +818 -0
- holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +38 -0
- holmes/plugins/toolsets/rabbitmq/api.py +398 -0
- holmes/plugins/toolsets/rabbitmq/rabbitmq_instructions.jinja2 +37 -0
- holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +222 -0
- holmes/plugins/toolsets/robusta/__init__.py +0 -0
- holmes/plugins/toolsets/robusta/robusta.py +235 -0
- holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +24 -0
- holmes/plugins/toolsets/runbook/__init__.py +0 -0
- holmes/plugins/toolsets/runbook/runbook_fetcher.py +78 -0
- holmes/plugins/toolsets/service_discovery.py +92 -0
- holmes/plugins/toolsets/servicenow/install.md +37 -0
- holmes/plugins/toolsets/servicenow/instructions.jinja2 +3 -0
- holmes/plugins/toolsets/servicenow/servicenow.py +198 -0
- holmes/plugins/toolsets/slab.yaml +20 -0
- holmes/plugins/toolsets/utils.py +137 -0
- holmes/plugins/utils.py +14 -0
- holmes/utils/__init__.py +0 -0
- holmes/utils/cache.py +84 -0
- holmes/utils/cert_utils.py +40 -0
- holmes/utils/default_toolset_installation_guide.jinja2 +44 -0
- holmes/utils/definitions.py +13 -0
- holmes/utils/env.py +53 -0
- holmes/utils/file_utils.py +56 -0
- holmes/utils/global_instructions.py +20 -0
- holmes/utils/holmes_status.py +22 -0
- holmes/utils/holmes_sync_toolsets.py +80 -0
- holmes/utils/markdown_utils.py +55 -0
- holmes/utils/pydantic_utils.py +54 -0
- holmes/utils/robusta.py +10 -0
- holmes/utils/tags.py +97 -0
- holmesgpt-0.11.5.dist-info/LICENSE.txt +21 -0
- holmesgpt-0.11.5.dist-info/METADATA +400 -0
- holmesgpt-0.11.5.dist-info/RECORD +183 -0
- holmesgpt-0.11.5.dist-info/WHEEL +4 -0
- 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))
|