@64mb/freelens-power 1.2.2 → 1.4.0
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 +3 -1
- package/out/main/common/store/monitoring-preferences-store.js +10 -0
- package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/renderer/ai-diagnostic-handoff.js +52 -0
- package/out/renderer/ai-diagnostic-handoff.js.map +1 -0
- package/out/renderer/ai-extras.js +40 -4
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai.js +48 -56
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/details/hpa-diagnostics-details.js +46 -0
- package/out/renderer/details/hpa-diagnostics-details.js.map +1 -0
- package/out/renderer/details/namespace-health-details.js +91 -0
- package/out/renderer/details/namespace-health-details.js.map +1 -0
- package/out/renderer/details/resource-health-details.js +135 -0
- package/out/renderer/details/resource-health-details.js.map +1 -0
- package/out/renderer/details/service-diagnostics-details.js +163 -0
- package/out/renderer/details/service-diagnostics-details.js.map +1 -0
- package/out/renderer/diagnostic-explain-button.js +16 -0
- package/out/renderer/diagnostic-explain-button.js.map +1 -0
- package/out/renderer/diagnostics-state.js +53 -0
- package/out/renderer/diagnostics-state.js.map +1 -0
- package/out/renderer/diagnostics.css.js +10 -1
- package/out/renderer/diagnostics.css.js.map +1 -1
- package/out/renderer/diagnostics.js +486 -114
- package/out/renderer/diagnostics.js.map +1 -1
- package/out/renderer/incident-copy-button.js +50 -0
- package/out/renderer/incident-copy-button.js.map +1 -0
- package/out/renderer/index.js +91 -19
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/monitoring-history-collection.js +1 -1
- package/out/renderer/monitoring.js +15 -0
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/preferences/monitoring-preferences.js +10 -0
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +98 -0
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/resource-relationships.css.js +7 -0
- package/out/renderer/resource-relationships.css.js.map +1 -0
- package/out/renderer/resource-relationships.js +571 -0
- package/out/renderer/resource-relationships.js.map +1 -0
- package/out/renderer/resource-search-distance.js +24 -0
- package/out/renderer/resource-search-distance.js.map +1 -0
- package/out/renderer/resource-search-fields.js +55 -0
- package/out/renderer/resource-search-fields.js.map +1 -0
- package/out/renderer/resource-search-model.js +193 -0
- package/out/renderer/resource-search-model.js.map +1 -0
- package/out/renderer/resource-search.js +365 -0
- package/out/renderer/resource-search.js.map +1 -0
- package/out/renderer/src/common/hpa-diagnostics.js +24 -0
- package/out/renderer/src/common/hpa-diagnostics.js.map +1 -0
- package/out/renderer/src/common/incident-summary.js +73 -0
- package/out/renderer/src/common/incident-summary.js.map +1 -0
- package/out/renderer/src/common/resource-health.js +92 -0
- package/out/renderer/src/common/resource-health.js.map +1 -0
- package/out/renderer/src/common/resource-relationships.js +155 -0
- package/out/renderer/src/common/resource-relationships.js.map +1 -0
- package/out/renderer/src/common/service-diagnostics.js +50 -0
- package/out/renderer/src/common/service-diagnostics.js.map +1 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js +10 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/renderer/src/common/timeline-groups.js +36 -0
- package/out/renderer/src/common/timeline-groups.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,14 +3,20 @@ const require__global_external_mobx = require("./_virtual/_global-external_mobx.
|
|
|
3
3
|
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
4
4
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
5
5
|
const require_ai_icon = require("./ai-icon.js");
|
|
6
|
+
const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
|
|
6
7
|
const require_date_time = require("./src/common/date-time.js");
|
|
8
|
+
const require_monitoring_collector = require("./monitoring-collector.js");
|
|
7
9
|
const require_resource_diagnostics = require("./src/common/resource-diagnostics.js");
|
|
10
|
+
const require_timeline_groups = require("./src/common/timeline-groups.js");
|
|
11
|
+
const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
|
|
8
12
|
const require_diagnostics_css = require("./diagnostics.css.js");
|
|
9
|
-
const
|
|
13
|
+
const require_diagnostics_state = require("./diagnostics-state.js");
|
|
14
|
+
const require_incident_copy_button = require("./incident-copy-button.js");
|
|
10
15
|
//#region src/renderer/diagnostics.tsx
|
|
11
16
|
var viewDescriptions = {
|
|
12
17
|
Consumers: ["What is using cluster resources?", "Compare workload usage and requests, then expand a workload to inspect its pods."],
|
|
13
18
|
Timeline: ["What changed in this workload?", "Follow related Kubernetes events and changes observed while this view is open."],
|
|
19
|
+
"Not ready": ["Why is this pod not ready?", "Inspect running pod readiness, conditions and retained events."],
|
|
14
20
|
Pending: ["Why is this pod waiting?", "Inspect scheduling evidence and resource requests before opening the pod for investigation."]
|
|
15
21
|
};
|
|
16
22
|
function DiagnosticsSelect({ label, value, onChange, options }) {
|
|
@@ -86,6 +92,7 @@ function consumerStatus(pods) {
|
|
|
86
92
|
"InvalidImageName"
|
|
87
93
|
]);
|
|
88
94
|
for (const pod of pods) {
|
|
95
|
+
if (pod.status?.phase === "Succeeded") continue;
|
|
89
96
|
const status = pod.status;
|
|
90
97
|
const reason = [...status?.containerStatuses ?? [], ...status?.initContainerStatuses ?? []].map((status) => status.state?.waiting?.reason).find((value) => value && failureReasons.has(value));
|
|
91
98
|
if (pod.status?.phase === "Failed" || reason) return {
|
|
@@ -100,14 +107,18 @@ function consumerStatus(pods) {
|
|
|
100
107
|
label: `${pendingCount} Pending`,
|
|
101
108
|
icon: "hourglass_empty"
|
|
102
109
|
};
|
|
103
|
-
if (pods.some((pod) => pod.status?.conditions?.some((condition) => condition.type === "Ready" && condition.status === "False"))) return {
|
|
110
|
+
if (pods.some((pod) => pod.status?.phase !== "Succeeded" && pod.status?.conditions?.some((condition) => condition.type === "Ready" && condition.status === "False"))) return {
|
|
104
111
|
tone: "warning",
|
|
105
112
|
label: "Not ready",
|
|
106
113
|
icon: "warning_amber"
|
|
107
114
|
};
|
|
108
|
-
if (pods.length && pods.every((pod) => pod.status?.phase === "Running" && pod.status.conditions?.some((condition) => condition.type === "Ready" && condition.status === "True"))) return {
|
|
115
|
+
if (pods.length && pods.every((pod) => pod.status?.phase === "Succeeded" || pod.status?.phase === "Running" && pod.status.conditions?.some((condition) => condition.type === "Ready" && condition.status === "True"))) return pods.every((pod) => pod.status?.phase === "Succeeded") ? {
|
|
116
|
+
tone: "healthy",
|
|
117
|
+
label: "Completed",
|
|
118
|
+
icon: "check_circle"
|
|
119
|
+
} : {
|
|
109
120
|
tone: "healthy",
|
|
110
|
-
label: "Ready",
|
|
121
|
+
label: pods.some((pod) => pod.status?.phase === "Succeeded") ? "Ready / Completed" : "Ready",
|
|
111
122
|
icon: "check_circle_outline"
|
|
112
123
|
};
|
|
113
124
|
return {
|
|
@@ -157,7 +168,12 @@ var plurals = {
|
|
|
157
168
|
function openResource(object, clusterId, event) {
|
|
158
169
|
event.stopPropagation();
|
|
159
170
|
const kind = object.kind ?? "Pod";
|
|
160
|
-
if (activeId() !== clusterId || !object.metadata.name
|
|
171
|
+
if (activeId() !== clusterId || !object.metadata.name) return;
|
|
172
|
+
if (kind === "Namespace") {
|
|
173
|
+
Promise.resolve(require_extensions.Renderer.Navigation.showDetails(`/api/v1/namespaces/${encodeURIComponent(object.metadata.name)}`)).catch(() => {});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!object.metadata.namespace || !resources[kind]) return;
|
|
161
177
|
const path = `${resources[kind]}/namespaces/${encodeURIComponent(object.metadata.namespace)}/${plurals[kind]}/${encodeURIComponent(object.metadata.name)}`;
|
|
162
178
|
Promise.resolve(require_extensions.Renderer.Navigation.showDetails(path)).catch(() => {});
|
|
163
179
|
}
|
|
@@ -209,20 +225,73 @@ function projection(data) {
|
|
|
209
225
|
return value;
|
|
210
226
|
}
|
|
211
227
|
function DiagnosticsPage() {
|
|
212
|
-
const
|
|
228
|
+
const initial = require__global_external_react.useRef(require_diagnostics_state.readDiagnosticsState(activeId() ?? "")).current;
|
|
229
|
+
const scrollContainer = require__global_external_react.useRef(null);
|
|
230
|
+
const stateCluster = require__global_external_react.useRef(activeId() ?? "");
|
|
231
|
+
const [tab, setTab] = require__global_external_react.useState(initial.tab);
|
|
213
232
|
const [snapshot, setSnapshot] = require__global_external_react.useState();
|
|
214
233
|
const [error, setError] = require__global_external_react.useState("");
|
|
215
|
-
const [filter, setFilter] = require__global_external_react.useState(
|
|
216
|
-
const [
|
|
217
|
-
const [
|
|
218
|
-
const
|
|
234
|
+
const [filter, setFilter] = require__global_external_react.useState(initial.filter);
|
|
235
|
+
const [namespaceScope, setNamespaceScope] = require__global_external_react.useState(initial.namespaceScope);
|
|
236
|
+
const [resourceScope, setResourceScope] = require__global_external_react.useState(initial.resourceScope);
|
|
237
|
+
const pendingScroll = require__global_external_react.useRef({
|
|
238
|
+
clusterId: stateCluster.current,
|
|
239
|
+
top: initial.scrollTop
|
|
240
|
+
});
|
|
241
|
+
const [needsAttention, setNeedsAttention] = require__global_external_react.useState(initial.needsAttention);
|
|
242
|
+
const [groupMode, setGroupMode] = require__global_external_react.useState(initial.groupMode);
|
|
243
|
+
const [namespacesOpen, setNamespacesOpen] = require__global_external_react.useState(initial.namespacesOpen);
|
|
219
244
|
const known = require__global_external_react.useRef(/* @__PURE__ */ new Map());
|
|
220
|
-
const [sort, setSort] = require__global_external_react.useState(
|
|
221
|
-
const [expanded, setExpanded] = require__global_external_react.useState(
|
|
222
|
-
const [selected, setSelected] = require__global_external_react.useState(
|
|
223
|
-
const [pendingUid, setPendingUid] = require__global_external_react.useState(
|
|
224
|
-
const [
|
|
225
|
-
const [
|
|
245
|
+
const [sort, setSort] = require__global_external_react.useState(initial.sort);
|
|
246
|
+
const [expanded, setExpanded] = require__global_external_react.useState(initial.expanded);
|
|
247
|
+
const [selected, setSelected] = require__global_external_react.useState(initial.selected);
|
|
248
|
+
const [pendingUid, setPendingUid] = require__global_external_react.useState(initial.pendingUid);
|
|
249
|
+
const [notReadyUid, setNotReadyUid] = require__global_external_react.useState(initial.notReadyUid ?? "");
|
|
250
|
+
const [reason, setReason] = require__global_external_react.useState(initial.reason);
|
|
251
|
+
const [warningOnly, setWarningOnly] = require__global_external_react.useState(initial.warningOnly);
|
|
252
|
+
const [groupedTimeline, setGroupedTimeline] = require__global_external_react.useState(initial.groupedTimeline);
|
|
253
|
+
const [openTimeline, setOpenTimeline] = require__global_external_react.useState(initial.openTimeline);
|
|
254
|
+
const viewState = require__global_external_react.useRef(initial);
|
|
255
|
+
viewState.current = {
|
|
256
|
+
tab,
|
|
257
|
+
filter,
|
|
258
|
+
namespaceScope,
|
|
259
|
+
resourceScope,
|
|
260
|
+
needsAttention,
|
|
261
|
+
groupMode,
|
|
262
|
+
namespacesOpen,
|
|
263
|
+
sort,
|
|
264
|
+
expanded,
|
|
265
|
+
selected,
|
|
266
|
+
pendingUid,
|
|
267
|
+
notReadyUid,
|
|
268
|
+
reason,
|
|
269
|
+
warningOnly,
|
|
270
|
+
groupedTimeline,
|
|
271
|
+
openTimeline,
|
|
272
|
+
scrollTop: viewState.current.scrollTop
|
|
273
|
+
};
|
|
274
|
+
require__global_external_react.useEffect(() => {
|
|
275
|
+
return () => require_diagnostics_state.saveDiagnosticsState(stateCluster.current, viewState.current);
|
|
276
|
+
}, [initial]);
|
|
277
|
+
require__global_external_react.useEffect(() => {
|
|
278
|
+
const focusNamespace = (event) => {
|
|
279
|
+
const detail = event.detail;
|
|
280
|
+
if (!detail || detail.clusterId !== stateCluster.current || detail.clusterId !== activeId() || !detail.namespace && !detail.resourceScope) return;
|
|
281
|
+
setTab("Consumers");
|
|
282
|
+
setNamespaceScope(detail.namespace);
|
|
283
|
+
setResourceScope(detail.resourceScope);
|
|
284
|
+
setFilter("");
|
|
285
|
+
setNeedsAttention(false);
|
|
286
|
+
setGroupMode(detail.namespace ? "namespace" : "workload");
|
|
287
|
+
setNamespacesOpen(detail.namespace ? [detail.namespace] : []);
|
|
288
|
+
viewState.current.scrollTop = 0;
|
|
289
|
+
pendingScroll.current = void 0;
|
|
290
|
+
if (scrollContainer.current) scrollContainer.current.scrollTop = 0;
|
|
291
|
+
};
|
|
292
|
+
window.addEventListener(require_diagnostics_state.DIAGNOSTICS_NAMESPACE_EVENT, focusNamespace);
|
|
293
|
+
return () => window.removeEventListener(require_diagnostics_state.DIAGNOSTICS_NAMESPACE_EVENT, focusNamespace);
|
|
294
|
+
}, []);
|
|
226
295
|
const [observed, setObserved] = require__global_external_react.useState([]);
|
|
227
296
|
const previous = require__global_external_react.useRef();
|
|
228
297
|
require__global_external_react.useEffect(() => {
|
|
@@ -236,9 +305,30 @@ function DiagnosticsPage() {
|
|
|
236
305
|
known.current.clear();
|
|
237
306
|
setSnapshot(void 0);
|
|
238
307
|
setObserved([]);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
308
|
+
require_diagnostics_state.saveDiagnosticsState(stateCluster.current, viewState.current);
|
|
309
|
+
stateCluster.current = activeId() ?? "";
|
|
310
|
+
const restored = require_diagnostics_state.readDiagnosticsState(stateCluster.current);
|
|
311
|
+
setTab(restored.tab);
|
|
312
|
+
setFilter(restored.filter);
|
|
313
|
+
setNamespaceScope(restored.namespaceScope);
|
|
314
|
+
setResourceScope(restored.resourceScope);
|
|
315
|
+
setNeedsAttention(restored.needsAttention);
|
|
316
|
+
setGroupMode(restored.groupMode);
|
|
317
|
+
setNamespacesOpen(restored.namespacesOpen);
|
|
318
|
+
setSort(restored.sort);
|
|
319
|
+
setExpanded(restored.expanded);
|
|
320
|
+
setSelected(restored.selected);
|
|
321
|
+
setPendingUid(restored.pendingUid);
|
|
322
|
+
setNotReadyUid(restored.notReadyUid ?? "");
|
|
323
|
+
setReason(restored.reason);
|
|
324
|
+
setWarningOnly(restored.warningOnly);
|
|
325
|
+
setGroupedTimeline(restored.groupedTimeline);
|
|
326
|
+
setOpenTimeline(restored.openTimeline);
|
|
327
|
+
viewState.current = restored;
|
|
328
|
+
pendingScroll.current = {
|
|
329
|
+
clusterId: stateCluster.current,
|
|
330
|
+
top: restored.scrollTop
|
|
331
|
+
};
|
|
242
332
|
setError("");
|
|
243
333
|
};
|
|
244
334
|
const load = async () => {
|
|
@@ -301,20 +391,60 @@ function DiagnosticsPage() {
|
|
|
301
391
|
};
|
|
302
392
|
}, []);
|
|
303
393
|
const data = snapshot?.clusterId === activeId() ? snapshot : void 0;
|
|
394
|
+
require__global_external_react.useLayoutEffect(() => {
|
|
395
|
+
const pending = pendingScroll.current;
|
|
396
|
+
if (!data || !pending || pending.clusterId !== data.clusterId || !scrollContainer.current) return;
|
|
397
|
+
scrollContainer.current.scrollTop = pending.top;
|
|
398
|
+
const frame = requestAnimationFrame(() => {
|
|
399
|
+
if (pendingScroll.current !== pending || !scrollContainer.current) return;
|
|
400
|
+
scrollContainer.current.scrollTop = pending.top;
|
|
401
|
+
viewState.current.scrollTop = scrollContainer.current.scrollTop;
|
|
402
|
+
pendingScroll.current = void 0;
|
|
403
|
+
});
|
|
404
|
+
return () => cancelAnimationFrame(frame);
|
|
405
|
+
}, [data]);
|
|
304
406
|
const projected = require__global_external_react.useMemo(() => data ? projection(data) : void 0, [data]);
|
|
305
|
-
const groups =
|
|
306
|
-
|
|
407
|
+
const groups = require__global_external_react.useMemo(() => {
|
|
408
|
+
if (!data || !projected) return [];
|
|
409
|
+
if (!resourceScope) return projected.groups;
|
|
410
|
+
if (resourceScope.kind === "Pod" || resourceScope.kind === "Node") {
|
|
411
|
+
const pods = (data.pods ?? []).filter((pod) => resourceScope.kind === "Pod" ? (resourceScope.uid ? pod.metadata.uid === resourceScope.uid : pod.metadata.name === resourceScope.name) && (!namespaceScope || pod.metadata.namespace === namespaceScope) : pod.spec?.nodeName === resourceScope.name);
|
|
412
|
+
if (resourceScope.kind === "Pod") {
|
|
413
|
+
const originalPods = /* @__PURE__ */ new Map();
|
|
414
|
+
return require_resource_diagnostics.buildConsumerGroups(pods.map((pod) => {
|
|
415
|
+
const focused = {
|
|
416
|
+
...pod,
|
|
417
|
+
status: {
|
|
418
|
+
...pod.status,
|
|
419
|
+
phase: void 0
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
originalPods.set(focused, pod);
|
|
423
|
+
return focused;
|
|
424
|
+
}), data.owners ?? [], projected.usage).map((group) => ({
|
|
425
|
+
...group,
|
|
426
|
+
pods: group.pods.map((pod) => originalPods.get(pod))
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
return require_resource_diagnostics.buildConsumerGroups(pods, data.owners ?? [], projected.usage);
|
|
430
|
+
}
|
|
431
|
+
return projected.groups.filter((group) => resourceScope.uid ? group.identity === resourceScope.uid : group.kind === resourceScope.kind && group.name === resourceScope.name);
|
|
432
|
+
}, [
|
|
433
|
+
data,
|
|
434
|
+
projected,
|
|
435
|
+
resourceScope,
|
|
436
|
+
namespaceScope
|
|
437
|
+
]);
|
|
438
|
+
const filtered = require__global_external_react.useMemo(() => groups.filter((group) => !namespaceScope || group.namespace === namespaceScope).filter((group) => `${group.namespace} ${group.name} ${group.kind}`.toLowerCase().includes(filter.toLowerCase())).filter((group) => !needsAttention || ["error", "warning"].includes(consumerStatus(group.pods).tone)).sort((a, b) => sort === "name" ? a.namespace.localeCompare(b.namespace) || a.name.localeCompare(b.name) || a.kind.localeCompare(b.kind) || a.identity.localeCompare(b.identity) : sort === "requests" || sort === "memoryRequests" ? b[sort === "requests" ? "cpu" : "memory"].requests - a[sort === "requests" ? "cpu" : "memory"].requests : (b[sort === "memory" ? "memory" : "cpu"].usage ?? -1) - (a[sort === "memory" ? "memory" : "cpu"].usage ?? -1)), [
|
|
307
439
|
groups,
|
|
308
440
|
filter,
|
|
441
|
+
namespaceScope,
|
|
309
442
|
sort,
|
|
310
443
|
needsAttention
|
|
311
444
|
]);
|
|
312
445
|
const warningUids = new Set((data?.events ?? []).filter((event) => event.type === "Warning").map((event) => event.involvedObject?.uid ?? event.regarding?.uid).filter((uid) => !!uid));
|
|
313
446
|
const applications = [...known.current.values()].filter((item) => !warningOnly || [...item.uids].some((uid) => warningUids.has(uid))).map((item) => item.group);
|
|
314
447
|
const selectionAvailable = applications.some((group) => groupKey(group) === selected);
|
|
315
|
-
require__global_external_react.useEffect(() => {
|
|
316
|
-
if (selected && !selectionAvailable) setSelected("");
|
|
317
|
-
}, [selected, selectionAvailable]);
|
|
318
448
|
const application = applications.find((group) => groupKey(group) === selected);
|
|
319
449
|
const uids = new Set(known.current.get(selected)?.uids ?? []);
|
|
320
450
|
const normalizedEvents = require_resource_diagnostics.normalizeEvents(data?.events ?? []);
|
|
@@ -324,11 +454,32 @@ function DiagnosticsPage() {
|
|
|
324
454
|
return [entry.id, event?.involvedObject ?? event?.regarding];
|
|
325
455
|
}));
|
|
326
456
|
const timeline = [...normalizedEvents, ...observed].filter((entry) => (!application || !!entry.objectUid && uids.has(entry.objectUid)) && (!reason || `${entry.reason} ${entry.message}`.toLowerCase().includes(reason.toLowerCase())) && (!warningOnly || warningIds.has(entry.id))).sort((a, b) => (Date.parse(b.time ?? "") || 0) - (Date.parse(a.time ?? "") || 0));
|
|
457
|
+
const timelineResource = (entry) => {
|
|
458
|
+
const object = entry.objectUid ? projected?.objects.get(entry.objectUid) : void 0;
|
|
459
|
+
const ref = eventReferences.get(entry.id);
|
|
460
|
+
return `${object?.metadata.namespace ?? ref?.namespace ?? "Unknown namespace"} / ${object?.kind ?? ref?.kind ?? "Resource"} / ${object?.metadata.name ?? ref?.name ?? entry.objectUid ?? entry.id}`;
|
|
461
|
+
};
|
|
462
|
+
const timelineGroups = require_timeline_groups.groupTimelineEntries(timeline, (entry) => {
|
|
463
|
+
if (!warningIds.has(entry.id)) return entry.id;
|
|
464
|
+
const owner = [...known.current.entries()].find(([, item]) => !!entry.objectUid && item.uids.has(entry.objectUid));
|
|
465
|
+
return owner ? `${owner[1].group.namespace}/${owner[1].group.kind}/${owner[1].group.name}` : timelineResource(entry);
|
|
466
|
+
}, timelineResource);
|
|
467
|
+
const displayedTimeline = groupedTimeline ? timelineGroups.map((group) => group.latest) : timeline;
|
|
327
468
|
const pending = (data?.pods ?? []).filter((pod) => pod.status?.phase === "Pending");
|
|
328
|
-
const
|
|
329
|
-
const
|
|
469
|
+
const attentionPods = new Set(groups.filter((group) => !namespaceScope || group.namespace === namespaceScope).flatMap((group) => group.pods).filter((pod) => ["warning", "error"].includes(consumerStatus([pod]).tone)).map((pod) => pod.metadata.uid ?? pod)).size;
|
|
470
|
+
const AttentionCard = data?.pods ? "button" : "div";
|
|
471
|
+
const notReady = (data?.pods ?? []).filter((pod) => pod.status?.phase === "Running" && ["warning", "error"].includes(consumerStatus([pod]).tone));
|
|
472
|
+
const readinessView = tab === "Not ready";
|
|
473
|
+
const triagePods = readinessView ? notReady : pending;
|
|
474
|
+
const triageUid = readinessView ? notReadyUid : pendingUid;
|
|
475
|
+
const triageLabel = readinessView ? "Not ready pod" : "Pending pod";
|
|
476
|
+
const selectedPod = triagePods.find((pod) => pod.metadata.uid === triageUid);
|
|
330
477
|
const diagnosis = selectedPod ? require_resource_diagnostics.diagnosePendingPod(selectedPod, data?.nodes ?? [], data?.events ?? [], data?.pvcs) : void 0;
|
|
331
478
|
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
479
|
+
ref: scrollContainer,
|
|
480
|
+
onScroll: (event) => {
|
|
481
|
+
if (!pendingScroll.current) viewState.current.scrollTop = event.currentTarget.scrollTop;
|
|
482
|
+
},
|
|
332
483
|
className: "power-diagnostics",
|
|
333
484
|
style: {
|
|
334
485
|
padding: 24,
|
|
@@ -350,7 +501,8 @@ function DiagnosticsPage() {
|
|
|
350
501
|
children: [
|
|
351
502
|
"Consumers",
|
|
352
503
|
"Timeline",
|
|
353
|
-
"Pending"
|
|
504
|
+
"Pending",
|
|
505
|
+
"Not ready"
|
|
354
506
|
].map((name) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
355
507
|
type: "button",
|
|
356
508
|
title: `Show ${name.toLowerCase()}`,
|
|
@@ -359,7 +511,7 @@ function DiagnosticsPage() {
|
|
|
359
511
|
children: [
|
|
360
512
|
/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: name === "Consumers" ? "account_tree" : name === "Timeline" ? "timeline" : "hourglass_empty" }),
|
|
361
513
|
name,
|
|
362
|
-
name === "Pending" && data?.pods ? ` (${pending.length})` : ""
|
|
514
|
+
name === "Pending" && data?.pods ? ` (${pending.length})` : name === "Not ready" && data?.pods ? ` (${notReady.length})` : ""
|
|
363
515
|
]
|
|
364
516
|
}, name))
|
|
365
517
|
}),
|
|
@@ -401,24 +553,24 @@ function DiagnosticsPage() {
|
|
|
401
553
|
})
|
|
402
554
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: data.pods?.length ?? "Unavailable" }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Pods in snapshot" })] })]
|
|
403
555
|
}),
|
|
404
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs(
|
|
405
|
-
className: `summary-card pending${!data.pods ? " unavailable" :
|
|
406
|
-
...
|
|
556
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs(AttentionCard, {
|
|
557
|
+
className: `summary-card pending${!data.pods ? " unavailable" : attentionPods ? " attention" : ""}`,
|
|
558
|
+
...data.pods ? {
|
|
407
559
|
type: "button",
|
|
408
|
-
title: "
|
|
409
|
-
"aria-label": "
|
|
560
|
+
title: "Show consumers needing attention",
|
|
561
|
+
"aria-label": "Show consumers needing attention",
|
|
410
562
|
onClick: () => {
|
|
411
|
-
|
|
412
|
-
|
|
563
|
+
setNeedsAttention(true);
|
|
564
|
+
setFilter("");
|
|
413
565
|
}
|
|
414
566
|
} : {},
|
|
415
567
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
416
568
|
className: "summary-icon",
|
|
417
569
|
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
418
|
-
material: "
|
|
570
|
+
material: "warning_amber",
|
|
419
571
|
size: 22
|
|
420
572
|
})
|
|
421
|
-
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: data.pods ?
|
|
573
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: data.pods ? attentionPods : "Unavailable" }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Needs attention" })] })]
|
|
422
574
|
})
|
|
423
575
|
]
|
|
424
576
|
}),
|
|
@@ -428,6 +580,42 @@ function DiagnosticsPage() {
|
|
|
428
580
|
children: "Loading resource diagnostics…"
|
|
429
581
|
}),
|
|
430
582
|
tab === "Consumers" && data && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
|
|
583
|
+
resourceScope && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
584
|
+
className: "namespace-scope",
|
|
585
|
+
role: "status",
|
|
586
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
587
|
+
className: "namespace-scope-label",
|
|
588
|
+
children: [
|
|
589
|
+
resourceScope.kind,
|
|
590
|
+
": ",
|
|
591
|
+
resourceScope.name
|
|
592
|
+
]
|
|
593
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
594
|
+
type: "button",
|
|
595
|
+
"aria-label": "Clear resource scope",
|
|
596
|
+
onClick: () => setResourceScope(void 0),
|
|
597
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
598
|
+
material: "close",
|
|
599
|
+
size: 13
|
|
600
|
+
}), "Show all resources"]
|
|
601
|
+
})]
|
|
602
|
+
}),
|
|
603
|
+
namespaceScope && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
604
|
+
className: "namespace-scope",
|
|
605
|
+
role: "status",
|
|
606
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
607
|
+
className: "namespace-scope-label",
|
|
608
|
+
children: ["Namespace: ", namespaceScope]
|
|
609
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
610
|
+
type: "button",
|
|
611
|
+
"aria-label": "Clear namespace scope",
|
|
612
|
+
onClick: () => setNamespaceScope(void 0),
|
|
613
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
614
|
+
material: "close",
|
|
615
|
+
size: 13
|
|
616
|
+
}), "Show all namespaces"]
|
|
617
|
+
})]
|
|
618
|
+
}),
|
|
431
619
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
432
620
|
className: "toolbar consumers-toolbar",
|
|
433
621
|
children: [
|
|
@@ -530,6 +718,7 @@ function DiagnosticsPage() {
|
|
|
530
718
|
/* @__PURE__ */ require_jsx_runtime.jsx("tbody", { children: (groupMode === "namespace" ? [...new Set(filtered.map((group) => group.namespace))] : [""]).map((namespace) => /* @__PURE__ */ require_jsx_runtime.jsx(NamespaceRows, {
|
|
531
719
|
namespace,
|
|
532
720
|
groups: namespace ? filtered.filter((group) => group.namespace === namespace) : filtered,
|
|
721
|
+
snapshotTime: data.time,
|
|
533
722
|
clusterId: data?.clusterId ?? "",
|
|
534
723
|
usage: projected?.usage ?? [],
|
|
535
724
|
open: !namespace || namespacesOpen.includes(namespace),
|
|
@@ -612,6 +801,14 @@ function DiagnosticsPage() {
|
|
|
612
801
|
className: "diagnostics-explainer",
|
|
613
802
|
children: [/* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "Timeline coverage and timing" }), /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Kubernetes events have limited retention. Observed changes begin with this open view, retain at most 300 entries, and have unknown transition times between snapshots. Related identities observed in this view are retained for at most 100 applications and 500 identities per application; older identity coverage can be incomplete." })]
|
|
614
803
|
}),
|
|
804
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
805
|
+
className: "warning-filter",
|
|
806
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Checkbox, {
|
|
807
|
+
label: "Group recurring warnings",
|
|
808
|
+
value: groupedTimeline,
|
|
809
|
+
onChange: setGroupedTimeline
|
|
810
|
+
})
|
|
811
|
+
}),
|
|
615
812
|
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
616
813
|
className: "timeline-legend",
|
|
617
814
|
"aria-label": "Event types",
|
|
@@ -630,7 +827,8 @@ function DiagnosticsPage() {
|
|
|
630
827
|
/* @__PURE__ */ require_jsx_runtime.jsx("ol", {
|
|
631
828
|
className: "diagnostics-timeline",
|
|
632
829
|
"aria-label": "Workload event timeline",
|
|
633
|
-
children:
|
|
830
|
+
children: displayedTimeline.map((entry) => {
|
|
831
|
+
const recurrence = groupedTimeline ? timelineGroups.find((group) => group.latest === entry) : void 0;
|
|
634
832
|
const kind = eventCategory(entry.reason);
|
|
635
833
|
const category = warningIds.has(entry.id) ? "warning" : kind === "event" && entry.certainty === "observed" ? "observed" : kind;
|
|
636
834
|
const label = category === "warning" ? "Warning" : category === "rollout" ? "Rollout" : category === "restart" ? "Restart" : category === "readiness" ? "Readiness" : entry.certainty === "observed" ? "Observed change" : "Kubernetes event";
|
|
@@ -649,41 +847,120 @@ function DiagnosticsPage() {
|
|
|
649
847
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
650
848
|
className: "timeline-content",
|
|
651
849
|
children: [
|
|
652
|
-
object && data && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
653
|
-
type: "button",
|
|
654
|
-
className: "timeline-resource-action",
|
|
655
|
-
"aria-label": "Open resource",
|
|
656
|
-
title: `Open ${object.kind ?? "Pod"} ${object.metadata.namespace} / ${object.metadata.name}`,
|
|
657
|
-
onClick: (event) => openResource(object, data.clusterId, event),
|
|
658
|
-
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" })
|
|
659
|
-
}),
|
|
660
850
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
661
|
-
className: "timeline-card-
|
|
662
|
-
children: [
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
851
|
+
className: "timeline-card-top",
|
|
852
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
853
|
+
className: "timeline-card-heading",
|
|
854
|
+
children: [
|
|
855
|
+
" ",
|
|
856
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
857
|
+
className: `timeline-type ${category}`,
|
|
858
|
+
children: label
|
|
859
|
+
}),
|
|
860
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("h4", { children: entry.reason }),
|
|
861
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("small", {
|
|
862
|
+
className: "timeline-resource-identity",
|
|
863
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
864
|
+
material: referenceKind === "Pod" ? "view_in_ar" : "account_tree",
|
|
865
|
+
size: 12
|
|
866
|
+
}), metadata?.name ? `${metadata.namespace ?? "Namespace unknown"} / ${referenceKind ?? "Resource"} / ${metadata.name}` : "Resource identity unavailable"]
|
|
867
|
+
})
|
|
868
|
+
]
|
|
869
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
870
|
+
className: "timeline-card-actions",
|
|
871
|
+
role: "group",
|
|
872
|
+
"aria-label": "Event actions",
|
|
873
|
+
children: [
|
|
874
|
+
object && data && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
875
|
+
type: "button",
|
|
876
|
+
className: "timeline-resource-action",
|
|
877
|
+
"aria-label": "Open resource",
|
|
878
|
+
title: `Open ${object.kind ?? "Pod"} ${object.metadata.namespace} / ${object.metadata.name}`,
|
|
879
|
+
onClick: (event) => openResource(object, data.clusterId, event),
|
|
880
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" })
|
|
881
|
+
}),
|
|
882
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
883
|
+
clusterId: data.clusterId,
|
|
884
|
+
incident: {
|
|
885
|
+
problem: entry.reason,
|
|
886
|
+
facts: [
|
|
887
|
+
entry.message,
|
|
888
|
+
`Reported count ${recurrence?.count ?? entry.count}`,
|
|
889
|
+
...(recurrence?.affected ?? []).length > 1 ? [`Affected resources: ${recurrence?.affected.join("; ")}`] : []
|
|
890
|
+
],
|
|
891
|
+
time: entry.time ?? data.time,
|
|
892
|
+
resources: (recurrence?.entries ?? [entry]).slice(0, 12).flatMap((original) => {
|
|
893
|
+
const originalObject = original.objectUid ? projected?.objects.get(original.objectUid) : void 0;
|
|
894
|
+
const originalReference = eventReferences.get(original.id);
|
|
895
|
+
const originalMetadata = originalObject?.metadata ?? originalReference;
|
|
896
|
+
const originalKind = originalObject?.kind ?? originalReference?.kind;
|
|
897
|
+
return originalMetadata?.name && originalKind ? [{
|
|
898
|
+
kind: originalKind,
|
|
899
|
+
namespace: originalMetadata.namespace,
|
|
900
|
+
name: originalMetadata.name
|
|
901
|
+
}] : [];
|
|
902
|
+
})
|
|
903
|
+
}
|
|
904
|
+
}),
|
|
905
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
906
|
+
type: "button",
|
|
907
|
+
className: "explain-finding",
|
|
908
|
+
"aria-label": "Explain with AI",
|
|
909
|
+
title: "Explain with AI",
|
|
910
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
911
|
+
clusterId: data.clusterId,
|
|
912
|
+
title: entry.reason,
|
|
913
|
+
summary: `${timelineResource(entry)}: ${entry.message}. Reported count ${recurrence?.count ?? entry.count}`,
|
|
914
|
+
resource: metadata?.name && referenceKind ? {
|
|
915
|
+
kind: referenceKind,
|
|
916
|
+
namespace: metadata.namespace,
|
|
917
|
+
name: metadata.name
|
|
918
|
+
} : void 0
|
|
919
|
+
}),
|
|
920
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
921
|
+
material: "auto_awesome",
|
|
922
|
+
size: 13
|
|
923
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
924
|
+
className: "timeline-action-label",
|
|
925
|
+
children: "Explain with AI"
|
|
926
|
+
})]
|
|
927
|
+
})
|
|
928
|
+
]
|
|
929
|
+
})]
|
|
677
930
|
}),
|
|
678
931
|
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: entry.message }),
|
|
679
932
|
/* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
|
|
680
933
|
entry.certainty === "reported" ? "Reported" : entry.certainty === "observed" ? "Observed" : "Unknown",
|
|
681
934
|
" ",
|
|
682
935
|
"· Count ",
|
|
683
|
-
entry.count,
|
|
936
|
+
recurrence?.count ?? entry.count,
|
|
684
937
|
entry.firstTime ? ` · First ${require_date_time.formatDateTime(entry.firstTime)}` : ""
|
|
685
938
|
] }),
|
|
686
|
-
entry.gap && /* @__PURE__ */ require_jsx_runtime.jsx("small", { children: entry.gap })
|
|
939
|
+
entry.gap && /* @__PURE__ */ require_jsx_runtime.jsx("small", { children: entry.gap }),
|
|
940
|
+
recurrence && recurrence.entries.length > 1 && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
|
|
941
|
+
open: openTimeline.includes(recurrence.key),
|
|
942
|
+
onToggle: (event) => {
|
|
943
|
+
const open = event.currentTarget.open;
|
|
944
|
+
setOpenTimeline((items) => open ? [.../* @__PURE__ */ new Set([...items, recurrence.key])] : items.filter((item) => item !== recurrence.key));
|
|
945
|
+
},
|
|
946
|
+
children: [
|
|
947
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("summary", { children: [
|
|
948
|
+
recurrence.affected.length,
|
|
949
|
+
" affected resources · ",
|
|
950
|
+
recurrence.entries.length,
|
|
951
|
+
" original entries"
|
|
952
|
+
] }),
|
|
953
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("small", { children: recurrence.affected.join(" · ") }),
|
|
954
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("ul", { children: recurrence.entries.map((original, index) => /* @__PURE__ */ require_jsx_runtime.jsxs("li", { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
|
|
955
|
+
original.time ? require_date_time.formatDateTime(original.time) : "Time unknown",
|
|
956
|
+
" ·",
|
|
957
|
+
" ",
|
|
958
|
+
timelineResource(original),
|
|
959
|
+
" · Count ",
|
|
960
|
+
original.count
|
|
961
|
+
] }), original.message] }, `${original.id}/${index}`)) })
|
|
962
|
+
]
|
|
963
|
+
})
|
|
687
964
|
]
|
|
688
965
|
})]
|
|
689
966
|
}, entry.id);
|
|
@@ -698,17 +975,17 @@ function DiagnosticsPage() {
|
|
|
698
975
|
})]
|
|
699
976
|
})
|
|
700
977
|
] }),
|
|
701
|
-
tab === "Pending" && data && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
|
|
702
|
-
|
|
978
|
+
(tab === "Pending" || readinessView) && data && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
|
|
979
|
+
triageLabel,
|
|
703
980
|
" ",
|
|
704
981
|
/* @__PURE__ */ require_jsx_runtime.jsx(DiagnosticsSelect, {
|
|
705
|
-
label:
|
|
706
|
-
value:
|
|
707
|
-
onChange: setPendingUid,
|
|
982
|
+
label: triageLabel,
|
|
983
|
+
value: triageUid,
|
|
984
|
+
onChange: readinessView ? setNotReadyUid : setPendingUid,
|
|
708
985
|
options: [{
|
|
709
986
|
value: "",
|
|
710
987
|
label: "Select a pod"
|
|
711
|
-
}, ...
|
|
988
|
+
}, ...triagePods.map((pod) => ({
|
|
712
989
|
value: pod.metadata.uid ?? "",
|
|
713
990
|
label: `${pod.metadata.namespace} / ${pod.metadata.name}`
|
|
714
991
|
}))]
|
|
@@ -725,7 +1002,7 @@ function DiagnosticsPage() {
|
|
|
725
1002
|
selectedPod.metadata.name
|
|
726
1003
|
] }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
727
1004
|
className: "pending-state-row",
|
|
728
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: diagnosis.state === "assigned-waiting" ? "Assigned and waiting" : "Unscheduled" }), consumerStatus([selectedPod]).tone === "error" && /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerStatus, { pods: [selectedPod] })]
|
|
1005
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: readinessView ? "Not ready" : diagnosis.state === "assigned-waiting" ? "Assigned and waiting" : "Unscheduled" }), consumerStatus([selectedPod]).tone === "error" && /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerStatus, { pods: [selectedPod] })]
|
|
729
1006
|
})] }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
730
1007
|
className: "pending-open-action",
|
|
731
1008
|
type: "button",
|
|
@@ -737,7 +1014,7 @@ function DiagnosticsPage() {
|
|
|
737
1014
|
}), "Open pod"]
|
|
738
1015
|
})]
|
|
739
1016
|
}),
|
|
740
|
-
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: diagnosis.state === "assigned-waiting" ? "A node is assigned. Check pod conditions and events for startup or storage issues." : "No node is assigned. Start with scheduler events, then inspect constraints." }),
|
|
1017
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: readinessView ? "The pod is running but needs attention. Check readiness conditions, container status and events." : diagnosis.state === "assigned-waiting" ? "A node is assigned. Check pod conditions and events for startup or storage issues." : "No node is assigned. Start with scheduler events, then inspect constraints." }),
|
|
741
1018
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
742
1019
|
className: "pending-request-badges",
|
|
743
1020
|
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
@@ -780,7 +1057,42 @@ function DiagnosticsPage() {
|
|
|
780
1057
|
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: selectedPod.status?.conditions?.map((condition) => `${condition.type}: ${condition.status}${condition.message ? ` (${condition.message})` : ""}`).join(" · ") })
|
|
781
1058
|
]
|
|
782
1059
|
}),
|
|
783
|
-
/* @__PURE__ */ require_jsx_runtime.jsx(
|
|
1060
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
1061
|
+
clusterId: data.clusterId,
|
|
1062
|
+
incident: {
|
|
1063
|
+
problem: triageLabel,
|
|
1064
|
+
facts: [
|
|
1065
|
+
readinessView ? consumerStatus([selectedPod]).label : diagnosis.state,
|
|
1066
|
+
`CPU request ${podRequest(selectedPod, "cpu")}; memory request ${podRequest(selectedPod, "memory")}`,
|
|
1067
|
+
...diagnosis.evidence.slice(0, 8).map((entry) => entry.message)
|
|
1068
|
+
],
|
|
1069
|
+
time: data.time,
|
|
1070
|
+
resources: [{
|
|
1071
|
+
kind: "Pod",
|
|
1072
|
+
namespace: selectedPod.metadata.namespace,
|
|
1073
|
+
name: selectedPod.metadata.name
|
|
1074
|
+
}]
|
|
1075
|
+
}
|
|
1076
|
+
}),
|
|
1077
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
1078
|
+
type: "button",
|
|
1079
|
+
className: "explain-finding",
|
|
1080
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
1081
|
+
clusterId: data.clusterId,
|
|
1082
|
+
title: triageLabel,
|
|
1083
|
+
summary: `${readinessView ? consumerStatus([selectedPod]).label : diagnosis.state}. CPU request ${podRequest(selectedPod, "cpu")}, memory request ${podRequest(selectedPod, "memory")}. ${diagnosis.evidence.slice(0, 8).map((entry) => entry.message).join("; ")}`,
|
|
1084
|
+
resource: {
|
|
1085
|
+
kind: "Pod",
|
|
1086
|
+
namespace: selectedPod.metadata.namespace,
|
|
1087
|
+
name: selectedPod.metadata.name
|
|
1088
|
+
}
|
|
1089
|
+
}),
|
|
1090
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1091
|
+
material: "auto_awesome",
|
|
1092
|
+
size: 13
|
|
1093
|
+
}), "Explain with AI"]
|
|
1094
|
+
}),
|
|
1095
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: readinessView ? "Readiness and retained evidence" : "Scheduling evidence" }),
|
|
784
1096
|
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
785
1097
|
className: "diagnostics-table",
|
|
786
1098
|
children: /* @__PURE__ */ require_jsx_runtime.jsxs("table", {
|
|
@@ -817,12 +1129,12 @@ function DiagnosticsPage() {
|
|
|
817
1129
|
})
|
|
818
1130
|
] }) : /* @__PURE__ */ require_jsx_runtime.jsx("p", {
|
|
819
1131
|
className: "diagnostics-empty",
|
|
820
|
-
children: !data.pods ?
|
|
1132
|
+
children: !data.pods ? `Pod inventory unavailable. Check cluster access before investigating ${readinessView ? "readiness" : "pending pods"}.` : triagePods.length ? `Select a ${readinessView ? "not ready" : "pending"} pod to see evidence and requests` : `No ${readinessView ? "not ready" : "pending"} pods in this snapshot`
|
|
821
1133
|
})] })
|
|
822
1134
|
]
|
|
823
1135
|
});
|
|
824
1136
|
}
|
|
825
|
-
function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
1137
|
+
function ConsumerRows({ group, clusterId, snapshotTime, expanded, toggle, usage, depth }) {
|
|
826
1138
|
return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("tr", {
|
|
827
1139
|
className: `consumer-workload-row consumer-depth-${depth} consumer-tone-${consumerStatus(group.pods).tone}`,
|
|
828
1140
|
"data-row-depth": depth,
|
|
@@ -833,7 +1145,7 @@ function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
|
833
1145
|
className: "consumer-identity-main",
|
|
834
1146
|
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
835
1147
|
className: "consumer-title-line",
|
|
836
|
-
children: [group.ownerResolved && resources[group.kind] && group.name !== "Unknown" ? /* @__PURE__ */ require_jsx_runtime.
|
|
1148
|
+
children: [group.ownerResolved && resources[group.kind] && group.name !== "Unknown" ? /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
837
1149
|
className: "consumer-resource-link",
|
|
838
1150
|
type: "button",
|
|
839
1151
|
title: `Open ${group.kind} ${group.namespace} / ${group.name}`,
|
|
@@ -845,10 +1157,10 @@ function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
|
845
1157
|
name: group.name
|
|
846
1158
|
}
|
|
847
1159
|
}, clusterId, event),
|
|
848
|
-
children:
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
})
|
|
1160
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1161
|
+
className: "consumer-link-name",
|
|
1162
|
+
children: group.name
|
|
1163
|
+
})
|
|
852
1164
|
}) : /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
853
1165
|
className: "consumer-identity-label",
|
|
854
1166
|
children: group.name
|
|
@@ -873,20 +1185,59 @@ function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
|
873
1185
|
group.ownerResolved ? "" : " · Owner chain incomplete"
|
|
874
1186
|
]
|
|
875
1187
|
})]
|
|
876
|
-
}), /* @__PURE__ */ require_jsx_runtime.
|
|
1188
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
877
1189
|
className: "consumer-row-actions",
|
|
878
|
-
children:
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1190
|
+
children: [
|
|
1191
|
+
["warning", "error"].includes(consumerStatus(group.pods).tone) && /* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
1192
|
+
clusterId,
|
|
1193
|
+
incident: {
|
|
1194
|
+
problem: `${group.kind} ${group.name}: ${consumerStatus(group.pods).label}`,
|
|
1195
|
+
facts: [
|
|
1196
|
+
`${group.pods.length} pods`,
|
|
1197
|
+
`CPU ${resourceText(group.cpu.usage, "cpu")} used / ${resourceText(group.cpu.requests, "cpu")} requested`,
|
|
1198
|
+
`Memory ${resourceText(group.memory.usage, "memory")} used / ${resourceText(group.memory.requests, "memory")} requested`
|
|
1199
|
+
],
|
|
1200
|
+
time: snapshotTime,
|
|
1201
|
+
resources: [{
|
|
1202
|
+
kind: group.kind,
|
|
1203
|
+
namespace: group.namespace,
|
|
1204
|
+
name: group.name
|
|
1205
|
+
}]
|
|
1206
|
+
}
|
|
1207
|
+
}),
|
|
1208
|
+
["warning", "error"].includes(consumerStatus(group.pods).tone) && /* @__PURE__ */ require_jsx_runtime.jsx(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
882
1209
|
type: "button",
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
"aria-
|
|
886
|
-
onClick:
|
|
887
|
-
|
|
1210
|
+
className: "explain-finding",
|
|
1211
|
+
title: "Explain with AI",
|
|
1212
|
+
"aria-label": `Explain finding for ${group.name}`,
|
|
1213
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
1214
|
+
clusterId,
|
|
1215
|
+
title: `${group.kind} ${group.name}`,
|
|
1216
|
+
summary: `${consumerStatus(group.pods).label}; ${group.pods.length} pods. CPU ${resourceText(group.cpu.usage, "cpu")} used / ${resourceText(group.cpu.requests, "cpu")} requested; memory ${resourceText(group.memory.usage, "memory")} used / ${resourceText(group.memory.requests, "memory")} requested`,
|
|
1217
|
+
resource: {
|
|
1218
|
+
kind: group.kind,
|
|
1219
|
+
namespace: group.namespace,
|
|
1220
|
+
name: group.name
|
|
1221
|
+
}
|
|
1222
|
+
}),
|
|
1223
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1224
|
+
material: "auto_awesome",
|
|
1225
|
+
size: 13
|
|
1226
|
+
})
|
|
1227
|
+
}),
|
|
1228
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1229
|
+
className: "consumer-expand-slot",
|
|
1230
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
1231
|
+
className: "consumer-expand",
|
|
1232
|
+
type: "button",
|
|
1233
|
+
title: expanded ? "Collapse pods" : "Expand pods",
|
|
1234
|
+
"aria-label": `${expanded ? "Collapse" : "Expand"} pods for ${group.name}`,
|
|
1235
|
+
"aria-expanded": expanded,
|
|
1236
|
+
onClick: toggle,
|
|
1237
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: expanded ? "expand_less" : "expand_more" })
|
|
1238
|
+
})
|
|
888
1239
|
})
|
|
889
|
-
|
|
1240
|
+
]
|
|
890
1241
|
})]
|
|
891
1242
|
}) }),
|
|
892
1243
|
/* @__PURE__ */ require_jsx_runtime.jsx("td", {
|
|
@@ -926,17 +1277,13 @@ function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
|
926
1277
|
type: "button",
|
|
927
1278
|
title: "Open pod details",
|
|
928
1279
|
onClick: (event) => openResource(pod, clusterId, event),
|
|
929
|
-
children: [
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
pod.metadata.name
|
|
935
|
-
|
|
936
|
-
material: "open_in_new",
|
|
937
|
-
size: 12
|
|
938
|
-
})
|
|
939
|
-
]
|
|
1280
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1281
|
+
material: "view_in_ar",
|
|
1282
|
+
size: 14
|
|
1283
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1284
|
+
className: "consumer-link-name",
|
|
1285
|
+
children: pod.metadata.name
|
|
1286
|
+
})]
|
|
940
1287
|
}), /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerStatus, { pods: [pod] })]
|
|
941
1288
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("small", {
|
|
942
1289
|
className: "consumer-subtitle",
|
|
@@ -980,7 +1327,7 @@ function podRequest(pod, metric) {
|
|
|
980
1327
|
return "Unavailable";
|
|
981
1328
|
}
|
|
982
1329
|
}
|
|
983
|
-
function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamespace, expanded, toggleGroup }) {
|
|
1330
|
+
function NamespaceRows({ namespace, groups, clusterId, snapshotTime, usage, open, toggleNamespace, expanded, toggleGroup }) {
|
|
984
1331
|
const sum = (metric) => groups.reduce((total, group) => {
|
|
985
1332
|
const next = group[metric];
|
|
986
1333
|
return {
|
|
@@ -1010,22 +1357,42 @@ function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamesp
|
|
|
1010
1357
|
className: "consumer-identity-main",
|
|
1011
1358
|
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
1012
1359
|
className: "consumer-title-line",
|
|
1013
|
-
children: [
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1360
|
+
children: [
|
|
1361
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
1362
|
+
type: "button",
|
|
1363
|
+
className: "consumer-resource-link namespace-identity",
|
|
1364
|
+
onClick: (event) => openResource({
|
|
1365
|
+
kind: "Namespace",
|
|
1366
|
+
metadata: { name: namespace }
|
|
1367
|
+
}, clusterId, event),
|
|
1368
|
+
title: "Open namespace",
|
|
1369
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1370
|
+
material: "folder_open",
|
|
1371
|
+
size: 17
|
|
1372
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1373
|
+
className: "consumer-link-name",
|
|
1374
|
+
children: namespace
|
|
1375
|
+
})]
|
|
1376
|
+
}),
|
|
1377
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(ConsumerStatus, { pods: groups.flatMap((group) => group.pods) }),
|
|
1378
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
|
|
1379
|
+
groups.filter((group) => consumerStatus(group.pods).tone === "healthy").length,
|
|
1380
|
+
" ready ·",
|
|
1381
|
+
" ",
|
|
1382
|
+
groups.filter((group) => ["warning", "error"].includes(consumerStatus(group.pods).tone)).length,
|
|
1383
|
+
" ",
|
|
1384
|
+
"need attention · ",
|
|
1385
|
+
groups.filter((group) => consumerStatus(group.pods).tone === "neutral").length,
|
|
1386
|
+
" ",
|
|
1387
|
+
"unknown"
|
|
1388
|
+
] })
|
|
1389
|
+
]
|
|
1023
1390
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("small", {
|
|
1024
1391
|
className: "consumer-subtitle",
|
|
1025
1392
|
title: "Resource totals include only consumers shown by the current filters",
|
|
1026
1393
|
children: [
|
|
1027
1394
|
groups.length,
|
|
1028
|
-
"
|
|
1395
|
+
" filtered consumers",
|
|
1029
1396
|
/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "·" }),
|
|
1030
1397
|
groups.reduce((total, group) => total + group.pods.length, 0),
|
|
1031
1398
|
" pods"
|
|
@@ -1069,6 +1436,7 @@ function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamesp
|
|
|
1069
1436
|
}), open && groups.map((group) => /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerRows, {
|
|
1070
1437
|
group,
|
|
1071
1438
|
depth: namespace ? 1 : 0,
|
|
1439
|
+
snapshotTime,
|
|
1072
1440
|
clusterId,
|
|
1073
1441
|
expanded: expanded.includes(groupKey(group)),
|
|
1074
1442
|
toggle: () => toggleGroup(groupKey(group)),
|
|
@@ -1082,6 +1450,10 @@ function podResourceGroup(pod, usage) {
|
|
|
1082
1450
|
metadata: {
|
|
1083
1451
|
...pod.metadata,
|
|
1084
1452
|
ownerReferences: []
|
|
1453
|
+
},
|
|
1454
|
+
status: {
|
|
1455
|
+
...pod.status,
|
|
1456
|
+
phase: void 0
|
|
1085
1457
|
}
|
|
1086
1458
|
}], [], usage)[0];
|
|
1087
1459
|
}
|