openk8s 1.0.0 → 1.0.2
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.
- package/README.md +7 -10
- package/package.json +5 -2
- package/src/app/__tests__/app-state.test.ts +376 -0
- package/src/app/__tests__/components/inspector-tokens.test.ts +101 -0
- package/src/app/__tests__/utils.test.ts +358 -0
- package/src/app/app-actions.ts +262 -0
- package/src/app/app-state.ts +4 -262
- package/src/app/app.tsx +22 -170
- package/src/app/components/detail-sections.tsx +131 -0
- package/src/app/components/footer.tsx +52 -0
- package/src/app/components/header.tsx +37 -0
- package/src/app/components/inspector-tokens.ts +93 -0
- package/src/app/components/inspector.tsx +3 -239
- package/src/app/components/resource-rows.tsx +5 -0
- package/src/app/hooks/keyboard/filter-handlers.ts +40 -0
- package/src/app/hooks/keyboard/global-handlers.ts +134 -0
- package/src/app/hooks/keyboard/helm-handlers.ts +104 -0
- package/src/app/hooks/keyboard/logs-handlers.ts +80 -0
- package/src/app/hooks/keyboard/navigation-handlers.ts +103 -0
- package/src/app/hooks/keyboard/overlay-handlers.ts +138 -0
- package/src/app/hooks/keyboard/port-forward-handlers.ts +71 -0
- package/src/app/hooks/keyboard/shell-edit-handlers.ts +253 -0
- package/src/app/hooks/use-app-keyboard.ts +56 -621
- package/src/app/hooks/use-app-side-effects.ts +1 -1
- package/src/app/hooks/use-clipboard.ts +1 -1
- package/src/app/hooks/use-data-fetching.ts +2 -9
- package/src/app/hooks/use-log-stream.ts +1 -1
- package/src/app/hooks/use-port-forward.ts +1 -1
- package/src/app/use-footer-hints.ts +107 -0
- package/src/index.tsx +4 -0
- package/src/lib/k8s/__tests__/k8s-format.test.ts +42 -0
- package/src/lib/k8s/__tests__/resource-detail-builder.test.ts +215 -0
- package/src/lib/k8s/__tests__/resource-parser.test.ts +455 -0
- package/src/lib/k8s/detail-builders/event-builder.ts +21 -0
- package/src/lib/k8s/detail-builders/hpa-cronjob-builder.ts +63 -0
- package/src/lib/k8s/detail-builders/node-builder.ts +41 -0
- package/src/lib/k8s/detail-builders/overview-builder.ts +103 -0
- package/src/lib/k8s/detail-builders/pod-builder.ts +140 -0
- package/src/lib/k8s/detail-builders/rbac-builder.ts +57 -0
- package/src/lib/k8s/resource-detail-builder.ts +22 -502
- package/src/lib/kubectl/__tests__/kubectl-helpers.test.ts +343 -0
- package/src/lib/kubectl/__tests__/metrics-utils.test.ts +84 -0
- package/src/lib/kubectl/__tests__/spawn-utils.test.ts +56 -0
- package/src/lib/kubectl/kubectl-helpers.ts +246 -0
- package/src/lib/kubectl/kubectl-service.ts +77 -565
- package/src/lib/kubectl/kubectl-types.ts +248 -0
- package/src/lib/kubectl/metrics-utils.ts +33 -0
- package/src/lib/kubectl/spawn-utils.ts +27 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
import type { ResourceKind, ResourceRef, LogOptions } from "../k8s/types";
|
|
4
|
+
|
|
5
|
+
export interface ListNamespacesOptions {
|
|
6
|
+
context: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ListResourceKindsOptions {
|
|
10
|
+
context: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ListResourcesOptions {
|
|
14
|
+
context: string;
|
|
15
|
+
namespace: string;
|
|
16
|
+
resourceKind: ResourceKind;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GetResourceDetailOptions {
|
|
20
|
+
context: string;
|
|
21
|
+
namespace: string;
|
|
22
|
+
resourceKind: ResourceKind;
|
|
23
|
+
name: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ListResourceEventsOptions {
|
|
27
|
+
context: string;
|
|
28
|
+
namespace: string;
|
|
29
|
+
resourceRef: ResourceRef;
|
|
30
|
+
namespaced: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface EditResourceOptions {
|
|
34
|
+
context: string;
|
|
35
|
+
namespace: string;
|
|
36
|
+
resourceRef: ResourceRef;
|
|
37
|
+
namespaced: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface HelmUpgradeValuesOptions {
|
|
41
|
+
context: string;
|
|
42
|
+
namespace: string;
|
|
43
|
+
name: string;
|
|
44
|
+
chart: string;
|
|
45
|
+
valuesFile: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface HelmRollbackOptions {
|
|
49
|
+
context: string;
|
|
50
|
+
namespace: string;
|
|
51
|
+
name: string;
|
|
52
|
+
revision?: number | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface HelmUpgradeOptions {
|
|
56
|
+
context: string;
|
|
57
|
+
namespace: string;
|
|
58
|
+
name: string;
|
|
59
|
+
chart: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type PodMetricsMap = Record<string, { cpu: string; memory: string }>;
|
|
63
|
+
export type NodeMetricsMap = Record<string, { cpu: string; memory: string; cpuPct: number; memPct: number }>;
|
|
64
|
+
|
|
65
|
+
export interface StreamLogsOptions {
|
|
66
|
+
context: string;
|
|
67
|
+
namespace: string;
|
|
68
|
+
resourceRef: ResourceRef;
|
|
69
|
+
namespaced: boolean;
|
|
70
|
+
container?: string | undefined;
|
|
71
|
+
logOptions?: LogOptions | undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface StreamLogsResult {
|
|
75
|
+
target: string;
|
|
76
|
+
child: ChildProcess;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ScaleResourceOptions {
|
|
80
|
+
context: string;
|
|
81
|
+
namespace: string;
|
|
82
|
+
resourceRef: ResourceRef;
|
|
83
|
+
namespaced: boolean;
|
|
84
|
+
replicas: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface RolloutRestartOptions {
|
|
88
|
+
context: string;
|
|
89
|
+
namespace: string;
|
|
90
|
+
resourceRef: ResourceRef;
|
|
91
|
+
namespaced: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface DeleteResourceOptions {
|
|
95
|
+
context: string;
|
|
96
|
+
namespace: string;
|
|
97
|
+
resourceRef: ResourceRef;
|
|
98
|
+
namespaced: boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface StartShellOptions {
|
|
102
|
+
context: string;
|
|
103
|
+
namespace: string;
|
|
104
|
+
resourceRef: ResourceRef;
|
|
105
|
+
namespaced: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface StartPortForwardOptions {
|
|
109
|
+
context: string;
|
|
110
|
+
namespace: string;
|
|
111
|
+
resourceRef: ResourceRef;
|
|
112
|
+
namespaced: boolean;
|
|
113
|
+
localPort: number;
|
|
114
|
+
remotePort: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface GetSecretValueOptions {
|
|
118
|
+
context: string;
|
|
119
|
+
namespace: string;
|
|
120
|
+
name: string;
|
|
121
|
+
key: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface GetAllSecretValuesOptions {
|
|
125
|
+
context: string;
|
|
126
|
+
namespace: string;
|
|
127
|
+
name: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface KubeConfigContextResponse {
|
|
131
|
+
contexts?: Array<{ name?: string | undefined }> | undefined;
|
|
132
|
+
"current-context"?: string | undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface NamespaceListResponse {
|
|
136
|
+
items?: Array<{ metadata?: { name?: string | undefined } | undefined }> | undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface GenericListItem {
|
|
140
|
+
kind?: string | undefined;
|
|
141
|
+
metadata?: {
|
|
142
|
+
name?: string | undefined;
|
|
143
|
+
namespace?: string | undefined;
|
|
144
|
+
creationTimestamp?: string | undefined;
|
|
145
|
+
} | undefined;
|
|
146
|
+
spec?: Record<string, unknown> | undefined;
|
|
147
|
+
status?: Record<string, unknown> | undefined;
|
|
148
|
+
type?: string | undefined;
|
|
149
|
+
reason?: string | undefined;
|
|
150
|
+
message?: string | undefined;
|
|
151
|
+
lastTimestamp?: string | undefined;
|
|
152
|
+
firstTimestamp?: string | undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface GenericListResponse {
|
|
156
|
+
apiVersion?: string | undefined;
|
|
157
|
+
kind?: string | undefined;
|
|
158
|
+
items?: GenericListItem[] | undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface EventListResponse {
|
|
162
|
+
items?: EventResponseItem[] | undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface SecretResponse {
|
|
166
|
+
data?: Record<string, string> | undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface EventResponseItem {
|
|
170
|
+
metadata?: {
|
|
171
|
+
creationTimestamp?: string | undefined;
|
|
172
|
+
} | undefined;
|
|
173
|
+
involvedObject?: {
|
|
174
|
+
kind?: string | undefined;
|
|
175
|
+
name?: string | undefined;
|
|
176
|
+
namespace?: string | undefined;
|
|
177
|
+
} | undefined;
|
|
178
|
+
reason?: string | undefined;
|
|
179
|
+
message?: string | undefined;
|
|
180
|
+
type?: string | undefined;
|
|
181
|
+
source?: {
|
|
182
|
+
component?: string | undefined;
|
|
183
|
+
host?: string | undefined;
|
|
184
|
+
} | undefined;
|
|
185
|
+
reportingComponent?: string | undefined;
|
|
186
|
+
firstTimestamp?: string | undefined;
|
|
187
|
+
lastTimestamp?: string | undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface HelmListItem {
|
|
191
|
+
name: string;
|
|
192
|
+
namespace: string;
|
|
193
|
+
revision: string;
|
|
194
|
+
updated: string;
|
|
195
|
+
status: string;
|
|
196
|
+
chart: string;
|
|
197
|
+
app_version: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface HelmStatusResponse {
|
|
201
|
+
name: string;
|
|
202
|
+
namespace: string;
|
|
203
|
+
version: number;
|
|
204
|
+
info: {
|
|
205
|
+
first_deployed: string;
|
|
206
|
+
last_deployed: string;
|
|
207
|
+
status: string;
|
|
208
|
+
notes?: string | undefined;
|
|
209
|
+
description?: string | undefined;
|
|
210
|
+
};
|
|
211
|
+
chart: {
|
|
212
|
+
metadata: {
|
|
213
|
+
name: string;
|
|
214
|
+
version: string;
|
|
215
|
+
appVersion?: string | undefined;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export interface ApiResourcesResponse {
|
|
221
|
+
resources: ApiResourceItem[];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface ApiResourceItem {
|
|
225
|
+
name: string;
|
|
226
|
+
singularName: string;
|
|
227
|
+
namespaced: boolean;
|
|
228
|
+
group?: string;
|
|
229
|
+
version: string;
|
|
230
|
+
kind: string;
|
|
231
|
+
verbs: string[];
|
|
232
|
+
shortNames?: string[];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface PodMetricsItem {
|
|
236
|
+
metadata?: { name?: string };
|
|
237
|
+
containers?: Array<{ usage?: { cpu?: string; memory?: string } }>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface NodeMetricsItem {
|
|
241
|
+
metadata?: { name?: string };
|
|
242
|
+
usage?: { cpu?: string; memory?: string };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface AllocatableCapacity {
|
|
246
|
+
cpu: number;
|
|
247
|
+
memory: number;
|
|
248
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function parseCpuNano(value: string): number {
|
|
2
|
+
if (!value) return 0;
|
|
3
|
+
if (value.endsWith("n")) return Number.parseInt(value, 10);
|
|
4
|
+
if (value.endsWith("m")) return Number.parseInt(value, 10) * 1_000_000;
|
|
5
|
+
const n = Number.parseFloat(value);
|
|
6
|
+
return Number.isFinite(n) ? n * 1_000_000_000 : 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function parseMemBytes(value: string): number {
|
|
10
|
+
if (!value) return 0;
|
|
11
|
+
const units: Record<string, number> = {
|
|
12
|
+
Ki: 1024, Mi: 1024 ** 2, Gi: 1024 ** 3, Ti: 1024 ** 4,
|
|
13
|
+
K: 1000, M: 1000 ** 2, G: 1000 ** 3, T: 1000 ** 4,
|
|
14
|
+
};
|
|
15
|
+
for (const [suffix, mult] of Object.entries(units)) {
|
|
16
|
+
if (value.endsWith(suffix)) {
|
|
17
|
+
return Number.parseFloat(value) * mult;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return Number.parseFloat(value) || 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatCpu(nano: number): string {
|
|
24
|
+
if (nano >= 1_000_000_000) return `${(nano / 1_000_000_000).toFixed(2)}`;
|
|
25
|
+
return `${Math.round(nano / 1_000_000)}m`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function formatMem(bytes: number): string {
|
|
29
|
+
if (bytes >= 1024 ** 3) return `${(bytes / 1024 ** 3).toFixed(1)}Gi`;
|
|
30
|
+
if (bytes >= 1024 ** 2) return `${Math.round(bytes / 1024 ** 2)}Mi`;
|
|
31
|
+
if (bytes >= 1024) return `${Math.round(bytes / 1024)}Ki`;
|
|
32
|
+
return `${bytes}B`;
|
|
33
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { spawn, type ChildProcess, type StdioOptions } from "node:child_process";
|
|
2
2
|
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
4
|
+
let shutdownController: AbortController | undefined;
|
|
5
|
+
|
|
6
|
+
export function setAbortController(controller: AbortController): void {
|
|
7
|
+
shutdownController = controller;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function abortRunningProcesses(): void {
|
|
11
|
+
shutdownController?.abort();
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export interface RunCommandOptions {
|
|
6
15
|
command: string;
|
|
@@ -28,6 +37,24 @@ export function runCommand(options: RunCommandOptions): Promise<CommandResult> {
|
|
|
28
37
|
reject(new Error(`${options.command} ${options.args.slice(0, 4).join(" ")} timed out after ${timeoutMs / 1_000}s`));
|
|
29
38
|
}, timeoutMs);
|
|
30
39
|
|
|
40
|
+
const onShutdown = (): void => {
|
|
41
|
+
if (settled) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
settled = true;
|
|
45
|
+
clearTimeout(timeoutId);
|
|
46
|
+
child.kill("SIGTERM");
|
|
47
|
+
reject(new Error("Application shutting down"));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (shutdownController) {
|
|
51
|
+
if (shutdownController.signal.aborted) {
|
|
52
|
+
onShutdown();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
shutdownController.signal.addEventListener("abort", onShutdown, { once: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
let stdout = "";
|
|
32
59
|
let stderr = "";
|
|
33
60
|
|