@64mb/freelens-power 1.2.2 → 1.3.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 +5 -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 +131 -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 +51 -0
- package/out/renderer/diagnostics-state.js.map +1 -0
- package/out/renderer/diagnostics.css.js +9 -0
- package/out/renderer/diagnostics.css.js.map +1 -1
- package/out/renderer/diagnostics.js +369 -73
- 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/preferences/monitoring-preferences.js +10 -0
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +88 -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 +556 -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 +5 -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,10 +3,15 @@ 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."],
|
|
@@ -86,6 +91,7 @@ function consumerStatus(pods) {
|
|
|
86
91
|
"InvalidImageName"
|
|
87
92
|
]);
|
|
88
93
|
for (const pod of pods) {
|
|
94
|
+
if (pod.status?.phase === "Succeeded") continue;
|
|
89
95
|
const status = pod.status;
|
|
90
96
|
const reason = [...status?.containerStatuses ?? [], ...status?.initContainerStatuses ?? []].map((status) => status.state?.waiting?.reason).find((value) => value && failureReasons.has(value));
|
|
91
97
|
if (pod.status?.phase === "Failed" || reason) return {
|
|
@@ -100,14 +106,18 @@ function consumerStatus(pods) {
|
|
|
100
106
|
label: `${pendingCount} Pending`,
|
|
101
107
|
icon: "hourglass_empty"
|
|
102
108
|
};
|
|
103
|
-
if (pods.some((pod) => pod.status?.conditions?.some((condition) => condition.type === "Ready" && condition.status === "False"))) return {
|
|
109
|
+
if (pods.some((pod) => pod.status?.phase !== "Succeeded" && pod.status?.conditions?.some((condition) => condition.type === "Ready" && condition.status === "False"))) return {
|
|
104
110
|
tone: "warning",
|
|
105
111
|
label: "Not ready",
|
|
106
112
|
icon: "warning_amber"
|
|
107
113
|
};
|
|
108
|
-
if (pods.length && pods.every((pod) => pod.status?.phase === "Running" && pod.status.conditions?.some((condition) => condition.type === "Ready" && condition.status === "True"))) return {
|
|
114
|
+
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") ? {
|
|
109
115
|
tone: "healthy",
|
|
110
|
-
label: "
|
|
116
|
+
label: "Completed",
|
|
117
|
+
icon: "check_circle"
|
|
118
|
+
} : {
|
|
119
|
+
tone: "healthy",
|
|
120
|
+
label: pods.some((pod) => pod.status?.phase === "Succeeded") ? "Ready / Completed" : "Ready",
|
|
111
121
|
icon: "check_circle_outline"
|
|
112
122
|
};
|
|
113
123
|
return {
|
|
@@ -209,20 +219,68 @@ function projection(data) {
|
|
|
209
219
|
return value;
|
|
210
220
|
}
|
|
211
221
|
function DiagnosticsPage() {
|
|
212
|
-
const
|
|
222
|
+
const initial = require__global_external_react.useRef(require_diagnostics_state.readDiagnosticsState(activeId() ?? "")).current;
|
|
223
|
+
const scrollContainer = require__global_external_react.useRef(null);
|
|
224
|
+
const stateCluster = require__global_external_react.useRef(activeId() ?? "");
|
|
225
|
+
const [tab, setTab] = require__global_external_react.useState(initial.tab);
|
|
213
226
|
const [snapshot, setSnapshot] = require__global_external_react.useState();
|
|
214
227
|
const [error, setError] = require__global_external_react.useState("");
|
|
215
|
-
const [filter, setFilter] = require__global_external_react.useState(
|
|
216
|
-
const [
|
|
217
|
-
const
|
|
218
|
-
|
|
228
|
+
const [filter, setFilter] = require__global_external_react.useState(initial.filter);
|
|
229
|
+
const [namespaceScope, setNamespaceScope] = require__global_external_react.useState(initial.namespaceScope);
|
|
230
|
+
const pendingScroll = require__global_external_react.useRef({
|
|
231
|
+
clusterId: stateCluster.current,
|
|
232
|
+
top: initial.scrollTop
|
|
233
|
+
});
|
|
234
|
+
const [needsAttention, setNeedsAttention] = require__global_external_react.useState(initial.needsAttention);
|
|
235
|
+
const [groupMode, setGroupMode] = require__global_external_react.useState(initial.groupMode);
|
|
236
|
+
const [namespacesOpen, setNamespacesOpen] = require__global_external_react.useState(initial.namespacesOpen);
|
|
219
237
|
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 [reason, setReason] = require__global_external_react.useState(
|
|
225
|
-
const [warningOnly, setWarningOnly] = require__global_external_react.useState(
|
|
238
|
+
const [sort, setSort] = require__global_external_react.useState(initial.sort);
|
|
239
|
+
const [expanded, setExpanded] = require__global_external_react.useState(initial.expanded);
|
|
240
|
+
const [selected, setSelected] = require__global_external_react.useState(initial.selected);
|
|
241
|
+
const [pendingUid, setPendingUid] = require__global_external_react.useState(initial.pendingUid);
|
|
242
|
+
const [reason, setReason] = require__global_external_react.useState(initial.reason);
|
|
243
|
+
const [warningOnly, setWarningOnly] = require__global_external_react.useState(initial.warningOnly);
|
|
244
|
+
const [groupedTimeline, setGroupedTimeline] = require__global_external_react.useState(initial.groupedTimeline);
|
|
245
|
+
const [openTimeline, setOpenTimeline] = require__global_external_react.useState(initial.openTimeline);
|
|
246
|
+
const viewState = require__global_external_react.useRef(initial);
|
|
247
|
+
viewState.current = {
|
|
248
|
+
tab,
|
|
249
|
+
filter,
|
|
250
|
+
namespaceScope,
|
|
251
|
+
needsAttention,
|
|
252
|
+
groupMode,
|
|
253
|
+
namespacesOpen,
|
|
254
|
+
sort,
|
|
255
|
+
expanded,
|
|
256
|
+
selected,
|
|
257
|
+
pendingUid,
|
|
258
|
+
reason,
|
|
259
|
+
warningOnly,
|
|
260
|
+
groupedTimeline,
|
|
261
|
+
openTimeline,
|
|
262
|
+
scrollTop: viewState.current.scrollTop
|
|
263
|
+
};
|
|
264
|
+
require__global_external_react.useEffect(() => {
|
|
265
|
+
return () => require_diagnostics_state.saveDiagnosticsState(stateCluster.current, viewState.current);
|
|
266
|
+
}, [initial]);
|
|
267
|
+
require__global_external_react.useEffect(() => {
|
|
268
|
+
const focusNamespace = (event) => {
|
|
269
|
+
const detail = event.detail;
|
|
270
|
+
if (!detail || detail.clusterId !== stateCluster.current || detail.clusterId !== activeId() || typeof detail.namespace !== "string" || !detail.namespace) return;
|
|
271
|
+
setTab("Consumers");
|
|
272
|
+
setNamespaceScope(detail.namespace);
|
|
273
|
+
setFilter("");
|
|
274
|
+
setNeedsAttention(false);
|
|
275
|
+
setGroupMode("namespace");
|
|
276
|
+
setNamespacesOpen([detail.namespace]);
|
|
277
|
+
viewState.current.scrollTop = 0;
|
|
278
|
+
pendingScroll.current = void 0;
|
|
279
|
+
if (scrollContainer.current) scrollContainer.current.scrollTop = 0;
|
|
280
|
+
};
|
|
281
|
+
window.addEventListener(require_diagnostics_state.DIAGNOSTICS_NAMESPACE_EVENT, focusNamespace);
|
|
282
|
+
return () => window.removeEventListener(require_diagnostics_state.DIAGNOSTICS_NAMESPACE_EVENT, focusNamespace);
|
|
283
|
+
}, []);
|
|
226
284
|
const [observed, setObserved] = require__global_external_react.useState([]);
|
|
227
285
|
const previous = require__global_external_react.useRef();
|
|
228
286
|
require__global_external_react.useEffect(() => {
|
|
@@ -236,9 +294,28 @@ function DiagnosticsPage() {
|
|
|
236
294
|
known.current.clear();
|
|
237
295
|
setSnapshot(void 0);
|
|
238
296
|
setObserved([]);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
297
|
+
require_diagnostics_state.saveDiagnosticsState(stateCluster.current, viewState.current);
|
|
298
|
+
stateCluster.current = activeId() ?? "";
|
|
299
|
+
const restored = require_diagnostics_state.readDiagnosticsState(stateCluster.current);
|
|
300
|
+
setTab(restored.tab);
|
|
301
|
+
setFilter(restored.filter);
|
|
302
|
+
setNamespaceScope(restored.namespaceScope);
|
|
303
|
+
setNeedsAttention(restored.needsAttention);
|
|
304
|
+
setGroupMode(restored.groupMode);
|
|
305
|
+
setNamespacesOpen(restored.namespacesOpen);
|
|
306
|
+
setSort(restored.sort);
|
|
307
|
+
setExpanded(restored.expanded);
|
|
308
|
+
setSelected(restored.selected);
|
|
309
|
+
setPendingUid(restored.pendingUid);
|
|
310
|
+
setReason(restored.reason);
|
|
311
|
+
setWarningOnly(restored.warningOnly);
|
|
312
|
+
setGroupedTimeline(restored.groupedTimeline);
|
|
313
|
+
setOpenTimeline(restored.openTimeline);
|
|
314
|
+
viewState.current = restored;
|
|
315
|
+
pendingScroll.current = {
|
|
316
|
+
clusterId: stateCluster.current,
|
|
317
|
+
top: restored.scrollTop
|
|
318
|
+
};
|
|
242
319
|
setError("");
|
|
243
320
|
};
|
|
244
321
|
const load = async () => {
|
|
@@ -301,20 +378,30 @@ function DiagnosticsPage() {
|
|
|
301
378
|
};
|
|
302
379
|
}, []);
|
|
303
380
|
const data = snapshot?.clusterId === activeId() ? snapshot : void 0;
|
|
381
|
+
require__global_external_react.useLayoutEffect(() => {
|
|
382
|
+
const pending = pendingScroll.current;
|
|
383
|
+
if (!data || !pending || pending.clusterId !== data.clusterId || !scrollContainer.current) return;
|
|
384
|
+
scrollContainer.current.scrollTop = pending.top;
|
|
385
|
+
const frame = requestAnimationFrame(() => {
|
|
386
|
+
if (pendingScroll.current !== pending || !scrollContainer.current) return;
|
|
387
|
+
scrollContainer.current.scrollTop = pending.top;
|
|
388
|
+
viewState.current.scrollTop = scrollContainer.current.scrollTop;
|
|
389
|
+
pendingScroll.current = void 0;
|
|
390
|
+
});
|
|
391
|
+
return () => cancelAnimationFrame(frame);
|
|
392
|
+
}, [data]);
|
|
304
393
|
const projected = require__global_external_react.useMemo(() => data ? projection(data) : void 0, [data]);
|
|
305
394
|
const groups = projected?.groups ?? [];
|
|
306
|
-
const filtered = require__global_external_react.useMemo(() => groups.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)), [
|
|
395
|
+
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
396
|
groups,
|
|
308
397
|
filter,
|
|
398
|
+
namespaceScope,
|
|
309
399
|
sort,
|
|
310
400
|
needsAttention
|
|
311
401
|
]);
|
|
312
402
|
const warningUids = new Set((data?.events ?? []).filter((event) => event.type === "Warning").map((event) => event.involvedObject?.uid ?? event.regarding?.uid).filter((uid) => !!uid));
|
|
313
403
|
const applications = [...known.current.values()].filter((item) => !warningOnly || [...item.uids].some((uid) => warningUids.has(uid))).map((item) => item.group);
|
|
314
404
|
const selectionAvailable = applications.some((group) => groupKey(group) === selected);
|
|
315
|
-
require__global_external_react.useEffect(() => {
|
|
316
|
-
if (selected && !selectionAvailable) setSelected("");
|
|
317
|
-
}, [selected, selectionAvailable]);
|
|
318
405
|
const application = applications.find((group) => groupKey(group) === selected);
|
|
319
406
|
const uids = new Set(known.current.get(selected)?.uids ?? []);
|
|
320
407
|
const normalizedEvents = require_resource_diagnostics.normalizeEvents(data?.events ?? []);
|
|
@@ -324,11 +411,26 @@ function DiagnosticsPage() {
|
|
|
324
411
|
return [entry.id, event?.involvedObject ?? event?.regarding];
|
|
325
412
|
}));
|
|
326
413
|
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));
|
|
414
|
+
const timelineResource = (entry) => {
|
|
415
|
+
const object = entry.objectUid ? projected?.objects.get(entry.objectUid) : void 0;
|
|
416
|
+
const ref = eventReferences.get(entry.id);
|
|
417
|
+
return `${object?.metadata.namespace ?? ref?.namespace ?? "Unknown namespace"} / ${object?.kind ?? ref?.kind ?? "Resource"} / ${object?.metadata.name ?? ref?.name ?? entry.objectUid ?? entry.id}`;
|
|
418
|
+
};
|
|
419
|
+
const timelineGroups = require_timeline_groups.groupTimelineEntries(timeline, (entry) => {
|
|
420
|
+
if (!warningIds.has(entry.id)) return entry.id;
|
|
421
|
+
const owner = [...known.current.entries()].find(([, item]) => !!entry.objectUid && item.uids.has(entry.objectUid));
|
|
422
|
+
return owner ? `${owner[1].group.namespace}/${owner[1].group.kind}/${owner[1].group.name}` : timelineResource(entry);
|
|
423
|
+
}, timelineResource);
|
|
424
|
+
const displayedTimeline = groupedTimeline ? timelineGroups.map((group) => group.latest) : timeline;
|
|
327
425
|
const pending = (data?.pods ?? []).filter((pod) => pod.status?.phase === "Pending");
|
|
328
426
|
const PendingCard = pending.length && data?.pods ? "button" : "div";
|
|
329
427
|
const selectedPod = pending.find((pod) => pod.metadata.uid === pendingUid);
|
|
330
428
|
const diagnosis = selectedPod ? require_resource_diagnostics.diagnosePendingPod(selectedPod, data?.nodes ?? [], data?.events ?? [], data?.pvcs) : void 0;
|
|
331
429
|
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
430
|
+
ref: scrollContainer,
|
|
431
|
+
onScroll: (event) => {
|
|
432
|
+
if (!pendingScroll.current) viewState.current.scrollTop = event.currentTarget.scrollTop;
|
|
433
|
+
},
|
|
332
434
|
className: "power-diagnostics",
|
|
333
435
|
style: {
|
|
334
436
|
padding: 24,
|
|
@@ -428,6 +530,22 @@ function DiagnosticsPage() {
|
|
|
428
530
|
children: "Loading resource diagnostics…"
|
|
429
531
|
}),
|
|
430
532
|
tab === "Consumers" && data && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
|
|
533
|
+
namespaceScope && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
534
|
+
className: "namespace-scope",
|
|
535
|
+
role: "status",
|
|
536
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
537
|
+
className: "namespace-scope-label",
|
|
538
|
+
children: ["Namespace: ", namespaceScope]
|
|
539
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
|
|
540
|
+
type: "button",
|
|
541
|
+
"aria-label": "Clear namespace scope",
|
|
542
|
+
onClick: () => setNamespaceScope(void 0),
|
|
543
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
544
|
+
material: "close",
|
|
545
|
+
size: 13
|
|
546
|
+
}), "Show all namespaces"]
|
|
547
|
+
})]
|
|
548
|
+
}),
|
|
431
549
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
432
550
|
className: "toolbar consumers-toolbar",
|
|
433
551
|
children: [
|
|
@@ -530,6 +648,7 @@ function DiagnosticsPage() {
|
|
|
530
648
|
/* @__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
649
|
namespace,
|
|
532
650
|
groups: namespace ? filtered.filter((group) => group.namespace === namespace) : filtered,
|
|
651
|
+
snapshotTime: data.time,
|
|
533
652
|
clusterId: data?.clusterId ?? "",
|
|
534
653
|
usage: projected?.usage ?? [],
|
|
535
654
|
open: !namespace || namespacesOpen.includes(namespace),
|
|
@@ -612,6 +731,14 @@ function DiagnosticsPage() {
|
|
|
612
731
|
className: "diagnostics-explainer",
|
|
613
732
|
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
733
|
}),
|
|
734
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
735
|
+
className: "warning-filter",
|
|
736
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Checkbox, {
|
|
737
|
+
label: "Group recurring warnings",
|
|
738
|
+
value: groupedTimeline,
|
|
739
|
+
onChange: setGroupedTimeline
|
|
740
|
+
})
|
|
741
|
+
}),
|
|
615
742
|
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
616
743
|
className: "timeline-legend",
|
|
617
744
|
"aria-label": "Event types",
|
|
@@ -630,7 +757,8 @@ function DiagnosticsPage() {
|
|
|
630
757
|
/* @__PURE__ */ require_jsx_runtime.jsx("ol", {
|
|
631
758
|
className: "diagnostics-timeline",
|
|
632
759
|
"aria-label": "Workload event timeline",
|
|
633
|
-
children:
|
|
760
|
+
children: displayedTimeline.map((entry) => {
|
|
761
|
+
const recurrence = groupedTimeline ? timelineGroups.find((group) => group.latest === entry) : void 0;
|
|
634
762
|
const kind = eventCategory(entry.reason);
|
|
635
763
|
const category = warningIds.has(entry.id) ? "warning" : kind === "event" && entry.certainty === "observed" ? "observed" : kind;
|
|
636
764
|
const label = category === "warning" ? "Warning" : category === "rollout" ? "Rollout" : category === "restart" ? "Restart" : category === "readiness" ? "Readiness" : entry.certainty === "observed" ? "Observed change" : "Kubernetes event";
|
|
@@ -649,41 +777,120 @@ function DiagnosticsPage() {
|
|
|
649
777
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
650
778
|
className: "timeline-content",
|
|
651
779
|
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
780
|
/* @__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
|
-
|
|
781
|
+
className: "timeline-card-top",
|
|
782
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
783
|
+
className: "timeline-card-heading",
|
|
784
|
+
children: [
|
|
785
|
+
" ",
|
|
786
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
787
|
+
className: `timeline-type ${category}`,
|
|
788
|
+
children: label
|
|
789
|
+
}),
|
|
790
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("h4", { children: entry.reason }),
|
|
791
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("small", {
|
|
792
|
+
className: "timeline-resource-identity",
|
|
793
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
794
|
+
material: referenceKind === "Pod" ? "view_in_ar" : "account_tree",
|
|
795
|
+
size: 12
|
|
796
|
+
}), metadata?.name ? `${metadata.namespace ?? "Namespace unknown"} / ${referenceKind ?? "Resource"} / ${metadata.name}` : "Resource identity unavailable"]
|
|
797
|
+
})
|
|
798
|
+
]
|
|
799
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
800
|
+
className: "timeline-card-actions",
|
|
801
|
+
role: "group",
|
|
802
|
+
"aria-label": "Event actions",
|
|
803
|
+
children: [
|
|
804
|
+
object && data && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
805
|
+
type: "button",
|
|
806
|
+
className: "timeline-resource-action",
|
|
807
|
+
"aria-label": "Open resource",
|
|
808
|
+
title: `Open ${object.kind ?? "Pod"} ${object.metadata.namespace} / ${object.metadata.name}`,
|
|
809
|
+
onClick: (event) => openResource(object, data.clusterId, event),
|
|
810
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" })
|
|
811
|
+
}),
|
|
812
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
813
|
+
clusterId: data.clusterId,
|
|
814
|
+
incident: {
|
|
815
|
+
problem: entry.reason,
|
|
816
|
+
facts: [
|
|
817
|
+
entry.message,
|
|
818
|
+
`Reported count ${recurrence?.count ?? entry.count}`,
|
|
819
|
+
...(recurrence?.affected ?? []).length > 1 ? [`Affected resources: ${recurrence?.affected.join("; ")}`] : []
|
|
820
|
+
],
|
|
821
|
+
time: entry.time ?? data.time,
|
|
822
|
+
resources: (recurrence?.entries ?? [entry]).slice(0, 12).flatMap((original) => {
|
|
823
|
+
const originalObject = original.objectUid ? projected?.objects.get(original.objectUid) : void 0;
|
|
824
|
+
const originalReference = eventReferences.get(original.id);
|
|
825
|
+
const originalMetadata = originalObject?.metadata ?? originalReference;
|
|
826
|
+
const originalKind = originalObject?.kind ?? originalReference?.kind;
|
|
827
|
+
return originalMetadata?.name && originalKind ? [{
|
|
828
|
+
kind: originalKind,
|
|
829
|
+
namespace: originalMetadata.namespace,
|
|
830
|
+
name: originalMetadata.name
|
|
831
|
+
}] : [];
|
|
832
|
+
})
|
|
833
|
+
}
|
|
834
|
+
}),
|
|
835
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
836
|
+
type: "button",
|
|
837
|
+
className: "explain-finding",
|
|
838
|
+
"aria-label": "Explain with AI",
|
|
839
|
+
title: "Explain with AI",
|
|
840
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
841
|
+
clusterId: data.clusterId,
|
|
842
|
+
title: entry.reason,
|
|
843
|
+
summary: `${timelineResource(entry)}: ${entry.message}. Reported count ${recurrence?.count ?? entry.count}`,
|
|
844
|
+
resource: metadata?.name && referenceKind ? {
|
|
845
|
+
kind: referenceKind,
|
|
846
|
+
namespace: metadata.namespace,
|
|
847
|
+
name: metadata.name
|
|
848
|
+
} : void 0
|
|
849
|
+
}),
|
|
850
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
851
|
+
material: "auto_awesome",
|
|
852
|
+
size: 13
|
|
853
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
854
|
+
className: "timeline-action-label",
|
|
855
|
+
children: "Explain with AI"
|
|
856
|
+
})]
|
|
857
|
+
})
|
|
858
|
+
]
|
|
859
|
+
})]
|
|
677
860
|
}),
|
|
678
861
|
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: entry.message }),
|
|
679
862
|
/* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
|
|
680
863
|
entry.certainty === "reported" ? "Reported" : entry.certainty === "observed" ? "Observed" : "Unknown",
|
|
681
864
|
" ",
|
|
682
865
|
"· Count ",
|
|
683
|
-
entry.count,
|
|
866
|
+
recurrence?.count ?? entry.count,
|
|
684
867
|
entry.firstTime ? ` · First ${require_date_time.formatDateTime(entry.firstTime)}` : ""
|
|
685
868
|
] }),
|
|
686
|
-
entry.gap && /* @__PURE__ */ require_jsx_runtime.jsx("small", { children: entry.gap })
|
|
869
|
+
entry.gap && /* @__PURE__ */ require_jsx_runtime.jsx("small", { children: entry.gap }),
|
|
870
|
+
recurrence && recurrence.entries.length > 1 && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
|
|
871
|
+
open: openTimeline.includes(recurrence.key),
|
|
872
|
+
onToggle: (event) => {
|
|
873
|
+
const open = event.currentTarget.open;
|
|
874
|
+
setOpenTimeline((items) => open ? [.../* @__PURE__ */ new Set([...items, recurrence.key])] : items.filter((item) => item !== recurrence.key));
|
|
875
|
+
},
|
|
876
|
+
children: [
|
|
877
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("summary", { children: [
|
|
878
|
+
recurrence.affected.length,
|
|
879
|
+
" affected resources · ",
|
|
880
|
+
recurrence.entries.length,
|
|
881
|
+
" original entries"
|
|
882
|
+
] }),
|
|
883
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("small", { children: recurrence.affected.join(" · ") }),
|
|
884
|
+
/* @__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: [
|
|
885
|
+
original.time ? require_date_time.formatDateTime(original.time) : "Time unknown",
|
|
886
|
+
" ·",
|
|
887
|
+
" ",
|
|
888
|
+
timelineResource(original),
|
|
889
|
+
" · Count ",
|
|
890
|
+
original.count
|
|
891
|
+
] }), original.message] }, `${original.id}/${index}`)) })
|
|
892
|
+
]
|
|
893
|
+
})
|
|
687
894
|
]
|
|
688
895
|
})]
|
|
689
896
|
}, entry.id);
|
|
@@ -780,6 +987,41 @@ function DiagnosticsPage() {
|
|
|
780
987
|
/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: selectedPod.status?.conditions?.map((condition) => `${condition.type}: ${condition.status}${condition.message ? ` (${condition.message})` : ""}`).join(" · ") })
|
|
781
988
|
]
|
|
782
989
|
}),
|
|
990
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
991
|
+
clusterId: data.clusterId,
|
|
992
|
+
incident: {
|
|
993
|
+
problem: "Pending pod",
|
|
994
|
+
facts: [
|
|
995
|
+
diagnosis.state,
|
|
996
|
+
`CPU request ${podRequest(selectedPod, "cpu")}; memory request ${podRequest(selectedPod, "memory")}`,
|
|
997
|
+
...diagnosis.evidence.slice(0, 8).map((entry) => entry.message)
|
|
998
|
+
],
|
|
999
|
+
time: data.time,
|
|
1000
|
+
resources: [{
|
|
1001
|
+
kind: "Pod",
|
|
1002
|
+
namespace: selectedPod.metadata.namespace,
|
|
1003
|
+
name: selectedPod.metadata.name
|
|
1004
|
+
}]
|
|
1005
|
+
}
|
|
1006
|
+
}),
|
|
1007
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
1008
|
+
type: "button",
|
|
1009
|
+
className: "explain-finding",
|
|
1010
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
1011
|
+
clusterId: data.clusterId,
|
|
1012
|
+
title: "Pending pod",
|
|
1013
|
+
summary: `${diagnosis.state}. CPU request ${podRequest(selectedPod, "cpu")}, memory request ${podRequest(selectedPod, "memory")}. ${diagnosis.evidence.slice(0, 8).map((entry) => entry.message).join("; ")}`,
|
|
1014
|
+
resource: {
|
|
1015
|
+
kind: "Pod",
|
|
1016
|
+
namespace: selectedPod.metadata.namespace,
|
|
1017
|
+
name: selectedPod.metadata.name
|
|
1018
|
+
}
|
|
1019
|
+
}),
|
|
1020
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1021
|
+
material: "auto_awesome",
|
|
1022
|
+
size: 13
|
|
1023
|
+
}), "Explain with AI"]
|
|
1024
|
+
}),
|
|
783
1025
|
/* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: "Scheduling evidence" }),
|
|
784
1026
|
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
785
1027
|
className: "diagnostics-table",
|
|
@@ -822,7 +1064,7 @@ function DiagnosticsPage() {
|
|
|
822
1064
|
]
|
|
823
1065
|
});
|
|
824
1066
|
}
|
|
825
|
-
function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
1067
|
+
function ConsumerRows({ group, clusterId, snapshotTime, expanded, toggle, usage, depth }) {
|
|
826
1068
|
return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("tr", {
|
|
827
1069
|
className: `consumer-workload-row consumer-depth-${depth} consumer-tone-${consumerStatus(group.pods).tone}`,
|
|
828
1070
|
"data-row-depth": depth,
|
|
@@ -873,20 +1115,59 @@ function ConsumerRows({ group, clusterId, expanded, toggle, usage, depth }) {
|
|
|
873
1115
|
group.ownerResolved ? "" : " · Owner chain incomplete"
|
|
874
1116
|
]
|
|
875
1117
|
})]
|
|
876
|
-
}), /* @__PURE__ */ require_jsx_runtime.
|
|
1118
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
877
1119
|
className: "consumer-row-actions",
|
|
878
|
-
children:
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1120
|
+
children: [
|
|
1121
|
+
["warning", "error"].includes(consumerStatus(group.pods).tone) && /* @__PURE__ */ require_jsx_runtime.jsx(require_incident_copy_button.IncidentCopyButton, {
|
|
1122
|
+
clusterId,
|
|
1123
|
+
incident: {
|
|
1124
|
+
problem: `${group.kind} ${group.name}: ${consumerStatus(group.pods).label}`,
|
|
1125
|
+
facts: [
|
|
1126
|
+
`${group.pods.length} pods`,
|
|
1127
|
+
`CPU ${resourceText(group.cpu.usage, "cpu")} used / ${resourceText(group.cpu.requests, "cpu")} requested`,
|
|
1128
|
+
`Memory ${resourceText(group.memory.usage, "memory")} used / ${resourceText(group.memory.requests, "memory")} requested`
|
|
1129
|
+
],
|
|
1130
|
+
time: snapshotTime,
|
|
1131
|
+
resources: [{
|
|
1132
|
+
kind: group.kind,
|
|
1133
|
+
namespace: group.namespace,
|
|
1134
|
+
name: group.name
|
|
1135
|
+
}]
|
|
1136
|
+
}
|
|
1137
|
+
}),
|
|
1138
|
+
["warning", "error"].includes(consumerStatus(group.pods).tone) && /* @__PURE__ */ require_jsx_runtime.jsx(require_diagnostic_explain_button.DiagnosticExplainButton, {
|
|
882
1139
|
type: "button",
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
"aria-
|
|
886
|
-
onClick:
|
|
887
|
-
|
|
1140
|
+
className: "explain-finding",
|
|
1141
|
+
title: "Explain with AI",
|
|
1142
|
+
"aria-label": `Explain finding for ${group.name}`,
|
|
1143
|
+
onClick: () => require_ai_diagnostic_handoff.explainDiagnosticFinding({
|
|
1144
|
+
clusterId,
|
|
1145
|
+
title: `${group.kind} ${group.name}`,
|
|
1146
|
+
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`,
|
|
1147
|
+
resource: {
|
|
1148
|
+
kind: group.kind,
|
|
1149
|
+
namespace: group.namespace,
|
|
1150
|
+
name: group.name
|
|
1151
|
+
}
|
|
1152
|
+
}),
|
|
1153
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1154
|
+
material: "auto_awesome",
|
|
1155
|
+
size: 13
|
|
1156
|
+
})
|
|
1157
|
+
}),
|
|
1158
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1159
|
+
className: "consumer-expand-slot",
|
|
1160
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
1161
|
+
className: "consumer-expand",
|
|
1162
|
+
type: "button",
|
|
1163
|
+
title: expanded ? "Collapse pods" : "Expand pods",
|
|
1164
|
+
"aria-label": `${expanded ? "Collapse" : "Expand"} pods for ${group.name}`,
|
|
1165
|
+
"aria-expanded": expanded,
|
|
1166
|
+
onClick: toggle,
|
|
1167
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: expanded ? "expand_less" : "expand_more" })
|
|
1168
|
+
})
|
|
888
1169
|
})
|
|
889
|
-
|
|
1170
|
+
]
|
|
890
1171
|
})]
|
|
891
1172
|
}) }),
|
|
892
1173
|
/* @__PURE__ */ require_jsx_runtime.jsx("td", {
|
|
@@ -980,7 +1261,7 @@ function podRequest(pod, metric) {
|
|
|
980
1261
|
return "Unavailable";
|
|
981
1262
|
}
|
|
982
1263
|
}
|
|
983
|
-
function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamespace, expanded, toggleGroup }) {
|
|
1264
|
+
function NamespaceRows({ namespace, groups, clusterId, snapshotTime, usage, open, toggleNamespace, expanded, toggleGroup }) {
|
|
984
1265
|
const sum = (metric) => groups.reduce((total, group) => {
|
|
985
1266
|
const next = group[metric];
|
|
986
1267
|
return {
|
|
@@ -1010,22 +1291,36 @@ function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamesp
|
|
|
1010
1291
|
className: "consumer-identity-main",
|
|
1011
1292
|
children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
1012
1293
|
className: "consumer-title-line",
|
|
1013
|
-
children: [
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1294
|
+
children: [
|
|
1295
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
|
|
1296
|
+
className: "namespace-identity",
|
|
1297
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, {
|
|
1298
|
+
material: "folder_open",
|
|
1299
|
+
size: 17
|
|
1300
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
1301
|
+
className: "consumer-identity-label",
|
|
1302
|
+
children: namespace
|
|
1303
|
+
})]
|
|
1304
|
+
}),
|
|
1305
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(ConsumerStatus, { pods: groups.flatMap((group) => group.pods) }),
|
|
1306
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
|
|
1307
|
+
groups.filter((group) => consumerStatus(group.pods).tone === "healthy").length,
|
|
1308
|
+
" ready ·",
|
|
1309
|
+
" ",
|
|
1310
|
+
groups.filter((group) => ["warning", "error"].includes(consumerStatus(group.pods).tone)).length,
|
|
1311
|
+
" ",
|
|
1312
|
+
"need attention · ",
|
|
1313
|
+
groups.filter((group) => consumerStatus(group.pods).tone === "neutral").length,
|
|
1314
|
+
" ",
|
|
1315
|
+
"unknown"
|
|
1316
|
+
] })
|
|
1317
|
+
]
|
|
1023
1318
|
}), /* @__PURE__ */ require_jsx_runtime.jsxs("small", {
|
|
1024
1319
|
className: "consumer-subtitle",
|
|
1025
1320
|
title: "Resource totals include only consumers shown by the current filters",
|
|
1026
1321
|
children: [
|
|
1027
1322
|
groups.length,
|
|
1028
|
-
"
|
|
1323
|
+
" filtered consumers",
|
|
1029
1324
|
/* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "·" }),
|
|
1030
1325
|
groups.reduce((total, group) => total + group.pods.length, 0),
|
|
1031
1326
|
" pods"
|
|
@@ -1069,6 +1364,7 @@ function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamesp
|
|
|
1069
1364
|
}), open && groups.map((group) => /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerRows, {
|
|
1070
1365
|
group,
|
|
1071
1366
|
depth: namespace ? 1 : 0,
|
|
1367
|
+
snapshotTime,
|
|
1072
1368
|
clusterId,
|
|
1073
1369
|
expanded: expanded.includes(groupKey(group)),
|
|
1074
1370
|
toggle: () => toggleGroup(groupKey(group)),
|