kx-cli 0.0.4__tar.gz → 0.0.5__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.
- kx_cli-0.0.5/PKG-INFO +154 -0
- kx_cli-0.0.5/README.md +134 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/pyproject.toml +2 -2
- kx_cli-0.0.5/src/kx/commands/back.py +13 -0
- kx_cli-0.0.5/src/kx/commands/drop.py +9 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/exec.py +8 -4
- kx_cli-0.0.5/src/kx/commands/forward.py +13 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/rollout.py +13 -8
- kx_cli-0.0.5/src/kx/config.py +38 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/console.py +38 -1
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/main.py +67 -14
- kx_cli-0.0.5/src/kx/state.py +100 -0
- kx_cli-0.0.5/src/kx_cli.egg-info/PKG-INFO +154 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx_cli.egg-info/SOURCES.txt +8 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx_cli.egg-info/requires.txt +1 -1
- kx_cli-0.0.5/tests/test_back.py +36 -0
- kx_cli-0.0.5/tests/test_config.py +87 -0
- kx_cli-0.0.5/tests/test_drop.py +31 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_exec.py +14 -2
- kx_cli-0.0.5/tests/test_forward.py +36 -0
- kx_cli-0.0.5/tests/test_rollout.py +125 -0
- kx_cli-0.0.5/tests/test_state.py +373 -0
- kx_cli-0.0.4/PKG-INFO +0 -125
- kx_cli-0.0.4/README.md +0 -105
- kx_cli-0.0.4/src/kx/state.py +0 -38
- kx_cli-0.0.4/src/kx_cli.egg-info/PKG-INFO +0 -125
- kx_cli-0.0.4/tests/test_rollout.py +0 -69
- kx_cli-0.0.4/tests/test_state.py +0 -102
- {kx_cli-0.0.4 → kx_cli-0.0.5}/LICENSE +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/setup.cfg +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/__init__.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/__init__.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/delete.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/describe.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/edit.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/events.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/get.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/labels.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/logs.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/namespace.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/port_forward.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/scale.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/state.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/tree.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/commands/yaml.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/events.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/graph.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/index.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/k8s.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/kinds.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/kubectl.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx/types.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx_cli.egg-info/dependency_links.txt +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx_cli.egg-info/entry_points.txt +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/src/kx_cli.egg-info/top_level.txt +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_cli_get.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_console.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_describe.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_edit.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_get.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_index.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_kinds.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_labels.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_logs.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_namespace.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_port_forward.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_scale.py +0 -0
- {kx_cli-0.0.4 → kx_cli-0.0.5}/tests/test_yaml.py +0 -0
kx_cli-0.0.5/PKG-INFO
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kx-cli
|
|
3
|
+
Version: 0.0.5
|
|
4
|
+
Summary: kubectl wrapper with index-based resource selection
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typer==0.26.8
|
|
13
|
+
Requires-Dist: kubernetes
|
|
14
|
+
Requires-Dist: rich
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: ruff; extra == "dev"
|
|
17
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest; extra == "dev"
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
<img src="https://raw.githubusercontent.com/jzills/kx/main/assets/banner.svg" alt="kx — kubectl, indexed." width="800"/>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<br>
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
# kubectl, indexed
|
|
30
|
+
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/kx-cli/)
|
|
36
|
+
[](https://pypi.org/project/kx-cli/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://github.com/jzills/kx/actions/workflows/pr.yml)
|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
`kx` is a kubectl wrapper that adds index-based resource selection. Run `kx get <resource>` once, then reference any result by number instead of typing full resource names.
|
|
43
|
+
|
|
44
|
+
<div align="center">
|
|
45
|
+
<img src="https://raw.githubusercontent.com/jzills/kx/main/demo/demo.gif" alt="kx demo" width="800"/>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install kx-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### List resources
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
kx get <resource> [--match|-m <substring>] [kubectl flags...]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Fetches resources and assigns index numbers. Any extra flags (e.g. `-n <namespace>`, `-A`) are passed through to kubectl. Use `--match`/`-m` to filter results by name (substring, case-insensitive).
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
$ kx get pods
|
|
66
|
+
X NAME READY STATUS RESTARTS AGE
|
|
67
|
+
1 api-7d9f4b8c6-xkp2q 1/1 Running 0 2d
|
|
68
|
+
2 worker-6c8b5f7d9-mnt4r 1/1 Running 3 5h
|
|
69
|
+
3 postgres-0 1/1 Running 0 12d
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All subsequent commands reference resources by their `X` index from the last `kx get`.
|
|
73
|
+
|
|
74
|
+
### Commands
|
|
75
|
+
|
|
76
|
+
<!-- commands-table-start -->
|
|
77
|
+
| Command | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `kx get <resource> [--match/-m text] [kubectl flags...]` | List resources and assign index numbers for use with other commands. |
|
|
80
|
+
| `kx describe <indexes>... [kubectl flags...]` | Show full kubectl describe output for one or more indexed resources. |
|
|
81
|
+
| `kx events <indexes>...` | Show Kubernetes events for one or more indexed resources. |
|
|
82
|
+
| `kx logs <index> [kubectl flags...]` | Stream logs for an indexed resource; aggregates across pods for Deployments, StatefulSets, DaemonSets, and Services. |
|
|
83
|
+
| `kx labels <indexes>... [--selector/-s]` | Show labels for one or more indexed resources; --selector formats output as a label selector. |
|
|
84
|
+
| `kx yaml <indexes>... [--show text]` | Print the raw YAML manifest for one or more indexed resources; --show filters to specific top-level fields. |
|
|
85
|
+
| `kx delete <indexes>... [--yes/-y]` | Delete one or more indexed resources (prompts for confirmation unless --yes). |
|
|
86
|
+
| `kx edit <index> [kubectl flags...]` | Open an indexed resource in your editor via kubectl edit. |
|
|
87
|
+
| `kx exec <index> [<cmd>...] [kubectl flags...]` | Open an interactive shell in an indexed pod (bash, falling back to sh). |
|
|
88
|
+
| `kx tree <index> [--index/-i]` | Show the ownership graph for an indexed resource; --index assigns indexes to tree nodes. |
|
|
89
|
+
| `kx rollout <action> <index>` | Run a rollout action (status, restart, pause, resume, history, undo) on a Deployment, StatefulSet, or DaemonSet. |
|
|
90
|
+
| `kx scale <index> <replicas>` | Scale an indexed Deployment, StatefulSet, or ReplicaSet to a given replica count. |
|
|
91
|
+
| `kx port-forward <index> <port> [kubectl flags...]` | Forward a local port to an indexed resource (Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Service). |
|
|
92
|
+
| `kx namespace <index>` | Switch to an indexed namespace; alias: kx ns (run kx get namespaces first). |
|
|
93
|
+
| `kx state [<position>] [--all/-a]` | Show current state, jump to a history position, or list all entries with --all. |
|
|
94
|
+
| `kx drop <position>` | Remove a history entry by position (shown in kx state --all). |
|
|
95
|
+
| `kx back` | Navigate to the previous kx get result. |
|
|
96
|
+
| `kx forward` | Navigate to the next kx get result. |
|
|
97
|
+
<!-- commands-table-end -->
|
|
98
|
+
|
|
99
|
+
### Example workflow
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# list deployments, pick index 2
|
|
103
|
+
kx get deployments
|
|
104
|
+
kx describe 2
|
|
105
|
+
|
|
106
|
+
# check events on that deployment
|
|
107
|
+
kx events 2
|
|
108
|
+
|
|
109
|
+
# drill into a pod
|
|
110
|
+
kx get pods
|
|
111
|
+
kx logs 1
|
|
112
|
+
kx exec 1 # opens bash/sh
|
|
113
|
+
kx exec 1 -- env # run a specific command
|
|
114
|
+
|
|
115
|
+
# forward local port 8080 to port 80 on a service
|
|
116
|
+
kx get services
|
|
117
|
+
kx port-forward 2 8080:80
|
|
118
|
+
|
|
119
|
+
# navigate history after multiple gets
|
|
120
|
+
kx get pods
|
|
121
|
+
kx get deployments
|
|
122
|
+
kx logs 1 # logs from pod index 1
|
|
123
|
+
kx state --all # review full history
|
|
124
|
+
|
|
125
|
+
# clean up
|
|
126
|
+
kx delete 3
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## State
|
|
130
|
+
|
|
131
|
+
`kx` maintains a history of up to 10 `kx get` results in `~/.kx_state.json`. A cursor tracks your current position; index-based commands always resolve against the entry at the cursor.
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
$ kx get pods # saves a new entry, cursor advances
|
|
135
|
+
$ kx get deployments # saves another entry, cursor advances
|
|
136
|
+
$ kx logs 1 # resolves index 1 from the pods result
|
|
137
|
+
$ kx state --all # lists all history entries and the current position
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use `kx state <position>` to jump directly to any history entry, and `kx drop <position>` to remove one.
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
python -m venv .venv
|
|
146
|
+
source .venv/bin/activate
|
|
147
|
+
pip install -e ".[dev]"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Run the CLI directly:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m kx.main --help
|
|
154
|
+
```
|
kx_cli-0.0.5/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jzills/kx/main/assets/banner.svg" alt="kx — kubectl, indexed." width="800"/>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<br>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+
# kubectl, indexed
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/kx-cli/)
|
|
16
|
+
[](https://pypi.org/project/kx-cli/)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
[](https://github.com/jzills/kx/actions/workflows/pr.yml)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
`kx` is a kubectl wrapper that adds index-based resource selection. Run `kx get <resource>` once, then reference any result by number instead of typing full resource names.
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<img src="https://raw.githubusercontent.com/jzills/kx/main/demo/demo.gif" alt="kx demo" width="800"/>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install kx-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### List resources
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
kx get <resource> [--match|-m <substring>] [kubectl flags...]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Fetches resources and assigns index numbers. Any extra flags (e.g. `-n <namespace>`, `-A`) are passed through to kubectl. Use `--match`/`-m` to filter results by name (substring, case-insensitive).
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
$ kx get pods
|
|
46
|
+
X NAME READY STATUS RESTARTS AGE
|
|
47
|
+
1 api-7d9f4b8c6-xkp2q 1/1 Running 0 2d
|
|
48
|
+
2 worker-6c8b5f7d9-mnt4r 1/1 Running 3 5h
|
|
49
|
+
3 postgres-0 1/1 Running 0 12d
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
All subsequent commands reference resources by their `X` index from the last `kx get`.
|
|
53
|
+
|
|
54
|
+
### Commands
|
|
55
|
+
|
|
56
|
+
<!-- commands-table-start -->
|
|
57
|
+
| Command | Description |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `kx get <resource> [--match/-m text] [kubectl flags...]` | List resources and assign index numbers for use with other commands. |
|
|
60
|
+
| `kx describe <indexes>... [kubectl flags...]` | Show full kubectl describe output for one or more indexed resources. |
|
|
61
|
+
| `kx events <indexes>...` | Show Kubernetes events for one or more indexed resources. |
|
|
62
|
+
| `kx logs <index> [kubectl flags...]` | Stream logs for an indexed resource; aggregates across pods for Deployments, StatefulSets, DaemonSets, and Services. |
|
|
63
|
+
| `kx labels <indexes>... [--selector/-s]` | Show labels for one or more indexed resources; --selector formats output as a label selector. |
|
|
64
|
+
| `kx yaml <indexes>... [--show text]` | Print the raw YAML manifest for one or more indexed resources; --show filters to specific top-level fields. |
|
|
65
|
+
| `kx delete <indexes>... [--yes/-y]` | Delete one or more indexed resources (prompts for confirmation unless --yes). |
|
|
66
|
+
| `kx edit <index> [kubectl flags...]` | Open an indexed resource in your editor via kubectl edit. |
|
|
67
|
+
| `kx exec <index> [<cmd>...] [kubectl flags...]` | Open an interactive shell in an indexed pod (bash, falling back to sh). |
|
|
68
|
+
| `kx tree <index> [--index/-i]` | Show the ownership graph for an indexed resource; --index assigns indexes to tree nodes. |
|
|
69
|
+
| `kx rollout <action> <index>` | Run a rollout action (status, restart, pause, resume, history, undo) on a Deployment, StatefulSet, or DaemonSet. |
|
|
70
|
+
| `kx scale <index> <replicas>` | Scale an indexed Deployment, StatefulSet, or ReplicaSet to a given replica count. |
|
|
71
|
+
| `kx port-forward <index> <port> [kubectl flags...]` | Forward a local port to an indexed resource (Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Service). |
|
|
72
|
+
| `kx namespace <index>` | Switch to an indexed namespace; alias: kx ns (run kx get namespaces first). |
|
|
73
|
+
| `kx state [<position>] [--all/-a]` | Show current state, jump to a history position, or list all entries with --all. |
|
|
74
|
+
| `kx drop <position>` | Remove a history entry by position (shown in kx state --all). |
|
|
75
|
+
| `kx back` | Navigate to the previous kx get result. |
|
|
76
|
+
| `kx forward` | Navigate to the next kx get result. |
|
|
77
|
+
<!-- commands-table-end -->
|
|
78
|
+
|
|
79
|
+
### Example workflow
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# list deployments, pick index 2
|
|
83
|
+
kx get deployments
|
|
84
|
+
kx describe 2
|
|
85
|
+
|
|
86
|
+
# check events on that deployment
|
|
87
|
+
kx events 2
|
|
88
|
+
|
|
89
|
+
# drill into a pod
|
|
90
|
+
kx get pods
|
|
91
|
+
kx logs 1
|
|
92
|
+
kx exec 1 # opens bash/sh
|
|
93
|
+
kx exec 1 -- env # run a specific command
|
|
94
|
+
|
|
95
|
+
# forward local port 8080 to port 80 on a service
|
|
96
|
+
kx get services
|
|
97
|
+
kx port-forward 2 8080:80
|
|
98
|
+
|
|
99
|
+
# navigate history after multiple gets
|
|
100
|
+
kx get pods
|
|
101
|
+
kx get deployments
|
|
102
|
+
kx logs 1 # logs from pod index 1
|
|
103
|
+
kx state --all # review full history
|
|
104
|
+
|
|
105
|
+
# clean up
|
|
106
|
+
kx delete 3
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## State
|
|
110
|
+
|
|
111
|
+
`kx` maintains a history of up to 10 `kx get` results in `~/.kx_state.json`. A cursor tracks your current position; index-based commands always resolve against the entry at the cursor.
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
$ kx get pods # saves a new entry, cursor advances
|
|
115
|
+
$ kx get deployments # saves another entry, cursor advances
|
|
116
|
+
$ kx logs 1 # resolves index 1 from the pods result
|
|
117
|
+
$ kx state --all # lists all history entries and the current position
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `kx state <position>` to jump directly to any history entry, and `kx drop <position>` to remove one.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python -m venv .venv
|
|
126
|
+
source .venv/bin/activate
|
|
127
|
+
pip install -e ".[dev]"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Run the CLI directly:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python -m kx.main --help
|
|
134
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kx-cli"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.5"
|
|
4
4
|
description = "kubectl wrapper with index-based resource selection"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -11,7 +11,7 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"typer==0.26.
|
|
14
|
+
"typer==0.26.8",
|
|
15
15
|
"kubernetes",
|
|
16
16
|
"rich",
|
|
17
17
|
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dataclasses import asdict
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from kx.state import StateServiceProtocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BackCommand:
|
|
8
|
+
def __init__(self, state: StateServiceProtocol):
|
|
9
|
+
self.state = state
|
|
10
|
+
|
|
11
|
+
def execute(self) -> str:
|
|
12
|
+
state = self.state.navigate(-1)
|
|
13
|
+
return json.dumps(asdict(state), indent=2)
|
|
@@ -4,13 +4,17 @@ from kx.kinds import Kind
|
|
|
4
4
|
from kx.kubectl import KubectlServiceProtocol
|
|
5
5
|
from kx.state import StateServiceProtocol
|
|
6
6
|
|
|
7
|
-
_SHELLS = ["bash", "sh"]
|
|
8
|
-
|
|
9
7
|
|
|
10
8
|
class ExecCommand:
|
|
11
|
-
def __init__(
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
state: StateServiceProtocol,
|
|
12
|
+
kubectl: KubectlServiceProtocol,
|
|
13
|
+
shells: tuple[str, ...] = ("bash", "sh"),
|
|
14
|
+
):
|
|
12
15
|
self.state = state
|
|
13
16
|
self.kubectl = kubectl
|
|
17
|
+
self.shells = shells
|
|
14
18
|
|
|
15
19
|
def execute(
|
|
16
20
|
self, index: int, cmd: list[str] | None, extra_args: list[str] = []
|
|
@@ -26,7 +30,7 @@ class ExecCommand:
|
|
|
26
30
|
if rc != 0:
|
|
27
31
|
raise ValueError(f"Command failed in container (exit {rc}).")
|
|
28
32
|
else:
|
|
29
|
-
for shell in
|
|
33
|
+
for shell in self.shells:
|
|
30
34
|
probe_rc = self.kubectl.probe(
|
|
31
35
|
[
|
|
32
36
|
"exec",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dataclasses import asdict
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from kx.state import StateServiceProtocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ForwardCommand:
|
|
8
|
+
def __init__(self, state: StateServiceProtocol):
|
|
9
|
+
self.state = state
|
|
10
|
+
|
|
11
|
+
def execute(self) -> str:
|
|
12
|
+
state = self.state.navigate(+1)
|
|
13
|
+
return json.dumps(asdict(state), indent=2)
|
|
@@ -5,11 +5,16 @@ from kx.kubectl import KubectlServiceProtocol
|
|
|
5
5
|
from kx.state import StateServiceProtocol
|
|
6
6
|
|
|
7
7
|
_SUPPORTED_KINDS = {Kind.Deployment, Kind.StatefulSet, Kind.DaemonSet}
|
|
8
|
+
_INTERACTIVE_ACTIONS = {"status"}
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class RolloutAction(str, Enum):
|
|
11
12
|
status = "status"
|
|
12
13
|
restart = "restart"
|
|
14
|
+
pause = "pause"
|
|
15
|
+
resume = "resume"
|
|
16
|
+
history = "history"
|
|
17
|
+
undo = "undo"
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
class RolloutCommand:
|
|
@@ -17,14 +22,14 @@ class RolloutCommand:
|
|
|
17
22
|
self.kubectl = kubectl
|
|
18
23
|
self.state = state
|
|
19
24
|
|
|
20
|
-
def execute(
|
|
25
|
+
def execute(
|
|
26
|
+
self, index: int, action: RolloutAction = RolloutAction.status
|
|
27
|
+
) -> str | None:
|
|
21
28
|
name, namespace, kind = self.state.fields(index)
|
|
22
29
|
if kind not in _SUPPORTED_KINDS:
|
|
23
30
|
raise ValueError(f"rollout is not supported for '{kind}'.")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
return None
|
|
31
|
+
args = ["rollout", action.value, f"{kind}/{name}", "-n", namespace]
|
|
32
|
+
if action.value in _INTERACTIVE_ACTIONS:
|
|
33
|
+
self.kubectl.run_interactive(args)
|
|
34
|
+
return None
|
|
35
|
+
return self.kubectl.run(args)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tomllib
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
_CONFIG_FILE = Path.home() / ".kx" / "config.toml"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True, slots=True)
|
|
10
|
+
class Config:
|
|
11
|
+
max_history: int = 10
|
|
12
|
+
shells: tuple[str, ...] = ("bash", "sh")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_config() -> Config:
|
|
16
|
+
kwargs: dict = {}
|
|
17
|
+
|
|
18
|
+
if _CONFIG_FILE.exists():
|
|
19
|
+
try:
|
|
20
|
+
with open(_CONFIG_FILE, "rb") as f:
|
|
21
|
+
data = tomllib.load(f)
|
|
22
|
+
except tomllib.TOMLDecodeError as e:
|
|
23
|
+
raise SystemExit(f"kx: error reading {_CONFIG_FILE}: {e}")
|
|
24
|
+
if "max_history" in data:
|
|
25
|
+
kwargs["max_history"] = data["max_history"]
|
|
26
|
+
if "shells" in data:
|
|
27
|
+
kwargs["shells"] = tuple(data["shells"])
|
|
28
|
+
|
|
29
|
+
if "KX_MAX_HISTORY" in os.environ:
|
|
30
|
+
try:
|
|
31
|
+
kwargs["max_history"] = int(os.environ["KX_MAX_HISTORY"])
|
|
32
|
+
except ValueError:
|
|
33
|
+
raise SystemExit("kx: KX_MAX_HISTORY must be an integer")
|
|
34
|
+
|
|
35
|
+
if "KX_SHELLS" in os.environ:
|
|
36
|
+
kwargs["shells"] = tuple(os.environ["KX_SHELLS"].split(","))
|
|
37
|
+
|
|
38
|
+
return Config(**kwargs)
|
|
@@ -5,6 +5,7 @@ from rich.console import Console
|
|
|
5
5
|
from rich.table import Table
|
|
6
6
|
|
|
7
7
|
from kx.kinds import plural_display
|
|
8
|
+
from kx.state import StateHistory
|
|
8
9
|
|
|
9
10
|
COLOR_HEADER = "#3fb950"
|
|
10
11
|
COLOR_DIM = "#7d8590"
|
|
@@ -262,7 +263,7 @@ _KX_ART = [
|
|
|
262
263
|
def print_help(commands: list[tuple[str, str]]) -> None:
|
|
263
264
|
_console.print()
|
|
264
265
|
for line in _KX_ART:
|
|
265
|
-
_console.print(
|
|
266
|
+
_console.print(line, style=COLOR_HEADER, markup=False, highlight=False)
|
|
266
267
|
_console.print(f"[{COLOR_DIM}]kubectl, indexed.[/{COLOR_DIM}]")
|
|
267
268
|
_console.print()
|
|
268
269
|
_console.rule(style=COLOR_DIM)
|
|
@@ -287,6 +288,42 @@ def print_help(commands: list[tuple[str, str]]) -> None:
|
|
|
287
288
|
)
|
|
288
289
|
|
|
289
290
|
|
|
291
|
+
def render_state_history(history: StateHistory) -> None:
|
|
292
|
+
total = len(history.states)
|
|
293
|
+
label = "entry" if total == 1 else "entries"
|
|
294
|
+
_console.print(f"[{COLOR_DIM}]History · {total} {label}[/{COLOR_DIM}]")
|
|
295
|
+
table = Table(
|
|
296
|
+
show_header=True,
|
|
297
|
+
header_style=f"bold {COLOR_HEADER}",
|
|
298
|
+
box=None,
|
|
299
|
+
padding=(0, 2),
|
|
300
|
+
)
|
|
301
|
+
table.add_column("X", justify="right")
|
|
302
|
+
table.add_column("")
|
|
303
|
+
table.add_column("KIND")
|
|
304
|
+
table.add_column("NAMESPACE")
|
|
305
|
+
table.add_column("ITEMS", justify="right")
|
|
306
|
+
for position, state in enumerate(history.states, start=1):
|
|
307
|
+
is_current = (position - 1) == history.cursor
|
|
308
|
+
marker = f"[bold {COLOR_HEADER}]→[/bold {COLOR_HEADER}]" if is_current else ""
|
|
309
|
+
unique_kinds = set(state.resources.values())
|
|
310
|
+
kind_label = (
|
|
311
|
+
plural_display(next(iter(unique_kinds)))
|
|
312
|
+
if len(unique_kinds) == 1
|
|
313
|
+
else "Mixed"
|
|
314
|
+
)
|
|
315
|
+
count = len(state.resources)
|
|
316
|
+
row_color = COLOR_BODY if is_current else COLOR_DIM
|
|
317
|
+
table.add_row(
|
|
318
|
+
f"[{row_color}]{position}[/{row_color}]",
|
|
319
|
+
marker,
|
|
320
|
+
f"[{row_color}]{kind_label}[/{row_color}]",
|
|
321
|
+
f"[{row_color}]{state.namespace}[/{row_color}]",
|
|
322
|
+
f"[{row_color}]{count}[/{row_color}]",
|
|
323
|
+
)
|
|
324
|
+
_console.print(table)
|
|
325
|
+
|
|
326
|
+
|
|
290
327
|
def render_labels(labels: dict[str, str]) -> None:
|
|
291
328
|
if not labels:
|
|
292
329
|
_console.print(f"[{COLOR_DIM}]No labels.[/{COLOR_DIM}]")
|