@64mb/freelens-power 1.0.2 → 1.1.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 +5 -1
- package/out/main/common/monitoring-history.js +52 -0
- package/out/main/common/monitoring-history.js.map +1 -0
- package/out/main/common/store/monitoring-preferences-store.js +25 -2
- package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/main/dashboard-views.js +132 -28
- package/out/main/dashboard-views.js.map +1 -1
- package/out/main/index.js +2 -0
- package/out/main/index.js.map +1 -1
- package/out/main/monitoring-history-database.js +103 -0
- package/out/main/monitoring-history-database.js.map +1 -0
- package/out/main/monitoring-history-worker.js +61 -0
- package/out/main/monitoring-history-worker.js.map +1 -0
- package/out/main/monitoring-history.js +100 -0
- package/out/main/monitoring-history.js.map +1 -0
- package/out/renderer/_virtual/_global-external_mobx.js +2 -1
- package/out/renderer/diagnostics.js +647 -0
- package/out/renderer/diagnostics.js.map +1 -0
- package/out/renderer/index.js +28 -1
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/monitoring-collector.js +202 -0
- package/out/renderer/monitoring-collector.js.map +1 -0
- package/out/renderer/monitoring-history-collection.js +184 -0
- package/out/renderer/monitoring-history-collection.js.map +1 -0
- package/out/renderer/monitoring-history-view.js +150 -0
- package/out/renderer/monitoring-history-view.js.map +1 -0
- package/out/renderer/monitoring-history.js +19 -0
- package/out/renderer/monitoring-history.js.map +1 -0
- package/out/renderer/monitoring.js +104 -130
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/node-monitoring.js +116 -0
- package/out/renderer/node-monitoring.js.map +1 -0
- package/out/renderer/pod-monitoring.js +107 -8
- package/out/renderer/pod-monitoring.js.map +1 -1
- package/out/renderer/preferences/monitoring-preferences.js +55 -10
- package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
- package/out/renderer/src/common/monitoring-history.js +15 -0
- package/out/renderer/src/common/monitoring-history.js.map +1 -0
- package/out/renderer/src/common/monitoring-math.js +98 -0
- package/out/renderer/src/common/monitoring-math.js.map +1 -0
- package/out/renderer/src/common/resource-diagnostics.js +227 -0
- package/out/renderer/src/common/resource-diagnostics.js.map +1 -0
- package/out/renderer/src/common/store/monitoring-preferences-store.js +25 -2
- package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
- package/out/renderer/welcome.js +3 -1
- package/out/renderer/welcome.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,96 +4,10 @@ const require_monitoring_preferences_store = require("./src/common/store/monitor
|
|
|
4
4
|
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
5
5
|
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
6
6
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
7
|
+
const require_monitoring_math = require("./src/common/monitoring-math.js");
|
|
8
|
+
const require_monitoring_collector = require("./monitoring-collector.js");
|
|
9
|
+
const require_monitoring_history_view = require("./monitoring-history-view.js");
|
|
7
10
|
//#region src/renderer/monitoring.tsx
|
|
8
|
-
function effectivePodResource(pod, metric, kind) {
|
|
9
|
-
const read = (resources) => {
|
|
10
|
-
const raw = resources?.[kind]?.[metric];
|
|
11
|
-
if (raw !== void 0) return parseQuantity(raw, metric === "memory");
|
|
12
|
-
if (kind === "requests" && resources?.limits?.[metric] !== void 0) return parseQuantity(resources.limits[metric], metric === "memory");
|
|
13
|
-
return kind === "requests" ? 0 : Infinity;
|
|
14
|
-
};
|
|
15
|
-
const spec = pod.spec;
|
|
16
|
-
if (!spec?.containers?.length) throw new Error("Missing pod containers");
|
|
17
|
-
let sidecars = 0;
|
|
18
|
-
let initPeak = 0;
|
|
19
|
-
for (const container of spec.initContainers ?? []) {
|
|
20
|
-
const value = read(container.resources);
|
|
21
|
-
if (container.restartPolicy === "Always") {
|
|
22
|
-
sidecars += value;
|
|
23
|
-
initPeak = Math.max(initPeak, sidecars);
|
|
24
|
-
} else initPeak = Math.max(initPeak, sidecars + value);
|
|
25
|
-
}
|
|
26
|
-
let effective = Math.max(initPeak, sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0));
|
|
27
|
-
if (spec.resources?.[kind]?.[metric] !== void 0) effective = read(spec.resources);
|
|
28
|
-
const overhead = spec.overhead?.[metric];
|
|
29
|
-
if (overhead !== void 0 && (kind === "requests" || effective > 0)) effective += parseQuantity(overhead, metric === "memory");
|
|
30
|
-
return effective;
|
|
31
|
-
}
|
|
32
|
-
function aggregateResources(nodes, pods) {
|
|
33
|
-
if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error("Missing node inventory");
|
|
34
|
-
const names = new Set(nodes.map((node) => node.metadata.name));
|
|
35
|
-
const scheduled = pods?.filter((pod) => pod.spec?.nodeName && names.has(pod.spec.nodeName) && pod.status?.phase !== "Succeeded" && pod.status?.phase !== "Failed");
|
|
36
|
-
const sum = (metric) => {
|
|
37
|
-
const capacity = nodes.reduce((total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? "", metric === "memory"), 0);
|
|
38
|
-
const allocatable = nodes.reduce((total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? "", metric === "memory"), 0);
|
|
39
|
-
const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, "requests"), 0);
|
|
40
|
-
const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, "limits"), 0);
|
|
41
|
-
return {
|
|
42
|
-
capacity,
|
|
43
|
-
allocatable,
|
|
44
|
-
requests,
|
|
45
|
-
limits: limits === Infinity ? void 0 : limits,
|
|
46
|
-
unbounded: limits === Infinity
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
return {
|
|
50
|
-
cpu: sum("cpu"),
|
|
51
|
-
memory: sum("memory")
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
var INTERVAL = 3e4;
|
|
55
|
-
function parseQuantity(raw, memory = false) {
|
|
56
|
-
const match = /^(\d+(?:\.\d*)?|\.\d+)([eE][+-]?\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);
|
|
57
|
-
if (!match) throw new Error("Invalid metrics quantity");
|
|
58
|
-
const suffix = match[2] ?? "";
|
|
59
|
-
const value = Number(match[1]) * (suffix.endsWith("i") ? 1024 ** ([
|
|
60
|
-
"Ki",
|
|
61
|
-
"Mi",
|
|
62
|
-
"Gi",
|
|
63
|
-
"Ti",
|
|
64
|
-
"Pi",
|
|
65
|
-
"Ei"
|
|
66
|
-
].indexOf(suffix) + 1) : /^[eE][+-]?\d+$/.test(suffix) ? 10 ** Number(suffix.slice(1)) : {
|
|
67
|
-
n: 1e-9,
|
|
68
|
-
u: 1e-6,
|
|
69
|
-
m: .001,
|
|
70
|
-
k: 1e3,
|
|
71
|
-
K: 1e3,
|
|
72
|
-
M: 1e6,
|
|
73
|
-
G: 1e9,
|
|
74
|
-
T: 0xe8d4a51000,
|
|
75
|
-
P: 0x38d7ea4c68000,
|
|
76
|
-
E: 0xde0b6b3a7640000
|
|
77
|
-
}[suffix] ?? 1);
|
|
78
|
-
if (!Number.isFinite(value) || !memory && suffix.endsWith("i")) throw new Error("Invalid metrics quantity");
|
|
79
|
-
return value;
|
|
80
|
-
}
|
|
81
|
-
function aggregateMetrics(nodes, now = Date.now()) {
|
|
82
|
-
if (!nodes.length) throw new Error("No node metrics available");
|
|
83
|
-
let cpu = 0;
|
|
84
|
-
let memory = 0;
|
|
85
|
-
for (const node of nodes) {
|
|
86
|
-
const timestamp = Date.parse(node.timestamp);
|
|
87
|
-
if (!Number.isFinite(timestamp) || now - timestamp > 12e4 || timestamp > now + 3e4) throw new Error("Node metrics are stale");
|
|
88
|
-
cpu += parseQuantity(node.usage.cpu);
|
|
89
|
-
memory += parseQuantity(node.usage.memory, true);
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
time: now,
|
|
93
|
-
cpu,
|
|
94
|
-
memory
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
11
|
function formatMonitoringTime(time) {
|
|
98
12
|
return new Date(time).toLocaleTimeString([], {
|
|
99
13
|
hour: "2-digit",
|
|
@@ -197,10 +111,11 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
197
111
|
}[key],
|
|
198
112
|
value: (sample) => sample.resources?.[metric][key]
|
|
199
113
|
}))];
|
|
200
|
-
const
|
|
114
|
+
const visibleSeries = scope === "pod" ? series.filter((line) => line.key === "usage" || line.key === "requests") : series;
|
|
115
|
+
const max = Math.max(...samples.flatMap((sample) => [...visibleSeries.map((line) => line.value(sample) ?? 0), sample.historical?.[metric]?.max ?? 0]), .001) * 1.1;
|
|
201
116
|
const end = last?.time ?? Date.now();
|
|
202
117
|
const start = samples[0]?.time ?? end;
|
|
203
|
-
const duration = Math.max(end - start,
|
|
118
|
+
const duration = Math.max(end - start, require_monitoring_math.MONITORING_INTERVAL);
|
|
204
119
|
const x = (sample) => 88 + (sample.time - start) / duration * plotWidth;
|
|
205
120
|
const y = (sample) => 208 - sample[metric] / max * 184;
|
|
206
121
|
const selectNearest = (event) => {
|
|
@@ -297,12 +212,26 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
297
212
|
fontSize: "11",
|
|
298
213
|
children: format(max * fraction)
|
|
299
214
|
})] }, fraction)),
|
|
300
|
-
|
|
215
|
+
samples.map((sample) => {
|
|
216
|
+
const summary = sample.historical?.[metric];
|
|
217
|
+
if (!summary || summary.min === summary.max) return null;
|
|
218
|
+
return /* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
219
|
+
"data-monitoring-extrema": "",
|
|
220
|
+
x1: x(sample),
|
|
221
|
+
x2: x(sample),
|
|
222
|
+
y1: 208 - summary.max / max * 184,
|
|
223
|
+
y2: 208 - summary.min / max * 184,
|
|
224
|
+
stroke: "#c93dce",
|
|
225
|
+
strokeOpacity: .35,
|
|
226
|
+
strokeWidth: 4
|
|
227
|
+
}, `range:${sample.time}`);
|
|
228
|
+
}),
|
|
229
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsx("path", {
|
|
301
230
|
"data-monitoring-series": line.key,
|
|
302
231
|
d: samples.reduce((path, sample, index) => {
|
|
303
232
|
const value = line.value(sample);
|
|
304
233
|
if (value === void 0) return path;
|
|
305
|
-
return `${path} ${(index ? line.value(samples[index - 1]) : void 0) === void 0 ? "M" : "L"}${x(sample)},${208 - value / max * 184}`;
|
|
234
|
+
return `${path} ${(index ? line.value(samples[index - 1]) : void 0) === void 0 || sample.breakBefore || sample.time - samples[index - 1].time > 1.8 * Math.max(sample.historical?.[metric]?.resolution ?? 3e4, samples[index - 1].historical?.[metric]?.resolution ?? 3e4) ? "M" : "L"}${x(sample)},${208 - value / max * 184}`;
|
|
306
235
|
}, ""),
|
|
307
236
|
fill: "none",
|
|
308
237
|
stroke: line.color,
|
|
@@ -362,7 +291,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
362
291
|
},
|
|
363
292
|
children: [
|
|
364
293
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [formatMonitoringTime(selected.time), pinned ? " · Pinned" : ""] }),
|
|
365
|
-
|
|
294
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
366
295
|
style: { color: line.color },
|
|
367
296
|
children: [
|
|
368
297
|
line.label,
|
|
@@ -370,6 +299,18 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
370
299
|
line.value(selected) === void 0 ? "Unavailable" : format(line.value(selected))
|
|
371
300
|
]
|
|
372
301
|
}, line.key)),
|
|
302
|
+
selected.historical?.[metric] && /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [
|
|
303
|
+
"Min: ",
|
|
304
|
+
format(selected.historical[metric].min),
|
|
305
|
+
" · Max: ",
|
|
306
|
+
format(selected.historical[metric].max),
|
|
307
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("br", {}),
|
|
308
|
+
selected.historical[metric].count,
|
|
309
|
+
" samples ·",
|
|
310
|
+
" ",
|
|
311
|
+
Math.round(selected.historical[metric].resolution / 1e3),
|
|
312
|
+
"s bucket"
|
|
313
|
+
] }),
|
|
373
314
|
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
374
315
|
style: {
|
|
375
316
|
color: "var(--textColorSecondary)",
|
|
@@ -388,7 +329,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
388
329
|
gap: "6px 16px",
|
|
389
330
|
marginBottom: 8
|
|
390
331
|
},
|
|
391
|
-
children:
|
|
332
|
+
children: visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
392
333
|
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
393
334
|
style: { color: line.color },
|
|
394
335
|
children: "●"
|
|
@@ -405,7 +346,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
405
346
|
},
|
|
406
347
|
children: last.resourceStatus
|
|
407
348
|
}),
|
|
408
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
349
|
+
(scope === "node" || status || !samples.length) && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
409
350
|
style: {
|
|
410
351
|
fontSize: 11,
|
|
411
352
|
color: "var(--textColorSecondary)",
|
|
@@ -415,7 +356,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
415
356
|
},
|
|
416
357
|
role: "status",
|
|
417
358
|
"aria-label": `${scope} metrics status`,
|
|
418
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
359
|
+
children: [scope === "node" && /* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
419
360
|
"aria-hidden": "true",
|
|
420
361
|
style: { fontSize: 12 },
|
|
421
362
|
children: "*"
|
|
@@ -616,9 +557,16 @@ function ResourceDonut({ metric, sample, status }) {
|
|
|
616
557
|
});
|
|
617
558
|
}
|
|
618
559
|
function MonitoringCharts({ samples, status }) {
|
|
560
|
+
const clusterId = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
|
|
561
|
+
const history = require_monitoring_history_view.useMonitoringHistory(clusterId ? {
|
|
562
|
+
clusterId,
|
|
563
|
+
scope: "cluster",
|
|
564
|
+
uid: clusterId
|
|
565
|
+
} : void 0, samples);
|
|
619
566
|
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
620
567
|
style: { containerType: "inline-size" },
|
|
621
|
-
children: [
|
|
568
|
+
children: [
|
|
569
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
622
570
|
[data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }
|
|
623
571
|
[data-monitoring-trends], [data-monitoring-summaries] { display:contents }
|
|
624
572
|
@container (min-width: 961px) {
|
|
@@ -632,27 +580,45 @@ function MonitoringCharts({ samples, status }) {
|
|
|
632
580
|
[data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }
|
|
633
581
|
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }
|
|
634
582
|
}
|
|
635
|
-
` }),
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
583
|
+
` }),
|
|
584
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
585
|
+
style: {
|
|
586
|
+
display: "flex",
|
|
587
|
+
alignItems: "center",
|
|
588
|
+
gap: 12,
|
|
589
|
+
marginBottom: 8
|
|
590
|
+
},
|
|
591
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring_history_view.MonitoringHistoryRange, {
|
|
592
|
+
range: history.range,
|
|
593
|
+
onChange: history.setRange
|
|
594
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("small", {
|
|
595
|
+
role: "status",
|
|
596
|
+
children: history.status
|
|
597
|
+
})]
|
|
598
|
+
}),
|
|
599
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
600
|
+
"data-monitoring-layout": "",
|
|
601
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
602
|
+
"data-monitoring-trends": "",
|
|
603
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(MetricChart, {
|
|
604
|
+
metric,
|
|
605
|
+
samples: history.samples,
|
|
606
|
+
status
|
|
607
|
+
}, metric))
|
|
608
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
609
|
+
"data-monitoring-summaries": "",
|
|
610
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(ResourceDonut, {
|
|
611
|
+
metric,
|
|
612
|
+
sample: samples.at(-1),
|
|
613
|
+
status
|
|
614
|
+
}, metric))
|
|
615
|
+
})]
|
|
616
|
+
})
|
|
617
|
+
]
|
|
653
618
|
});
|
|
654
619
|
}
|
|
655
620
|
function startMonitoring() {
|
|
621
|
+
const release = require_monitoring_collector.retainMonitoringCollector();
|
|
656
622
|
const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
|
|
657
623
|
let stopCurrent;
|
|
658
624
|
const stop = require__global_external_mobx.reaction(() => preferences.enabled, (enabled) => {
|
|
@@ -662,6 +628,7 @@ function startMonitoring() {
|
|
|
662
628
|
return () => {
|
|
663
629
|
stop();
|
|
664
630
|
stopCurrent?.();
|
|
631
|
+
release();
|
|
665
632
|
};
|
|
666
633
|
}
|
|
667
634
|
function mountMonitoring() {
|
|
@@ -712,7 +679,7 @@ function mountMonitoring() {
|
|
|
712
679
|
cluster = currentCluster;
|
|
713
680
|
overview = currentOverview;
|
|
714
681
|
}
|
|
715
|
-
const cutoff = Date.now() - 60 *
|
|
682
|
+
const cutoff = Date.now() - 60 * require_monitoring_math.MONITORING_INTERVAL;
|
|
716
683
|
if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);
|
|
717
684
|
if (!overview || !cluster || document.visibilityState === "hidden") return;
|
|
718
685
|
if (!host) {
|
|
@@ -760,21 +727,30 @@ function mountMonitoring() {
|
|
|
760
727
|
render();
|
|
761
728
|
if (pendingGeneration === generation || Date.now() < nextSample) return;
|
|
762
729
|
pendingGeneration = generation;
|
|
763
|
-
nextSample = Date.now() +
|
|
730
|
+
nextSample = Date.now() + require_monitoring_math.MONITORING_INTERVAL;
|
|
764
731
|
const requestGeneration = generation;
|
|
765
732
|
const isCurrent = () => !stopped && requestGeneration === generation && document.visibilityState !== "hidden" && overview?.isConnected === true && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;
|
|
766
733
|
try {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
734
|
+
const collected = await require_monitoring_collector.getMonitoringSnapshot();
|
|
735
|
+
const metrics = collected.nodeMetrics === void 0 ? { status: "rejected" } : {
|
|
736
|
+
status: "fulfilled",
|
|
737
|
+
value: collected.nodeMetrics
|
|
738
|
+
};
|
|
739
|
+
const inventory = collected.nodes === void 0 ? { status: "rejected" } : {
|
|
740
|
+
status: "fulfilled",
|
|
741
|
+
value: collected.nodes
|
|
742
|
+
};
|
|
743
|
+
const pods = collected.pods === void 0 ? { status: "rejected" } : {
|
|
744
|
+
status: "fulfilled",
|
|
745
|
+
value: collected.pods
|
|
746
|
+
};
|
|
772
747
|
if (!isCurrent()) return;
|
|
773
748
|
if (metrics.status !== "fulfilled") throw new Error("Metrics unavailable");
|
|
774
|
-
const sample = aggregateMetrics(metrics.value ?? []);
|
|
749
|
+
const sample = require_monitoring_math.aggregateMetrics(metrics.value ?? []);
|
|
750
|
+
sample.time = Math.min(...metrics.value.map((item) => Date.parse(item.timestamp)));
|
|
775
751
|
const warnings = [];
|
|
776
752
|
if (inventory.status === "fulfilled") try {
|
|
777
|
-
sample.resources = aggregateResources(inventory.value ?? [], pods.status === "fulfilled" ? pods.value ?? void 0 : void 0);
|
|
753
|
+
sample.resources = require_monitoring_math.aggregateResources(inventory.value ?? [], pods.status === "fulfilled" ? pods.value ?? void 0 : void 0);
|
|
778
754
|
const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));
|
|
779
755
|
const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));
|
|
780
756
|
sample.completeUsage = expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));
|
|
@@ -785,7 +761,7 @@ function mountMonitoring() {
|
|
|
785
761
|
else warnings.push("Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable");
|
|
786
762
|
if (pods.status !== "fulfilled") warnings.push("Cluster-wide Pods unavailable or access denied; requests unavailable");
|
|
787
763
|
sample.resourceStatus = warnings.join(". ");
|
|
788
|
-
samples = [...samples, sample].slice(-60);
|
|
764
|
+
samples = [...samples.filter((entry) => entry.time !== sample.time), sample].sort((a, b) => a.time - b.time).slice(-60);
|
|
789
765
|
status = "";
|
|
790
766
|
} catch {
|
|
791
767
|
if (!isCurrent()) return;
|
|
@@ -818,11 +794,9 @@ function mountMonitoring() {
|
|
|
818
794
|
}
|
|
819
795
|
//#endregion
|
|
820
796
|
exports.MetricChart = MetricChart;
|
|
821
|
-
exports.
|
|
822
|
-
exports.aggregateResources = aggregateResources;
|
|
823
|
-
exports.effectivePodResource = effectivePodResource;
|
|
797
|
+
exports.effectivePodResource = require_monitoring_math.effectivePodResource;
|
|
824
798
|
exports.formatMonitoringTime = formatMonitoringTime;
|
|
825
|
-
exports.parseQuantity = parseQuantity;
|
|
799
|
+
exports.parseQuantity = require_monitoring_math.parseQuantity;
|
|
826
800
|
exports.startMonitoring = startMonitoring;
|
|
827
801
|
|
|
828
802
|
//# sourceMappingURL=monitoring.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\n\ntype ResourceValues = { requests?: Partial<Record<string, string>>; limits?: Partial<Record<string, string>> };\ntype ResourceContainer = { resources?: ResourceValues; restartPolicy?: string };\nexport type MonitoringPod = {\n spec?: {\n nodeName?: string;\n containers?: ResourceContainer[];\n initContainers?: ResourceContainer[];\n overhead?: Partial<Record<string, string>>;\n resources?: ResourceValues;\n };\n status?: { phase?: string };\n};\nexport type MonitoringNode = {\n metadata: { name: string };\n status?: { capacity?: Partial<Record<string, string>>; allocatable?: Partial<Record<string, string>> };\n};\ntype ResourceTotals = {\n capacity?: number;\n allocatable?: number;\n requests?: number;\n limits?: number;\n unbounded?: boolean;\n};\nexport type MonitoringSample = {\n time: number;\n cpu: number;\n memory: number;\n resources?: Record<\"cpu\" | \"memory\", ResourceTotals>;\n resourceStatus?: string;\n completeUsage?: boolean;\n};\n\nexport function effectivePodResource(\n pod: MonitoringPod,\n metric: \"cpu\" | \"memory\",\n kind: \"requests\" | \"limits\",\n): number {\n const read = (resources?: ResourceValues) => {\n const raw = resources?.[kind]?.[metric];\n if (raw !== undefined) return parseQuantity(raw, metric === \"memory\");\n if (kind === \"requests\" && resources?.limits?.[metric] !== undefined)\n return parseQuantity(resources.limits[metric], metric === \"memory\");\n return kind === \"requests\" ? 0 : Infinity;\n };\n const spec = pod.spec;\n if (!spec?.containers?.length) throw new Error(\"Missing pod containers\");\n let sidecars = 0;\n let initPeak = 0;\n for (const container of spec.initContainers ?? []) {\n const value = read(container.resources);\n if (container.restartPolicy === \"Always\") {\n sidecars += value;\n initPeak = Math.max(initPeak, sidecars);\n } else initPeak = Math.max(initPeak, sidecars + value);\n }\n let effective = Math.max(\n initPeak,\n sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0),\n );\n if (spec.resources?.[kind]?.[metric] !== undefined) effective = read(spec.resources);\n const overhead = spec.overhead?.[metric];\n if (overhead !== undefined && (kind === \"requests\" || effective > 0))\n effective += parseQuantity(overhead, metric === \"memory\");\n return effective;\n}\n\nexport function aggregateResources(\n nodes: MonitoringNode[],\n pods?: MonitoringPod[],\n): Record<\"cpu\" | \"memory\", ResourceTotals> {\n if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error(\"Missing node inventory\");\n const names = new Set(nodes.map((node) => node.metadata.name));\n const scheduled = pods?.filter(\n (pod) =>\n pod.spec?.nodeName &&\n names.has(pod.spec.nodeName) &&\n pod.status?.phase !== \"Succeeded\" &&\n pod.status?.phase !== \"Failed\",\n );\n const sum = (metric: \"cpu\" | \"memory\"): ResourceTotals => {\n const capacity = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const allocatable = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"requests\"), 0);\n const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"limits\"), 0);\n return {\n capacity,\n allocatable,\n requests,\n limits: limits === Infinity ? undefined : limits,\n unbounded: limits === Infinity,\n };\n };\n return { cpu: sum(\"cpu\"), memory: sum(\"memory\") };\n}\nconst INTERVAL = 30_000;\nexport const MAX_SAMPLES = 60;\n\nexport function parseQuantity(raw: string, memory = false): number {\n const match = /^(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);\n if (!match) throw new Error(\"Invalid metrics quantity\");\n const suffix = match[2] ?? \"\";\n const factors: Record<string, number> = {\n n: 1e-9,\n u: 1e-6,\n m: 1e-3,\n k: 1e3,\n K: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n E: 1e18,\n };\n const value =\n Number(match[1]) *\n (suffix.endsWith(\"i\")\n ? 1024 ** ([\"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\"].indexOf(suffix) + 1)\n : /^[eE][+-]?\\d+$/.test(suffix)\n ? 10 ** Number(suffix.slice(1))\n : (factors[suffix] ?? 1));\n if (!Number.isFinite(value) || (!memory && suffix.endsWith(\"i\"))) throw new Error(\"Invalid metrics quantity\");\n return value;\n}\n\nexport function aggregateMetrics(\n nodes: { usage: { cpu: string; memory: string }; timestamp: string }[],\n now = Date.now(),\n): MonitoringSample {\n if (!nodes.length) throw new Error(\"No node metrics available\");\n let cpu = 0;\n let memory = 0;\n for (const node of nodes) {\n const timestamp = Date.parse(node.timestamp);\n if (!Number.isFinite(timestamp) || now - timestamp > 120_000 || timestamp > now + 30_000)\n throw new Error(\"Node metrics are stale\");\n cpu += parseQuantity(node.usage.cpu);\n memory += parseQuantity(node.usage.memory, true);\n }\n return { time: now, cpu, memory };\n}\n\nexport function formatMonitoringTime(time: number): string {\n return new Date(time).toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n}\n\nfunction ResourceTitle({ metric, label }: { metric: \"cpu\" | \"memory\"; label?: string }) {\n return (\n <strong style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n <span className=\"Icon material\" aria-hidden=\"true\" style={{ \"--size\": \"18px\", fontWeight: 400 } as CSSProperties}>\n <span className=\"icon\">{metric === \"cpu\" ? \"developer_board\" : \"memory\"}</span>\n </span>\n {label ?? (metric === \"cpu\" ? \"CPU\" : \"Memory\")}\n </strong>\n );\n}\n\nexport function MetricChart({\n metric,\n samples,\n status,\n scope = \"node\",\n}: {\n metric: \"cpu\" | \"memory\";\n samples: MonitoringSample[];\n status: string;\n scope?: \"node\" | \"pod\";\n}) {\n const [selectedTime, setSelectedTime] = useState<number>();\n const [pinned, setPinned] = useState(false);\n const previousMetric = useRef(metric);\n useEffect(() => {\n if (\n previousMetric.current !== metric ||\n (selectedTime !== undefined && !samples.some((sample) => sample.time === selectedTime))\n ) {\n setPinned(false);\n setSelectedTime(undefined);\n }\n previousMetric.current = metric;\n }, [metric, samples, selectedTime]);\n useEffect(() => {\n if (!pinned) return;\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setPinned(false);\n setSelectedTime(undefined);\n }\n };\n document.addEventListener(\"keydown\", escape, true);\n return () => document.removeEventListener(\"keydown\", escape, true);\n }, [pinned]);\n const chartRef = useRef<SVGSVGElement>(null);\n const [width, setWidth] = useState(600);\n useLayoutEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n const measure = () => {\n const measured = chart.getBoundingClientRect().width;\n if (measured > 0) setWidth(measured);\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(chart);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, []);\n const plotWidth = Math.max(1, width - 112);\n const last = samples.at(-1);\n const selected = samples.find((sample) => sample.time === selectedTime);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const series = [\n { key: \"usage\", label: \"Usage\", color: \"#c93dce\", value: (sample: MonitoringSample) => sample[metric] },\n ...([\"requests\", \"allocatable\", \"capacity\"] as const).map((key) => ({\n key,\n label: { requests: \"Requests\", allocatable: \"Allocatable Capacity\", capacity: \"Capacity\" }[key],\n color: { requests: \"#4caf50\", allocatable: \"#5486b5\", capacity: \"var(--textColorSecondary)\" }[key],\n value: (sample: MonitoringSample) => sample.resources?.[metric][key],\n })),\n ];\n const max = Math.max(...samples.flatMap((sample) => series.map((line) => line.value(sample) ?? 0)), 0.001) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const duration = Math.max(end - start, INTERVAL);\n const x = (sample: MonitoringSample) => 88 + ((sample.time - start) / duration) * plotWidth;\n const y = (sample: MonitoringSample) => 208 - (sample[metric] / max) * 184;\n const selectNearest = (event: React.MouseEvent<SVGSVGElement>) => {\n const bounds = event.currentTarget.getBoundingClientRect();\n if (!bounds.width || !samples.length) return;\n const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);\n const offset = (bounds.width - width * scale) / 2;\n const position = (event.clientX - bounds.left - offset) / scale;\n const time = start + ((position - 88) / plotWidth) * duration;\n const nearest = samples.reduce((nearest, sample) =>\n Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest,\n ).time;\n setSelectedTime(nearest);\n return nearest;\n };\n return (\n <section\n aria-label={`${label} metrics-server usage`}\n style={{\n padding: \"var(--power-monitoring-trend-top, 16px) 16px 16px\",\n background: \"var(--contentColor)\",\n minWidth: 0,\n minHeight: 280,\n position: \"relative\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <ResourceTitle metric={metric} />\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n ref={chartRef}\n data-monitoring-trend=\"\"\n viewBox={`0 0 ${width} 250`}\n role=\"img\"\n tabIndex={0}\n aria-label={`${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`}\n onMouseMove={(event) => {\n if (!pinned) selectNearest(event);\n }}\n onClick={(event) => {\n if (pinned) setPinned(false);\n else if (samples.length) {\n if (selectNearest(event) === undefined && !selected) setSelectedTime(last?.time);\n setPinned(true);\n }\n }}\n onMouseLeave={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onFocus={() => {\n if (!pinned) setSelectedTime(last?.time);\n }}\n onBlur={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (samples.length) {\n if (!selected) setSelectedTime(last?.time);\n setPinned(!pinned);\n }\n return;\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const index = samples.findIndex((sample) => sample.time === selectedTime);\n setSelectedTime(\n samples[\n Math.max(\n 0,\n Math.min(\n samples.length - 1,\n (index < 0 ? samples.length - 1 : index) + (event.key === \"ArrowLeft\" ? -1 : 1),\n ),\n )\n ]?.time,\n );\n }}\n style={{ width: \"100%\", height: 250, color: \"#c93dce\", display: \"block\" }}\n >\n <title>Click to pin tooltip · click again or Escape to unpin</title>\n {[0, 0.5, 1].map((fraction) => (\n <g key={fraction}>\n <line\n x1=\"88\"\n x2={width - 24}\n y1={208 - fraction * 184}\n y2={208 - fraction * 184}\n stroke=\"var(--borderFaintColor)\"\n />\n <text x=\"80\" y={212 - fraction * 184} textAnchor=\"end\" fill=\"var(--textColorSecondary)\" fontSize=\"11\">\n {format(max * fraction)}\n </text>\n </g>\n ))}\n {series.map((line) => (\n <path\n key={line.key}\n data-monitoring-series={line.key}\n d={samples.reduce((path, sample, index) => {\n const value = line.value(sample);\n if (value === undefined) return path;\n const previous = index ? line.value(samples[index - 1]) : undefined;\n return `${path} ${previous === undefined ? \"M\" : \"L\"}${x(sample)},${208 - (value / max) * 184}`;\n }, \"\")}\n fill=\"none\"\n stroke={line.color}\n strokeWidth=\"2\"\n strokeDasharray={line.key === \"capacity\" || line.key === \"allocatable\" ? \"5 3\" : undefined}\n />\n ))}\n {samples.map((sample) => (\n <circle key={sample.time} cx={x(sample)} cy={y(sample)} r=\"2\" fill=\"currentColor\">\n <title>\n {formatMonitoringTime(sample.time)}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n {samples.length > 0 &&\n [0, 0.5, 1].map((fraction) => (\n <text\n key={fraction}\n x={88 + fraction * plotWidth}\n y=\"235\"\n textAnchor={fraction === 0 ? \"start\" : fraction === 1 ? \"end\" : \"middle\"}\n fill=\"var(--textColorSecondary)\"\n fontSize=\"11\"\n >\n {formatMonitoringTime(start + fraction * duration)}\n </text>\n ))}\n {selected && (\n <line\n data-monitoring-crosshair=\"\"\n x1={x(selected)}\n x2={x(selected)}\n y1=\"24\"\n y2=\"208\"\n stroke=\"var(--textColorSecondary)\"\n strokeDasharray=\"4 3\"\n />\n )}\n </svg>\n <style>{`\n @keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }\n [data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }\n @media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }\n `}</style>\n {selected && (\n <div\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 44,\n right: 24,\n padding: 8,\n background: \"var(--contentColor)\",\n border: \"1px solid var(--borderFaintColor)\",\n pointerEvents: \"none\",\n }}\n >\n <div>\n {formatMonitoringTime(selected.time)}\n {pinned ? \" · Pinned\" : \"\"}\n </div>\n {series.map((line) => (\n <div key={line.key} style={{ color: line.color }}>\n {line.label}: {line.value(selected) === undefined ? \"Unavailable\" : format(line.value(selected)!)}\n </div>\n ))}\n <div style={{ color: \"var(--textColorSecondary)\", marginTop: 4, fontSize: 10 }}>\n {pinned ? \"Click chart or Escape to unpin\" : \"Click or Enter to pin\"}\n </div>\n </div>\n )}\n <div\n aria-label={`${label} chart legend`}\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"6px 16px\", marginBottom: 8 }}\n >\n {series.map((line) => (\n <span key={line.key}>\n <span style={{ color: line.color }}>●</span> {line.label}\n </span>\n ))}\n </div>\n {last?.resourceStatus && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", marginBottom: 8 }}>\n {last.resourceStatus}\n </div>\n )}\n <div\n style={{ fontSize: 11, color: \"var(--textColorSecondary)\", display: \"flex\", alignItems: \"center\", gap: 4 }}\n role=\"status\"\n aria-label={`${scope} metrics status`}\n >\n <sup aria-hidden=\"true\" style={{ fontSize: 12 }}>\n *\n </sup>\n <span>{status || (samples.length ? \"metrics-server data\" : \"Waiting for metrics-server data\")}</span>\n </div>\n </section>\n );\n}\n\nfunction ResourceDonut({\n metric,\n sample,\n status,\n}: {\n metric: \"cpu\" | \"memory\";\n sample?: MonitoringSample;\n status: string;\n}) {\n const [activeRing, setActiveRing] = useState<number>();\n useEffect(() => {\n if (activeRing === undefined) return;\n const dismiss = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n setActiveRing(undefined);\n };\n document.addEventListener(\"keydown\", dismiss, true);\n return () => document.removeEventListener(\"keydown\", dismiss, true);\n }, [activeRing]);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const totals = sample?.resources?.[metric];\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const available = (value?: number) => (value === undefined ? \"Unavailable\" : format(value));\n const denominator = totals?.allocatable;\n const rings = [\n { label: \"Usage\", color: \"#c93dce\", value: sample?.[metric] },\n { label: \"Requests\", color: \"#4caf50\", value: totals?.requests },\n ];\n const percentage = (value?: number) =>\n value !== undefined && denominator !== undefined && denominator > 0 ? (value / denominator) * 100 : undefined;\n const usagePercent = percentage(sample?.[metric]);\n const unused =\n sample?.completeUsage && denominator !== undefined ? format(denominator - sample[metric]) : \"Unavailable\";\n const unrequested =\n denominator !== undefined && totals?.requests !== undefined ? format(denominator - totals.requests) : \"Unavailable\";\n const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;\n const hovered = activeRing === undefined ? undefined : rings[activeRing];\n const tooltipId = `power-monitoring-${metric}-donut-tooltip`;\n return (\n <section\n aria-label={`${label} resource summary`}\n style={{\n padding: \"var(--power-monitoring-summary-top, 16px) 16px 16px\",\n minWidth: 0,\n background: \"var(--contentColor)\",\n position: \"relative\",\n overflow: \"visible\",\n }}\n >\n <ResourceTitle metric={metric} label={metric === \"memory\" ? \"RAM\" : \"CPU\"} />\n <svg\n data-monitoring-donut=\"\"\n viewBox=\"0 0 200 200\"\n role=\"img\"\n aria-label={`${label} usage and requests relative to allocatable capacity`}\n style={{ display: \"block\", width: \"100%\", height: 200 }}\n >\n {rings.map((ring, index) => {\n const radius = 76 - index * 18;\n const percent = percentage(ring.value);\n const detail = `${ring.label}: ${available(ring.value)}${percent === undefined ? \"\" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? \", over allocatable\" : \"\"})`}`;\n return (\n <g\n key={ring.label}\n tabIndex={0}\n role=\"img\"\n aria-label={detail}\n aria-describedby={activeRing === index ? tooltipId : undefined}\n onMouseEnter={() => setActiveRing(index)}\n onMouseMove={() => setActiveRing(index)}\n onMouseLeave={() => setActiveRing(undefined)}\n onFocus={() => setActiveRing(index)}\n onBlur={() => setActiveRing(undefined)}\n >\n <circle cx=\"100\" cy=\"100\" r={radius} fill=\"none\" stroke=\"var(--borderFaintColor)\" strokeWidth=\"12\" />\n {percent !== undefined && (\n <circle\n data-monitoring-ring={ring.label}\n cx=\"100\"\n cy=\"100\"\n r={radius}\n fill=\"none\"\n stroke={ring.color}\n strokeWidth=\"12\"\n pathLength=\"100\"\n strokeDasharray={`${Math.min(100, Math.max(0, percent))} 100`}\n transform=\"rotate(-90 100 100)\"\n />\n )}\n </g>\n );\n })}\n <text\n data-monitoring-usage-percentage=\"\"\n x=\"100\"\n y=\"100\"\n textAnchor=\"middle\"\n fill=\"var(--textColorPrimary)\"\n fontSize=\"26\"\n fontWeight=\"600\"\n >\n {usagePercent === undefined ? \"N/A\" : `${usagePercent.toFixed(1)}%`}\n </text>\n <text x=\"100\" y=\"120\" textAnchor=\"middle\" fill=\"var(--textColorPrimary)\" fontSize=\"12\">\n Usage\n </text>\n </svg>\n {hovered && (\n <div\n id={tooltipId}\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 48,\n left: 8,\n right: 8,\n zIndex: 20,\n padding: 8,\n border: \"1px solid var(--borderFaintColor)\",\n background: \"var(--contentColor)\",\n pointerEvents: \"none\",\n overflowWrap: \"anywhere\",\n boxShadow: \"0 3px 12px #0003\",\n }}\n >\n <div style={{ color: hovered.color }}>\n {hovered.label}: {available(hovered.value)}\n </div>\n <div>\n {percentage(hovered.value) === undefined\n ? \"Percentage unavailable\"\n : `${percentage(hovered.value)!.toFixed(1)}% of allocatable${percentage(hovered.value)! > 100 ? \" (over allocatable)\" : \"\"}`}\n </div>\n <div>Allocatable: {available(denominator)}</div>\n </div>\n )}\n <div aria-label={`${label} resource totals`} style={{ display: \"grid\", gap: 6, fontSize: 12 }}>\n {rings.map((ring) => (\n <div\n key={ring.label}\n title={`${ring.label} relative to allocatable capacity. ${ring.label === \"Usage\" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`}\n >\n <span style={{ color: ring.color }}>●</span> {ring.label}: {available(ring.value)}\n {percentage(ring.value) !== undefined && <span> ({percentage(ring.value)!.toFixed(1)}%)</span>}\n </div>\n ))}\n <div title={allocationDetail}>\n <span style={{ color: \"#5486b5\" }}>●</span> Allocatable: {available(denominator)}\n </div>\n </div>\n {(status || sample?.resourceStatus) && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", fontSize: 11, marginTop: 8 }}>\n {status || sample?.resourceStatus}\n </div>\n )}\n </section>\n );\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n return (\n <div style={{ containerType: \"inline-size\" }}>\n <style>{`\n [data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }\n [data-monitoring-trends], [data-monitoring-summaries] { display:contents }\n @container (min-width: 961px) {\n [data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }\n }\n @container (max-width: 960px) {\n [data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }\n }\n @container (max-width: 580px) {\n [data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }\n }\n `}</style>\n <div data-monitoring-layout=\"\">\n <div data-monitoring-trends=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={metric} metric={metric} samples={samples} status={status} />\n ))}\n </div>\n <div data-monitoring-summaries=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <ResourceDonut key={metric} metric={metric} sample={samples.at(-1)} status={status} />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n };\n}\n\nfunction mountMonitoring(): () => void {\n const suppression = document.createElement(\"style\");\n suppression.setAttribute(\"data-freelens-power-native-metrics-suppression\", \"\");\n suppression.textContent = `\n [data-testid=\"cluster-overview-page\"] > [class*=\"ClusterMetrics\"],\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"ClusterMetrics\"]),\n [data-testid=\"cluster-overview-page\"] > :is([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]):not(:has(.PieChart)),\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]):not(:has(.PieChart)) {\n display:none !important;\n }\n `;\n document.head.append(suppression);\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n renderedSamples = undefined;\n renderedStatus = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n const cutoff = Date.now() - MAX_SAMPLES * INTERVAL;\n if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n host.style.gridColumn = \"1 / -1\";\n hidden.set(overview as HTMLElement, overview.getAttribute(\"style\"));\n Object.assign((overview as HTMLElement).style, {\n gridTemplateAreas: \"none\",\n gridTemplateColumns: \"minmax(0, 1fr)\",\n gridTemplateRows: \"none\",\n height: \"auto\",\n });\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && panel.style.display !== \"none\") {\n if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n // native CSS modules retain [ClusterMetrics] in the generated class name\n for (const child of overview.children) {\n if (!(child instanceof HTMLElement) || child === host) continue;\n const nativeMetrics =\n child.matches('[class*=\"ClusterMetrics\"]') || child.querySelector('[class*=\"ClusterMetrics\"]');\n const unavailablePies =\n (child.matches('[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]') ||\n child.querySelector(\n '[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]',\n )) &&\n !child.querySelector(\".PieChart\");\n if ((nativeMetrics || unavailablePies) && child.style.display !== \"none\") {\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.display = \"none\";\n }\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.gridArea = \"auto\";\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status)) {\n ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n renderedSamples = samples;\n renderedStatus = status;\n }\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const [metrics, inventory, pods] = await Promise.allSettled([\n Renderer.K8sApi.nodesMetricsApi.list(),\n Promise.resolve().then(() => Renderer.K8sApi.nodesApi.list()),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.list()),\n ]);\n if (!isCurrent()) return;\n if (metrics.status !== \"fulfilled\") throw new Error(\"Metrics unavailable\");\n const sample = aggregateMetrics(metrics.value ?? []);\n const warnings: string[] = [];\n if (inventory.status === \"fulfilled\") {\n try {\n sample.resources = aggregateResources(\n inventory.value ?? [],\n pods.status === \"fulfilled\" ? (pods.value ?? undefined) : undefined,\n );\n const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));\n const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));\n sample.completeUsage =\n expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));\n if (!sample.completeUsage)\n warnings.push(\n \"Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable\",\n );\n } catch {\n warnings.push(\"Resource totals unavailable: incomplete or invalid node/pod data\");\n }\n } else\n warnings.push(\"Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable\");\n if (pods.status !== \"fulfilled\")\n warnings.push(\"Cluster-wide Pods unavailable or access denied; requests unavailable\");\n sample.resourceStatus = warnings.join(\". \");\n samples = [...samples, sample].slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const observer = new MutationObserver(() => {\n void update();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n observer.disconnect();\n suppression.remove();\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport {\n aggregateMetrics,\n aggregateResources,\n MONITORING_INTERVAL as INTERVAL,\n MAX_SAMPLES,\n type MonitoringSample,\n} from \"../common/monitoring-math\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { getMonitoringSnapshot, retainMonitoringCollector } from \"./monitoring-collector\";\nimport { MonitoringHistoryRange, useMonitoringHistory } from \"./monitoring-history-view\";\n\nexport {\n aggregateMetrics,\n aggregateResources,\n effectivePodResource,\n MAX_SAMPLES,\n type MonitoringNode,\n type MonitoringPod,\n type MonitoringSample,\n parseQuantity,\n} from \"../common/monitoring-math\";\n\nexport function formatMonitoringTime(time: number): string {\n return new Date(time).toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n}\n\nfunction ResourceTitle({ metric, label }: { metric: \"cpu\" | \"memory\"; label?: string }) {\n return (\n <strong style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n <span className=\"Icon material\" aria-hidden=\"true\" style={{ \"--size\": \"18px\", fontWeight: 400 } as CSSProperties}>\n <span className=\"icon\">{metric === \"cpu\" ? \"developer_board\" : \"memory\"}</span>\n </span>\n {label ?? (metric === \"cpu\" ? \"CPU\" : \"Memory\")}\n </strong>\n );\n}\n\nexport function MetricChart({\n metric,\n samples,\n status,\n scope = \"node\",\n}: {\n metric: \"cpu\" | \"memory\";\n samples: MonitoringSample[];\n status: string;\n scope?: \"node\" | \"pod\";\n}) {\n const [selectedTime, setSelectedTime] = useState<number>();\n const [pinned, setPinned] = useState(false);\n const previousMetric = useRef(metric);\n useEffect(() => {\n if (\n previousMetric.current !== metric ||\n (selectedTime !== undefined && !samples.some((sample) => sample.time === selectedTime))\n ) {\n setPinned(false);\n setSelectedTime(undefined);\n }\n previousMetric.current = metric;\n }, [metric, samples, selectedTime]);\n useEffect(() => {\n if (!pinned) return;\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setPinned(false);\n setSelectedTime(undefined);\n }\n };\n document.addEventListener(\"keydown\", escape, true);\n return () => document.removeEventListener(\"keydown\", escape, true);\n }, [pinned]);\n const chartRef = useRef<SVGSVGElement>(null);\n const [width, setWidth] = useState(600);\n useLayoutEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n const measure = () => {\n const measured = chart.getBoundingClientRect().width;\n if (measured > 0) setWidth(measured);\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(chart);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, []);\n const plotWidth = Math.max(1, width - 112);\n const last = samples.at(-1);\n const selected = samples.find((sample) => sample.time === selectedTime);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const series = [\n { key: \"usage\", label: \"Usage\", color: \"#c93dce\", value: (sample: MonitoringSample) => sample[metric] },\n ...([\"requests\", \"allocatable\", \"capacity\"] as const).map((key) => ({\n key,\n label: { requests: \"Requests\", allocatable: \"Allocatable Capacity\", capacity: \"Capacity\" }[key],\n color: { requests: \"#4caf50\", allocatable: \"#5486b5\", capacity: \"var(--textColorSecondary)\" }[key],\n value: (sample: MonitoringSample) => sample.resources?.[metric][key],\n })),\n ];\n const visibleSeries =\n scope === \"pod\" ? series.filter((line) => line.key === \"usage\" || line.key === \"requests\") : series;\n const max =\n Math.max(\n ...samples.flatMap((sample) => [\n ...visibleSeries.map((line) => line.value(sample) ?? 0),\n sample.historical?.[metric]?.max ?? 0,\n ]),\n 0.001,\n ) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const duration = Math.max(end - start, INTERVAL);\n const x = (sample: MonitoringSample) => 88 + ((sample.time - start) / duration) * plotWidth;\n const y = (sample: MonitoringSample) => 208 - (sample[metric] / max) * 184;\n const selectNearest = (event: React.MouseEvent<SVGSVGElement>) => {\n const bounds = event.currentTarget.getBoundingClientRect();\n if (!bounds.width || !samples.length) return;\n const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);\n const offset = (bounds.width - width * scale) / 2;\n const position = (event.clientX - bounds.left - offset) / scale;\n const time = start + ((position - 88) / plotWidth) * duration;\n const nearest = samples.reduce((nearest, sample) =>\n Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest,\n ).time;\n setSelectedTime(nearest);\n return nearest;\n };\n return (\n <section\n aria-label={`${label} metrics-server usage`}\n style={{\n padding: \"var(--power-monitoring-trend-top, 16px) 16px 16px\",\n background: \"var(--contentColor)\",\n minWidth: 0,\n minHeight: 280,\n position: \"relative\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <ResourceTitle metric={metric} />\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n ref={chartRef}\n data-monitoring-trend=\"\"\n viewBox={`0 0 ${width} 250`}\n role=\"img\"\n tabIndex={0}\n aria-label={`${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`}\n onMouseMove={(event) => {\n if (!pinned) selectNearest(event);\n }}\n onClick={(event) => {\n if (pinned) setPinned(false);\n else if (samples.length) {\n if (selectNearest(event) === undefined && !selected) setSelectedTime(last?.time);\n setPinned(true);\n }\n }}\n onMouseLeave={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onFocus={() => {\n if (!pinned) setSelectedTime(last?.time);\n }}\n onBlur={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (samples.length) {\n if (!selected) setSelectedTime(last?.time);\n setPinned(!pinned);\n }\n return;\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const index = samples.findIndex((sample) => sample.time === selectedTime);\n setSelectedTime(\n samples[\n Math.max(\n 0,\n Math.min(\n samples.length - 1,\n (index < 0 ? samples.length - 1 : index) + (event.key === \"ArrowLeft\" ? -1 : 1),\n ),\n )\n ]?.time,\n );\n }}\n style={{ width: \"100%\", height: 250, color: \"#c93dce\", display: \"block\" }}\n >\n <title>Click to pin tooltip · click again or Escape to unpin</title>\n {[0, 0.5, 1].map((fraction) => (\n <g key={fraction}>\n <line\n x1=\"88\"\n x2={width - 24}\n y1={208 - fraction * 184}\n y2={208 - fraction * 184}\n stroke=\"var(--borderFaintColor)\"\n />\n <text x=\"80\" y={212 - fraction * 184} textAnchor=\"end\" fill=\"var(--textColorSecondary)\" fontSize=\"11\">\n {format(max * fraction)}\n </text>\n </g>\n ))}\n {samples.map((sample) => {\n const summary = sample.historical?.[metric];\n if (!summary || summary.min === summary.max) return null;\n return (\n <line\n key={`range:${sample.time}`}\n data-monitoring-extrema=\"\"\n x1={x(sample)}\n x2={x(sample)}\n y1={208 - (summary.max / max) * 184}\n y2={208 - (summary.min / max) * 184}\n stroke=\"#c93dce\"\n strokeOpacity={0.35}\n strokeWidth={4}\n />\n );\n })}\n {visibleSeries.map((line) => (\n <path\n key={line.key}\n data-monitoring-series={line.key}\n d={samples.reduce((path, sample, index) => {\n const value = line.value(sample);\n if (value === undefined) return path;\n const previous = index ? line.value(samples[index - 1]) : undefined;\n return `${path} ${previous === undefined || sample.breakBefore || sample.time - samples[index - 1].time > 1.8 * Math.max(sample.historical?.[metric]?.resolution ?? INTERVAL, samples[index - 1].historical?.[metric]?.resolution ?? INTERVAL) ? \"M\" : \"L\"}${x(sample)},${208 - (value / max) * 184}`;\n }, \"\")}\n fill=\"none\"\n stroke={line.color}\n strokeWidth=\"2\"\n strokeDasharray={line.key === \"capacity\" || line.key === \"allocatable\" ? \"5 3\" : undefined}\n />\n ))}\n {samples.map((sample) => (\n <circle key={sample.time} cx={x(sample)} cy={y(sample)} r=\"2\" fill=\"currentColor\">\n <title>\n {formatMonitoringTime(sample.time)}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n {samples.length > 0 &&\n [0, 0.5, 1].map((fraction) => (\n <text\n key={fraction}\n x={88 + fraction * plotWidth}\n y=\"235\"\n textAnchor={fraction === 0 ? \"start\" : fraction === 1 ? \"end\" : \"middle\"}\n fill=\"var(--textColorSecondary)\"\n fontSize=\"11\"\n >\n {formatMonitoringTime(start + fraction * duration)}\n </text>\n ))}\n {selected && (\n <line\n data-monitoring-crosshair=\"\"\n x1={x(selected)}\n x2={x(selected)}\n y1=\"24\"\n y2=\"208\"\n stroke=\"var(--textColorSecondary)\"\n strokeDasharray=\"4 3\"\n />\n )}\n </svg>\n <style>{`\n @keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }\n [data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }\n @media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }\n `}</style>\n {selected && (\n <div\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 44,\n right: 24,\n padding: 8,\n background: \"var(--contentColor)\",\n border: \"1px solid var(--borderFaintColor)\",\n pointerEvents: \"none\",\n }}\n >\n <div>\n {formatMonitoringTime(selected.time)}\n {pinned ? \" · Pinned\" : \"\"}\n </div>\n {visibleSeries.map((line) => (\n <div key={line.key} style={{ color: line.color }}>\n {line.label}: {line.value(selected) === undefined ? \"Unavailable\" : format(line.value(selected)!)}\n </div>\n ))}\n {selected.historical?.[metric] && (\n <div>\n Min: {format(selected.historical[metric]!.min)} · Max: {format(selected.historical[metric]!.max)}\n <br />\n {selected.historical[metric]!.count} samples ·{\" \"}\n {Math.round(selected.historical[metric]!.resolution / 1000)}s bucket\n </div>\n )}\n <div style={{ color: \"var(--textColorSecondary)\", marginTop: 4, fontSize: 10 }}>\n {pinned ? \"Click chart or Escape to unpin\" : \"Click or Enter to pin\"}\n </div>\n </div>\n )}\n <div\n aria-label={`${label} chart legend`}\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"6px 16px\", marginBottom: 8 }}\n >\n {visibleSeries.map((line) => (\n <span key={line.key}>\n <span style={{ color: line.color }}>●</span> {line.label}\n </span>\n ))}\n </div>\n {last?.resourceStatus && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", marginBottom: 8 }}>\n {last.resourceStatus}\n </div>\n )}\n {(scope === \"node\" || status || !samples.length) && (\n <div\n style={{ fontSize: 11, color: \"var(--textColorSecondary)\", display: \"flex\", alignItems: \"center\", gap: 4 }}\n role=\"status\"\n aria-label={`${scope} metrics status`}\n >\n {scope === \"node\" && (\n <sup aria-hidden=\"true\" style={{ fontSize: 12 }}>\n *\n </sup>\n )}\n <span>{status || (samples.length ? \"metrics-server data\" : \"Waiting for metrics-server data\")}</span>\n </div>\n )}\n </section>\n );\n}\n\nfunction ResourceDonut({\n metric,\n sample,\n status,\n}: {\n metric: \"cpu\" | \"memory\";\n sample?: MonitoringSample;\n status: string;\n}) {\n const [activeRing, setActiveRing] = useState<number>();\n useEffect(() => {\n if (activeRing === undefined) return;\n const dismiss = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n setActiveRing(undefined);\n };\n document.addEventListener(\"keydown\", dismiss, true);\n return () => document.removeEventListener(\"keydown\", dismiss, true);\n }, [activeRing]);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const totals = sample?.resources?.[metric];\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const available = (value?: number) => (value === undefined ? \"Unavailable\" : format(value));\n const denominator = totals?.allocatable;\n const rings = [\n { label: \"Usage\", color: \"#c93dce\", value: sample?.[metric] },\n { label: \"Requests\", color: \"#4caf50\", value: totals?.requests },\n ];\n const percentage = (value?: number) =>\n value !== undefined && denominator !== undefined && denominator > 0 ? (value / denominator) * 100 : undefined;\n const usagePercent = percentage(sample?.[metric]);\n const unused =\n sample?.completeUsage && denominator !== undefined ? format(denominator - sample[metric]) : \"Unavailable\";\n const unrequested =\n denominator !== undefined && totals?.requests !== undefined ? format(denominator - totals.requests) : \"Unavailable\";\n const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;\n const hovered = activeRing === undefined ? undefined : rings[activeRing];\n const tooltipId = `power-monitoring-${metric}-donut-tooltip`;\n return (\n <section\n aria-label={`${label} resource summary`}\n style={{\n padding: \"var(--power-monitoring-summary-top, 16px) 16px 16px\",\n minWidth: 0,\n background: \"var(--contentColor)\",\n position: \"relative\",\n overflow: \"visible\",\n }}\n >\n <ResourceTitle metric={metric} label={metric === \"memory\" ? \"RAM\" : \"CPU\"} />\n <svg\n data-monitoring-donut=\"\"\n viewBox=\"0 0 200 200\"\n role=\"img\"\n aria-label={`${label} usage and requests relative to allocatable capacity`}\n style={{ display: \"block\", width: \"100%\", height: 200 }}\n >\n {rings.map((ring, index) => {\n const radius = 76 - index * 18;\n const percent = percentage(ring.value);\n const detail = `${ring.label}: ${available(ring.value)}${percent === undefined ? \"\" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? \", over allocatable\" : \"\"})`}`;\n return (\n <g\n key={ring.label}\n tabIndex={0}\n role=\"img\"\n aria-label={detail}\n aria-describedby={activeRing === index ? tooltipId : undefined}\n onMouseEnter={() => setActiveRing(index)}\n onMouseMove={() => setActiveRing(index)}\n onMouseLeave={() => setActiveRing(undefined)}\n onFocus={() => setActiveRing(index)}\n onBlur={() => setActiveRing(undefined)}\n >\n <circle cx=\"100\" cy=\"100\" r={radius} fill=\"none\" stroke=\"var(--borderFaintColor)\" strokeWidth=\"12\" />\n {percent !== undefined && (\n <circle\n data-monitoring-ring={ring.label}\n cx=\"100\"\n cy=\"100\"\n r={radius}\n fill=\"none\"\n stroke={ring.color}\n strokeWidth=\"12\"\n pathLength=\"100\"\n strokeDasharray={`${Math.min(100, Math.max(0, percent))} 100`}\n transform=\"rotate(-90 100 100)\"\n />\n )}\n </g>\n );\n })}\n <text\n data-monitoring-usage-percentage=\"\"\n x=\"100\"\n y=\"100\"\n textAnchor=\"middle\"\n fill=\"var(--textColorPrimary)\"\n fontSize=\"26\"\n fontWeight=\"600\"\n >\n {usagePercent === undefined ? \"N/A\" : `${usagePercent.toFixed(1)}%`}\n </text>\n <text x=\"100\" y=\"120\" textAnchor=\"middle\" fill=\"var(--textColorPrimary)\" fontSize=\"12\">\n Usage\n </text>\n </svg>\n {hovered && (\n <div\n id={tooltipId}\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 48,\n left: 8,\n right: 8,\n zIndex: 20,\n padding: 8,\n border: \"1px solid var(--borderFaintColor)\",\n background: \"var(--contentColor)\",\n pointerEvents: \"none\",\n overflowWrap: \"anywhere\",\n boxShadow: \"0 3px 12px #0003\",\n }}\n >\n <div style={{ color: hovered.color }}>\n {hovered.label}: {available(hovered.value)}\n </div>\n <div>\n {percentage(hovered.value) === undefined\n ? \"Percentage unavailable\"\n : `${percentage(hovered.value)!.toFixed(1)}% of allocatable${percentage(hovered.value)! > 100 ? \" (over allocatable)\" : \"\"}`}\n </div>\n <div>Allocatable: {available(denominator)}</div>\n </div>\n )}\n <div aria-label={`${label} resource totals`} style={{ display: \"grid\", gap: 6, fontSize: 12 }}>\n {rings.map((ring) => (\n <div\n key={ring.label}\n title={`${ring.label} relative to allocatable capacity. ${ring.label === \"Usage\" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`}\n >\n <span style={{ color: ring.color }}>●</span> {ring.label}: {available(ring.value)}\n {percentage(ring.value) !== undefined && <span> ({percentage(ring.value)!.toFixed(1)}%)</span>}\n </div>\n ))}\n <div title={allocationDetail}>\n <span style={{ color: \"#5486b5\" }}>●</span> Allocatable: {available(denominator)}\n </div>\n </div>\n {(status || sample?.resourceStatus) && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", fontSize: 11, marginTop: 8 }}>\n {status || sample?.resourceStatus}\n </div>\n )}\n </section>\n );\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n const clusterId = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const history = useMonitoringHistory(\n clusterId ? { clusterId, scope: \"cluster\", uid: clusterId } : undefined,\n samples,\n );\n return (\n <div style={{ containerType: \"inline-size\" }}>\n <style>{`\n [data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }\n [data-monitoring-trends], [data-monitoring-summaries] { display:contents }\n @container (min-width: 961px) {\n [data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }\n }\n @container (max-width: 960px) {\n [data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }\n }\n @container (max-width: 580px) {\n [data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }\n }\n `}</style>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12, marginBottom: 8 }}>\n <MonitoringHistoryRange range={history.range} onChange={history.setRange} />\n <small role=\"status\">{history.status}</small>\n </div>\n <div data-monitoring-layout=\"\">\n <div data-monitoring-trends=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={metric} metric={metric} samples={history.samples} status={status} />\n ))}\n </div>\n <div data-monitoring-summaries=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <ResourceDonut key={metric} metric={metric} sample={samples.at(-1)} status={status} />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const release = retainMonitoringCollector();\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n release();\n };\n}\n\nfunction mountMonitoring(): () => void {\n const suppression = document.createElement(\"style\");\n suppression.setAttribute(\"data-freelens-power-native-metrics-suppression\", \"\");\n suppression.textContent = `\n [data-testid=\"cluster-overview-page\"] > [class*=\"ClusterMetrics\"],\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"ClusterMetrics\"]),\n [data-testid=\"cluster-overview-page\"] > :is([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]):not(:has(.PieChart)),\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]):not(:has(.PieChart)) {\n display:none !important;\n }\n `;\n document.head.append(suppression);\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n renderedSamples = undefined;\n renderedStatus = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n const cutoff = Date.now() - MAX_SAMPLES * INTERVAL;\n if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n host.style.gridColumn = \"1 / -1\";\n hidden.set(overview as HTMLElement, overview.getAttribute(\"style\"));\n Object.assign((overview as HTMLElement).style, {\n gridTemplateAreas: \"none\",\n gridTemplateColumns: \"minmax(0, 1fr)\",\n gridTemplateRows: \"none\",\n height: \"auto\",\n });\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && panel.style.display !== \"none\") {\n if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n // native CSS modules retain [ClusterMetrics] in the generated class name\n for (const child of overview.children) {\n if (!(child instanceof HTMLElement) || child === host) continue;\n const nativeMetrics =\n child.matches('[class*=\"ClusterMetrics\"]') || child.querySelector('[class*=\"ClusterMetrics\"]');\n const unavailablePies =\n (child.matches('[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]') ||\n child.querySelector(\n '[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]',\n )) &&\n !child.querySelector(\".PieChart\");\n if ((nativeMetrics || unavailablePies) && child.style.display !== \"none\") {\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.display = \"none\";\n }\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.gridArea = \"auto\";\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status)) {\n ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n renderedSamples = samples;\n renderedStatus = status;\n }\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const collected = await getMonitoringSnapshot();\n const metrics =\n collected.nodeMetrics === undefined\n ? { status: \"rejected\" as const }\n : { status: \"fulfilled\" as const, value: collected.nodeMetrics };\n const inventory =\n collected.nodes === undefined\n ? { status: \"rejected\" as const }\n : { status: \"fulfilled\" as const, value: collected.nodes };\n const pods =\n collected.pods === undefined\n ? { status: \"rejected\" as const }\n : { status: \"fulfilled\" as const, value: collected.pods };\n if (!isCurrent()) return;\n if (metrics.status !== \"fulfilled\") throw new Error(\"Metrics unavailable\");\n const sample = aggregateMetrics(metrics.value ?? []);\n sample.time = Math.min(...metrics.value.map((item) => Date.parse(item.timestamp)));\n const warnings: string[] = [];\n if (inventory.status === \"fulfilled\") {\n try {\n sample.resources = aggregateResources(\n inventory.value ?? [],\n pods.status === \"fulfilled\" ? (pods.value ?? undefined) : undefined,\n );\n const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));\n const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));\n sample.completeUsage =\n expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));\n if (!sample.completeUsage)\n warnings.push(\n \"Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable\",\n );\n } catch {\n warnings.push(\"Resource totals unavailable: incomplete or invalid node/pod data\");\n }\n } else\n warnings.push(\"Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable\");\n if (pods.status !== \"fulfilled\")\n warnings.push(\"Cluster-wide Pods unavailable or access denied; requests unavailable\");\n sample.resourceStatus = warnings.join(\". \");\n samples = [...samples.filter((entry) => entry.time !== sample.time), sample]\n .sort((a, b) => a.time - b.time)\n .slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const observer = new MutationObserver(() => {\n void update();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n observer.disconnect();\n suppression.remove();\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|