kdebug 0.3.4__tar.gz → 0.4.1__tar.gz
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.
- {kdebug-0.3.4 → kdebug-0.4.1}/.gitignore +4 -0
- {kdebug-0.3.4 → kdebug-0.4.1}/PKG-INFO +17 -8
- {kdebug-0.3.4 → kdebug-0.4.1}/README.md +16 -7
- {kdebug-0.3.4 → kdebug-0.4.1}/pyproject.toml +1 -1
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/__init__.py +1 -1
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/cli.py +68 -22
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/completions/__init__.py +0 -0
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/completions/_kdebug +0 -0
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/completions/kdebug.bash +0 -0
- {kdebug-0.3.4 → kdebug-0.4.1}/src/kdebug/completions/kdebug.fish +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kdebug
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Universal Kubernetes Debug Container Utility
|
|
5
5
|
Project-URL: Homepage, https://github.com/jessegoodier/kdebug
|
|
6
6
|
Project-URL: Repository, https://github.com/jessegoodier/kdebug
|
|
@@ -20,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
|
|
21
21
|
Simple utility for launching ephemeral debug containers in Kubernetes pods with interactive shell access, backup capabilities, and a colorful TUI for pod selection.
|
|
22
22
|
|
|
23
|
-
Similar to kpf
|
|
23
|
+
Similar to [kpf](https://github.com/jessegoodier/kpf), this is a python wrapper around `kubectl debug` and `kubectl cp`.
|
|
24
24
|
|
|
25
25
|
## Features
|
|
26
26
|
|
|
@@ -31,6 +31,18 @@ Similar to kpf <https://github.com/jessegoodier/kpf>, this is a python wrapper a
|
|
|
31
31
|
- 🔐 **Root Access Support** - Run debug containers as root when needed
|
|
32
32
|
- 📦 **Controller Support** - Works with Deployments, StatefulSets, and DaemonSets
|
|
33
33
|
|
|
34
|
+
<details open>
|
|
35
|
+
<summary>Demo of the debug TUI</summary>
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
</details>
|
|
39
|
+
|
|
40
|
+
<details open>
|
|
41
|
+
<summary>Demo of backups</summary>
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
</details>
|
|
45
|
+
|
|
34
46
|
## Installation
|
|
35
47
|
|
|
36
48
|
```bash
|
|
@@ -45,7 +57,7 @@ git clone https://github.com/jessegoodier/kdebug.git
|
|
|
45
57
|
cd kdebug
|
|
46
58
|
```
|
|
47
59
|
|
|
48
|
-
Then make it
|
|
60
|
+
Then make it executable and add to something in your PATH
|
|
49
61
|
|
|
50
62
|
```
|
|
51
63
|
chmod +x bin/kdebug
|
|
@@ -113,14 +125,11 @@ These options are passed to all kubectl commands, including those used for tab c
|
|
|
113
125
|
|
|
114
126
|
When no pod or controller is specified, kdebug launches an interactive menu system:
|
|
115
127
|
|
|
116
|
-
# Interactive mode - select from all resources in current namespace
|
|
117
128
|
```bash
|
|
129
|
+
# Interactive mode - select from all resources in current namespace
|
|
118
130
|
kdebug
|
|
119
|
-
```
|
|
120
131
|
|
|
121
132
|
# Interactive mode with specific namespace
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
133
|
kdebug -n openclaw
|
|
125
134
|
```
|
|
126
135
|
|
|
@@ -270,7 +279,7 @@ kdebug uses a kubecolor-inspired color scheme:
|
|
|
270
279
|
|
|
271
280
|
## Requirements
|
|
272
281
|
|
|
273
|
-
- Python 3.
|
|
282
|
+
- Python 3.9+
|
|
274
283
|
- kubectl configured with cluster access
|
|
275
284
|
- Kubernetes cluster with ephemeral containers support (v1.23+)
|
|
276
285
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Simple utility for launching ephemeral debug containers in Kubernetes pods with interactive shell access, backup capabilities, and a colorful TUI for pod selection.
|
|
4
4
|
|
|
5
|
-
Similar to kpf
|
|
5
|
+
Similar to [kpf](https://github.com/jessegoodier/kpf), this is a python wrapper around `kubectl debug` and `kubectl cp`.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -13,6 +13,18 @@ Similar to kpf <https://github.com/jessegoodier/kpf>, this is a python wrapper a
|
|
|
13
13
|
- 🔐 **Root Access Support** - Run debug containers as root when needed
|
|
14
14
|
- 📦 **Controller Support** - Works with Deployments, StatefulSets, and DaemonSets
|
|
15
15
|
|
|
16
|
+
<details open>
|
|
17
|
+
<summary>Demo of the debug TUI</summary>
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
</details>
|
|
21
|
+
|
|
22
|
+
<details open>
|
|
23
|
+
<summary>Demo of backups</summary>
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
</details>
|
|
27
|
+
|
|
16
28
|
## Installation
|
|
17
29
|
|
|
18
30
|
```bash
|
|
@@ -27,7 +39,7 @@ git clone https://github.com/jessegoodier/kdebug.git
|
|
|
27
39
|
cd kdebug
|
|
28
40
|
```
|
|
29
41
|
|
|
30
|
-
Then make it
|
|
42
|
+
Then make it executable and add to something in your PATH
|
|
31
43
|
|
|
32
44
|
```
|
|
33
45
|
chmod +x bin/kdebug
|
|
@@ -95,14 +107,11 @@ These options are passed to all kubectl commands, including those used for tab c
|
|
|
95
107
|
|
|
96
108
|
When no pod or controller is specified, kdebug launches an interactive menu system:
|
|
97
109
|
|
|
98
|
-
# Interactive mode - select from all resources in current namespace
|
|
99
110
|
```bash
|
|
111
|
+
# Interactive mode - select from all resources in current namespace
|
|
100
112
|
kdebug
|
|
101
|
-
```
|
|
102
113
|
|
|
103
114
|
# Interactive mode with specific namespace
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
115
|
kdebug -n openclaw
|
|
107
116
|
```
|
|
108
117
|
|
|
@@ -252,7 +261,7 @@ kdebug uses a kubecolor-inspired color scheme:
|
|
|
252
261
|
|
|
253
262
|
## Requirements
|
|
254
263
|
|
|
255
|
-
- Python 3.
|
|
264
|
+
- Python 3.9+
|
|
256
265
|
- kubectl configured with cluster access
|
|
257
266
|
- Kubernetes cluster with ephemeral containers support (v1.23+)
|
|
258
267
|
|
|
@@ -466,9 +466,7 @@ def select_pod(args) -> Optional[Dict]:
|
|
|
466
466
|
# Validate cluster connection and namespace before proceeding
|
|
467
467
|
error = validate_cluster_connection(namespace)
|
|
468
468
|
if error:
|
|
469
|
-
print(
|
|
470
|
-
f"{colorize('✗ Error:', Colors.RED)} {error}"
|
|
471
|
-
)
|
|
469
|
+
print(f"{colorize('✗ Error:', Colors.RED)} {error}")
|
|
472
470
|
return None
|
|
473
471
|
|
|
474
472
|
# Direct pod selection
|
|
@@ -495,12 +493,12 @@ def select_pod(args) -> Optional[Dict]:
|
|
|
495
493
|
return pods[0]
|
|
496
494
|
|
|
497
495
|
# Interactive mode - no pod or controller specified
|
|
498
|
-
print(f"\n{colorize('Starting interactive pod selection...', Colors.
|
|
496
|
+
print(f"\n{colorize('Starting interactive pod selection...', Colors.CYAN)}")
|
|
499
497
|
|
|
500
498
|
# Direct pod selection via TUI
|
|
501
499
|
pod_name = select_pod_interactive(namespace)
|
|
502
500
|
if not pod_name:
|
|
503
|
-
print(f"\n{colorize('Selection cancelled', Colors.
|
|
501
|
+
print(f"\n{colorize('Selection cancelled', Colors.CYAN)}")
|
|
504
502
|
return None
|
|
505
503
|
|
|
506
504
|
return {"name": pod_name, "namespace": namespace}
|
|
@@ -703,6 +701,42 @@ def check_pod_security_context(pod_name: str, namespace: str) -> Dict:
|
|
|
703
701
|
return {"can_run_as_root": True, "reason": "Unable to parse pod spec"}
|
|
704
702
|
|
|
705
703
|
|
|
704
|
+
def get_container_run_as_user(
|
|
705
|
+
pod_name: str, namespace: str, target_container: Optional[str]
|
|
706
|
+
) -> Optional[int]:
|
|
707
|
+
"""Detect the runAsUser UID from the target container or pod security context."""
|
|
708
|
+
cmd = f"{kubectl_base_cmd()} get pod {pod_name} -n {namespace} -o json"
|
|
709
|
+
output = run_command(cmd, check=False)
|
|
710
|
+
|
|
711
|
+
if not output:
|
|
712
|
+
return None
|
|
713
|
+
|
|
714
|
+
try:
|
|
715
|
+
pod_data = json.loads(output)
|
|
716
|
+
spec = pod_data.get("spec", {})
|
|
717
|
+
|
|
718
|
+
# Check container-level securityContext first (overrides pod-level)
|
|
719
|
+
if target_container:
|
|
720
|
+
for container in spec.get("containers", []):
|
|
721
|
+
if container.get("name") == target_container:
|
|
722
|
+
container_uid = container.get("securityContext", {}).get(
|
|
723
|
+
"runAsUser"
|
|
724
|
+
)
|
|
725
|
+
if container_uid is not None:
|
|
726
|
+
return int(container_uid)
|
|
727
|
+
break
|
|
728
|
+
|
|
729
|
+
# Fall back to pod-level securityContext
|
|
730
|
+
pod_uid = spec.get("securityContext", {}).get("runAsUser")
|
|
731
|
+
if pod_uid is not None:
|
|
732
|
+
return int(pod_uid)
|
|
733
|
+
|
|
734
|
+
except (json.JSONDecodeError, ValueError, TypeError):
|
|
735
|
+
pass
|
|
736
|
+
|
|
737
|
+
return None
|
|
738
|
+
|
|
739
|
+
|
|
706
740
|
def launch_debug_container(
|
|
707
741
|
pod_name: str,
|
|
708
742
|
namespace: str,
|
|
@@ -710,6 +744,7 @@ def launch_debug_container(
|
|
|
710
744
|
target_container: Optional[str],
|
|
711
745
|
existing_containers: List[str],
|
|
712
746
|
as_root: bool = False,
|
|
747
|
+
run_as_user: Optional[int] = None,
|
|
713
748
|
) -> Optional[str]:
|
|
714
749
|
"""Launch a debug container attached to the pod and return its name."""
|
|
715
750
|
print(f"Launching debug container for pod {colorize(pod_name, Colors.CYAN)}...")
|
|
@@ -727,12 +762,16 @@ def launch_debug_container(
|
|
|
727
762
|
file=sys.stderr,
|
|
728
763
|
)
|
|
729
764
|
print(f"{colorize('Tip:', Colors.CYAN)} Try without --as-root flag\n")
|
|
730
|
-
|
|
731
|
-
if existing_containers:
|
|
765
|
+
elif run_as_user is not None:
|
|
732
766
|
print(
|
|
733
|
-
f"
|
|
767
|
+
f"Running as UID {colorize(str(run_as_user), Colors.CYAN)} (matching target container)"
|
|
734
768
|
)
|
|
735
769
|
|
|
770
|
+
# if existing_containers:
|
|
771
|
+
# print(
|
|
772
|
+
# f"Existing ephemeral containers: {colorize(', '.join(existing_containers), Colors.BRIGHT_BLACK)}"
|
|
773
|
+
# )
|
|
774
|
+
|
|
736
775
|
# Build kubectl debug command
|
|
737
776
|
cmd_parts = [
|
|
738
777
|
f"nohup {kubectl_base_cmd()} debug -i --tty",
|
|
@@ -752,6 +791,10 @@ def launch_debug_container(
|
|
|
752
791
|
|
|
753
792
|
if as_root:
|
|
754
793
|
cmd_parts.append('--custom=<(echo \'{"securityContext":{"runAsUser":0}}\')')
|
|
794
|
+
elif run_as_user is not None:
|
|
795
|
+
cmd_parts.append(
|
|
796
|
+
f'--custom=<(echo \'{{"securityContext":{{"runAsUser":{run_as_user}}}}}\')'
|
|
797
|
+
)
|
|
755
798
|
|
|
756
799
|
cmd_parts.extend(
|
|
757
800
|
[
|
|
@@ -798,13 +841,12 @@ def exec_interactive(
|
|
|
798
841
|
) -> int:
|
|
799
842
|
"""Execute an interactive command in the debug container."""
|
|
800
843
|
print(f"\n{colorize('=' * 60, Colors.BLUE)}")
|
|
801
|
-
print(
|
|
802
|
-
|
|
803
|
-
)
|
|
844
|
+
print(f"{colorize('Starting interactive session', Colors.BOLD)} in:")
|
|
845
|
+
print(f"Pod: {colorize(pod_name, Colors.CYAN)}")
|
|
804
846
|
print(f"Container: {colorize(container_name, Colors.CYAN)}")
|
|
805
|
-
print(f"Command: {colorize(cmd, Colors.
|
|
847
|
+
print(f"Command: {colorize(cmd, Colors.CYAN)}")
|
|
806
848
|
if cd_into:
|
|
807
|
-
print(f"Directory: {colorize(cd_into, Colors.
|
|
849
|
+
print(f"Directory: {colorize(cd_into, Colors.CYAN)}")
|
|
808
850
|
print(f"{colorize('=' * 60, Colors.BLUE)}\n")
|
|
809
851
|
|
|
810
852
|
# If cd_into is specified, wrap command to cd first
|
|
@@ -883,7 +925,7 @@ def create_backup(
|
|
|
883
925
|
f"{kubectl_base_cmd()} exec {pod_name} "
|
|
884
926
|
f"-n {namespace} "
|
|
885
927
|
f"-c {container_name} "
|
|
886
|
-
f"-- ls -d {backup_path} 2>/dev/null"
|
|
928
|
+
f"-- ls -d /proc/1/root{backup_path} 2>/dev/null"
|
|
887
929
|
)
|
|
888
930
|
|
|
889
931
|
result = run_command(verify_cmd, check=False)
|
|
@@ -903,7 +945,7 @@ def create_backup(
|
|
|
903
945
|
f"{kubectl_base_cmd()} exec {pod_name} "
|
|
904
946
|
f"-n {namespace} "
|
|
905
947
|
f"-c {container_name} "
|
|
906
|
-
f"-- ls -la {parent_dir} 2>/dev/null | head -20"
|
|
948
|
+
f"-- ls -la /proc/1/root{parent_dir} 2>/dev/null | head -20"
|
|
907
949
|
)
|
|
908
950
|
parent_result = run_command(parent_cmd, check=False)
|
|
909
951
|
if parent_result:
|
|
@@ -977,7 +1019,7 @@ def create_backup(
|
|
|
977
1019
|
f"{kubectl_base_cmd()} cp "
|
|
978
1020
|
f"-n {namespace} "
|
|
979
1021
|
f"-c {container_name} "
|
|
980
|
-
f"{pod_name}
|
|
1022
|
+
f"{pod_name}:/proc/1/root{backup_path} "
|
|
981
1023
|
f"{local_filename}"
|
|
982
1024
|
)
|
|
983
1025
|
|
|
@@ -1043,7 +1085,7 @@ def parse_controller_arg(value: str) -> Tuple[str, str]:
|
|
|
1043
1085
|
controller_type, controller_name = value.split("/", 1)
|
|
1044
1086
|
if not controller_name:
|
|
1045
1087
|
raise argparse.ArgumentTypeError(
|
|
1046
|
-
|
|
1088
|
+
"Missing controller name after '/'. Expected TYPE/NAME (e.g. sts/myapp)."
|
|
1047
1089
|
)
|
|
1048
1090
|
if controller_type.lower() not in CONTROLLER_ALIASES:
|
|
1049
1091
|
valid_types = ", ".join(sorted(CONTROLLER_ALIASES.keys()))
|
|
@@ -1200,15 +1242,13 @@ Usage:
|
|
|
1200
1242
|
)
|
|
1201
1243
|
|
|
1202
1244
|
print(f"\n{colorize('=' * 60, Colors.BLUE)}")
|
|
1245
|
+
print(f"{colorize('Namespace:', Colors.BOLD)} {colorize(namespace, Colors.CYAN)}")
|
|
1203
1246
|
print(f"{colorize('Target Pod:', Colors.BOLD)} {colorize(pod_name, Colors.CYAN)}")
|
|
1204
|
-
print(
|
|
1205
|
-
f"{colorize('Namespace:', Colors.BOLD)} {colorize(namespace, Colors.MAGENTA)}"
|
|
1206
|
-
)
|
|
1207
1247
|
print(
|
|
1208
1248
|
f"{colorize('Target Container:', Colors.BOLD)} {colorize(target_container, Colors.CYAN)}"
|
|
1209
1249
|
)
|
|
1210
1250
|
print(
|
|
1211
|
-
f"{colorize('Debug Image:', Colors.BOLD)} {colorize(args.debug_image, Colors.
|
|
1251
|
+
f"{colorize('Debug Image:', Colors.BOLD)} {colorize(args.debug_image, Colors.CYAN)}"
|
|
1212
1252
|
)
|
|
1213
1253
|
print(f"{colorize('=' * 60, Colors.BLUE)}\n")
|
|
1214
1254
|
|
|
@@ -1222,7 +1262,12 @@ Usage:
|
|
|
1222
1262
|
f"Found existing ephemeral containers: {colorize(', '.join(existing_containers), Colors.BRIGHT_BLACK)}"
|
|
1223
1263
|
)
|
|
1224
1264
|
# For simplicity, we'll create a new one. In production, you might want to reuse.
|
|
1225
|
-
print(f"{colorize('Creating new debug container...', Colors.
|
|
1265
|
+
print(f"{colorize('Creating new debug container...', Colors.MAGENTA)}")
|
|
1266
|
+
|
|
1267
|
+
# Detect target container UID for the debug container
|
|
1268
|
+
run_as_user = None
|
|
1269
|
+
if not args.as_root:
|
|
1270
|
+
run_as_user = get_container_run_as_user(pod_name, namespace, target_container)
|
|
1226
1271
|
|
|
1227
1272
|
# Launch debug container
|
|
1228
1273
|
debug_container = launch_debug_container(
|
|
@@ -1232,6 +1277,7 @@ Usage:
|
|
|
1232
1277
|
target_container,
|
|
1233
1278
|
existing_containers,
|
|
1234
1279
|
args.as_root,
|
|
1280
|
+
run_as_user,
|
|
1235
1281
|
)
|
|
1236
1282
|
|
|
1237
1283
|
if not debug_container:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|