@64mb/freelens-power 1.0.1 → 1.0.3
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 +4 -4
- package/THIRD_PARTY_LICENSES +41 -0
- package/out/main/ai.js +9 -6
- package/out/main/ai.js.map +1 -1
- package/out/main/common/ai-extras.js +17 -0
- package/out/main/common/ai-extras.js.map +1 -1
- package/out/main/common/dashboard-readiness.js +88 -0
- package/out/main/common/dashboard-readiness.js.map +1 -0
- package/out/main/common/dashboard-selector.js +9 -1
- package/out/main/common/dashboard-selector.js.map +1 -1
- package/out/main/common/dashboard-views.js +2 -0
- package/out/main/common/dashboard-views.js.map +1 -1
- package/out/main/dashboard-network.js +104 -0
- package/out/main/dashboard-network.js.map +1 -0
- package/out/main/dashboard-views.js +98 -21
- package/out/main/dashboard-views.js.map +1 -1
- package/out/renderer/ai-extras.js +41 -12
- package/out/renderer/ai-extras.js.map +1 -1
- package/out/renderer/ai-message.js +1 -0
- package/out/renderer/ai-message.js.map +1 -1
- package/out/renderer/ai-provider-icon.js +44 -0
- package/out/renderer/ai-provider-icon.js.map +1 -0
- package/out/renderer/ai-shortcuts.js +1 -1
- package/out/renderer/ai-shortcuts.js.map +1 -1
- package/out/renderer/ai.js +53 -10
- package/out/renderer/ai.js.map +1 -1
- package/out/renderer/appearance.js +2 -1
- package/out/renderer/appearance.js.map +1 -1
- package/out/renderer/assets/claude.js +6 -0
- package/out/renderer/assets/claude.js.map +1 -0
- package/out/renderer/dashboard.js +39 -4
- package/out/renderer/dashboard.js.map +1 -1
- package/out/renderer/index.js +2 -0
- package/out/renderer/index.js.map +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js +1 -1
- package/out/renderer/menus/secure-node-shell-menu.js.map +1 -1
- package/out/renderer/monitoring.js +672 -91
- package/out/renderer/monitoring.js.map +1 -1
- package/out/renderer/pod-monitoring.js +282 -0
- package/out/renderer/pod-monitoring.js.map +1 -0
- package/out/renderer/preferences/ai-preferences.js +2 -2
- package/out/renderer/preferences/ai-preferences.js.map +1 -1
- package/out/renderer/preferences/dashboard-preferences.js +1 -8
- package/out/renderer/preferences/dashboard-preferences.js.map +1 -1
- package/out/renderer/preferences/preferences.js +1 -1
- package/out/renderer/preferences/preferences.js.map +1 -1
- package/out/renderer/preferences/settings-icon-action.js +3 -1
- package/out/renderer/preferences/settings-icon-action.js.map +1 -1
- package/out/renderer/src/common/ai-extras.js +17 -0
- package/out/renderer/src/common/ai-extras.js.map +1 -1
- package/out/renderer/src/common/dashboard-selector.js.map +1 -1
- package/out/renderer/src/common/dashboard-views.js +2 -0
- package/out/renderer/src/common/dashboard-views.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,59 @@
|
|
|
1
1
|
const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
2
|
const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
|
|
3
3
|
const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
|
|
4
|
+
const require__global_external_react = require("./_virtual/_global-external_react.js");
|
|
4
5
|
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
5
6
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
6
7
|
//#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
|
+
}
|
|
7
54
|
var INTERVAL = 3e4;
|
|
8
55
|
function parseQuantity(raw, memory = false) {
|
|
9
|
-
const match = /^(\d+(?:\.\d+)
|
|
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);
|
|
10
57
|
if (!match) throw new Error("Invalid metrics quantity");
|
|
11
58
|
const suffix = match[2] ?? "";
|
|
12
59
|
const value = Number(match[1]) * (suffix.endsWith("i") ? 1024 ** ([
|
|
@@ -16,7 +63,7 @@ function parseQuantity(raw, memory = false) {
|
|
|
16
63
|
"Ti",
|
|
17
64
|
"Pi",
|
|
18
65
|
"Ei"
|
|
19
|
-
].indexOf(suffix) + 1) : {
|
|
66
|
+
].indexOf(suffix) + 1) : /^[eE][+-]?\d+$/.test(suffix) ? 10 ** Number(suffix.slice(1)) : {
|
|
20
67
|
n: 1e-9,
|
|
21
68
|
u: 1e-6,
|
|
22
69
|
m: .001,
|
|
@@ -47,100 +94,563 @@ function aggregateMetrics(nodes, now = Date.now()) {
|
|
|
47
94
|
memory
|
|
48
95
|
};
|
|
49
96
|
}
|
|
50
|
-
function
|
|
51
|
-
return
|
|
97
|
+
function formatMonitoringTime(time) {
|
|
98
|
+
return new Date(time).toLocaleTimeString([], {
|
|
99
|
+
hour: "2-digit",
|
|
100
|
+
minute: "2-digit",
|
|
101
|
+
second: "2-digit",
|
|
102
|
+
hourCycle: "h23"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function ResourceTitle({ metric, label }) {
|
|
106
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("strong", {
|
|
107
|
+
style: {
|
|
108
|
+
display: "inline-flex",
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
gap: 6
|
|
111
|
+
},
|
|
112
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
113
|
+
className: "Icon material",
|
|
114
|
+
"aria-hidden": "true",
|
|
115
|
+
style: {
|
|
116
|
+
"--size": "18px",
|
|
117
|
+
fontWeight: 400
|
|
118
|
+
},
|
|
119
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
120
|
+
className: "icon",
|
|
121
|
+
children: metric === "cpu" ? "developer_board" : "memory"
|
|
122
|
+
})
|
|
123
|
+
}), label ?? (metric === "cpu" ? "CPU" : "Memory")]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
127
|
+
const [selectedTime, setSelectedTime] = require__global_external_react.useState();
|
|
128
|
+
const [pinned, setPinned] = require__global_external_react.useState(false);
|
|
129
|
+
const previousMetric = require__global_external_react.useRef(metric);
|
|
130
|
+
require__global_external_react.useEffect(() => {
|
|
131
|
+
if (previousMetric.current !== metric || selectedTime !== void 0 && !samples.some((sample) => sample.time === selectedTime)) {
|
|
132
|
+
setPinned(false);
|
|
133
|
+
setSelectedTime(void 0);
|
|
134
|
+
}
|
|
135
|
+
previousMetric.current = metric;
|
|
136
|
+
}, [
|
|
137
|
+
metric,
|
|
138
|
+
samples,
|
|
139
|
+
selectedTime
|
|
140
|
+
]);
|
|
141
|
+
require__global_external_react.useEffect(() => {
|
|
142
|
+
if (!pinned) return;
|
|
143
|
+
const escape = (event) => {
|
|
144
|
+
if (event.key === "Escape") {
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
event.stopPropagation();
|
|
147
|
+
setPinned(false);
|
|
148
|
+
setSelectedTime(void 0);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
document.addEventListener("keydown", escape, true);
|
|
152
|
+
return () => document.removeEventListener("keydown", escape, true);
|
|
153
|
+
}, [pinned]);
|
|
154
|
+
const chartRef = require__global_external_react.useRef(null);
|
|
155
|
+
const [width, setWidth] = require__global_external_react.useState(600);
|
|
156
|
+
require__global_external_react.useLayoutEffect(() => {
|
|
157
|
+
const chart = chartRef.current;
|
|
158
|
+
if (!chart) return;
|
|
159
|
+
const measure = () => {
|
|
160
|
+
const measured = chart.getBoundingClientRect().width;
|
|
161
|
+
if (measured > 0) setWidth(measured);
|
|
162
|
+
};
|
|
163
|
+
measure();
|
|
164
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
|
|
165
|
+
observer?.observe(chart);
|
|
166
|
+
window.addEventListener("resize", measure);
|
|
167
|
+
return () => {
|
|
168
|
+
observer?.disconnect();
|
|
169
|
+
window.removeEventListener("resize", measure);
|
|
170
|
+
};
|
|
171
|
+
}, []);
|
|
172
|
+
const plotWidth = Math.max(1, width - 112);
|
|
173
|
+
const last = samples.at(-1);
|
|
174
|
+
const selected = samples.find((sample) => sample.time === selectedTime);
|
|
175
|
+
const label = metric === "cpu" ? "CPU" : "Memory";
|
|
176
|
+
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
177
|
+
const series = [{
|
|
178
|
+
key: "usage",
|
|
179
|
+
label: "Usage",
|
|
180
|
+
color: "#c93dce",
|
|
181
|
+
value: (sample) => sample[metric]
|
|
182
|
+
}, ...[
|
|
183
|
+
"requests",
|
|
184
|
+
"allocatable",
|
|
185
|
+
"capacity"
|
|
186
|
+
].map((key) => ({
|
|
187
|
+
key,
|
|
188
|
+
label: {
|
|
189
|
+
requests: "Requests",
|
|
190
|
+
allocatable: "Allocatable Capacity",
|
|
191
|
+
capacity: "Capacity"
|
|
192
|
+
}[key],
|
|
193
|
+
color: {
|
|
194
|
+
requests: "#4caf50",
|
|
195
|
+
allocatable: "#5486b5",
|
|
196
|
+
capacity: "var(--textColorSecondary)"
|
|
197
|
+
}[key],
|
|
198
|
+
value: (sample) => sample.resources?.[metric][key]
|
|
199
|
+
}))];
|
|
200
|
+
const visibleSeries = scope === "pod" ? series.filter((line) => line.key === "usage" || line.key === "requests") : series;
|
|
201
|
+
const max = Math.max(...samples.flatMap((sample) => visibleSeries.map((line) => line.value(sample) ?? 0)), .001) * 1.1;
|
|
202
|
+
const end = last?.time ?? Date.now();
|
|
203
|
+
const start = samples[0]?.time ?? end;
|
|
204
|
+
const duration = Math.max(end - start, INTERVAL);
|
|
205
|
+
const x = (sample) => 88 + (sample.time - start) / duration * plotWidth;
|
|
206
|
+
const y = (sample) => 208 - sample[metric] / max * 184;
|
|
207
|
+
const selectNearest = (event) => {
|
|
208
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
209
|
+
if (!bounds.width || !samples.length) return;
|
|
210
|
+
const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);
|
|
211
|
+
const offset = (bounds.width - width * scale) / 2;
|
|
212
|
+
const time = start + ((event.clientX - bounds.left - offset) / scale - 88) / plotWidth * duration;
|
|
213
|
+
const nearest = samples.reduce((nearest, sample) => Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest).time;
|
|
214
|
+
setSelectedTime(nearest);
|
|
215
|
+
return nearest;
|
|
216
|
+
};
|
|
217
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
218
|
+
"aria-label": `${label} metrics-server usage`,
|
|
52
219
|
style: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
220
|
+
padding: "var(--power-monitoring-trend-top, 16px) 16px 16px",
|
|
221
|
+
background: "var(--contentColor)",
|
|
222
|
+
minWidth: 0,
|
|
223
|
+
minHeight: 280,
|
|
224
|
+
position: "relative"
|
|
57
225
|
},
|
|
58
|
-
children: [
|
|
59
|
-
|
|
60
|
-
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
61
|
-
const max = Math.max(...samples.map((sample) => sample[metric]), .001) * 1.1;
|
|
62
|
-
const end = last?.time ?? Date.now();
|
|
63
|
-
const start = samples[0]?.time ?? end;
|
|
64
|
-
const points = samples.map((sample) => `${8 + (sample.time - start) / Math.max(end - start, INTERVAL) * 384},${112 - sample[metric] / max * 100}`).join(" ");
|
|
65
|
-
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
226
|
+
children: [
|
|
227
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
66
228
|
style: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
229
|
+
display: "flex",
|
|
230
|
+
alignItems: "center",
|
|
231
|
+
gap: 12
|
|
232
|
+
},
|
|
233
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx(ResourceTitle, { metric }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status ? "Unavailable" : last ? format(last[metric]) : "Collecting…" })]
|
|
234
|
+
}),
|
|
235
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
236
|
+
ref: chartRef,
|
|
237
|
+
"data-monitoring-trend": "",
|
|
238
|
+
viewBox: `0 0 ${width} 250`,
|
|
239
|
+
role: "img",
|
|
240
|
+
tabIndex: 0,
|
|
241
|
+
"aria-label": `${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`,
|
|
242
|
+
onMouseMove: (event) => {
|
|
243
|
+
if (!pinned) selectNearest(event);
|
|
244
|
+
},
|
|
245
|
+
onClick: (event) => {
|
|
246
|
+
if (pinned) setPinned(false);
|
|
247
|
+
else if (samples.length) {
|
|
248
|
+
if (selectNearest(event) === void 0 && !selected) setSelectedTime(last?.time);
|
|
249
|
+
setPinned(true);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
onMouseLeave: () => {
|
|
253
|
+
if (!pinned) setSelectedTime(void 0);
|
|
254
|
+
},
|
|
255
|
+
onFocus: () => {
|
|
256
|
+
if (!pinned) setSelectedTime(last?.time);
|
|
257
|
+
},
|
|
258
|
+
onBlur: () => {
|
|
259
|
+
if (!pinned) setSelectedTime(void 0);
|
|
260
|
+
},
|
|
261
|
+
onKeyDown: (event) => {
|
|
262
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
263
|
+
event.preventDefault();
|
|
264
|
+
if (samples.length) {
|
|
265
|
+
if (!selected) setSelectedTime(last?.time);
|
|
266
|
+
setPinned(!pinned);
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
271
|
+
event.preventDefault();
|
|
272
|
+
const index = samples.findIndex((sample) => sample.time === selectedTime);
|
|
273
|
+
setSelectedTime(samples[Math.max(0, Math.min(samples.length - 1, (index < 0 ? samples.length - 1 : index) + (event.key === "ArrowLeft" ? -1 : 1)))]?.time);
|
|
274
|
+
},
|
|
275
|
+
style: {
|
|
276
|
+
width: "100%",
|
|
277
|
+
height: 250,
|
|
278
|
+
color: "#c93dce",
|
|
279
|
+
display: "block"
|
|
280
|
+
},
|
|
281
|
+
children: [
|
|
282
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("title", { children: "Click to pin tooltip · click again or Escape to unpin" }),
|
|
283
|
+
[
|
|
284
|
+
0,
|
|
285
|
+
.5,
|
|
286
|
+
1
|
|
287
|
+
].map((fraction) => /* @__PURE__ */ require_jsx_runtime.jsxs("g", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
288
|
+
x1: "88",
|
|
289
|
+
x2: width - 24,
|
|
290
|
+
y1: 208 - fraction * 184,
|
|
291
|
+
y2: 208 - fraction * 184,
|
|
292
|
+
stroke: "var(--borderFaintColor)"
|
|
293
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
294
|
+
x: "80",
|
|
295
|
+
y: 212 - fraction * 184,
|
|
296
|
+
textAnchor: "end",
|
|
297
|
+
fill: "var(--textColorSecondary)",
|
|
298
|
+
fontSize: "11",
|
|
299
|
+
children: format(max * fraction)
|
|
300
|
+
})] }, fraction)),
|
|
301
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsx("path", {
|
|
302
|
+
"data-monitoring-series": line.key,
|
|
303
|
+
d: samples.reduce((path, sample, index) => {
|
|
304
|
+
const value = line.value(sample);
|
|
305
|
+
if (value === void 0) return path;
|
|
306
|
+
return `${path} ${(index ? line.value(samples[index - 1]) : void 0) === void 0 ? "M" : "L"}${x(sample)},${208 - value / max * 184}`;
|
|
307
|
+
}, ""),
|
|
308
|
+
fill: "none",
|
|
309
|
+
stroke: line.color,
|
|
310
|
+
strokeWidth: "2",
|
|
311
|
+
strokeDasharray: line.key === "capacity" || line.key === "allocatable" ? "5 3" : void 0
|
|
312
|
+
}, line.key)),
|
|
313
|
+
samples.map((sample) => /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
314
|
+
cx: x(sample),
|
|
315
|
+
cy: y(sample),
|
|
316
|
+
r: "2",
|
|
317
|
+
fill: "currentColor",
|
|
318
|
+
children: /* @__PURE__ */ require_jsx_runtime.jsxs("title", { children: [
|
|
319
|
+
formatMonitoringTime(sample.time),
|
|
320
|
+
": ",
|
|
321
|
+
format(sample[metric])
|
|
322
|
+
] })
|
|
323
|
+
}, sample.time)),
|
|
324
|
+
samples.length > 0 && [
|
|
325
|
+
0,
|
|
326
|
+
.5,
|
|
327
|
+
1
|
|
328
|
+
].map((fraction) => /* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
329
|
+
x: 88 + fraction * plotWidth,
|
|
330
|
+
y: "235",
|
|
331
|
+
textAnchor: fraction === 0 ? "start" : fraction === 1 ? "end" : "middle",
|
|
332
|
+
fill: "var(--textColorSecondary)",
|
|
333
|
+
fontSize: "11",
|
|
334
|
+
children: formatMonitoringTime(start + fraction * duration)
|
|
335
|
+
}, fraction)),
|
|
336
|
+
selected && /* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
337
|
+
"data-monitoring-crosshair": "",
|
|
338
|
+
x1: x(selected),
|
|
339
|
+
x2: x(selected),
|
|
340
|
+
y1: "24",
|
|
341
|
+
y2: "208",
|
|
342
|
+
stroke: "var(--textColorSecondary)",
|
|
343
|
+
strokeDasharray: "4 3"
|
|
344
|
+
})
|
|
345
|
+
]
|
|
346
|
+
}),
|
|
347
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
348
|
+
@keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }
|
|
349
|
+
[data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }
|
|
350
|
+
@media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }
|
|
351
|
+
` }),
|
|
352
|
+
selected && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
353
|
+
"data-monitoring-tooltip": "",
|
|
354
|
+
role: "tooltip",
|
|
355
|
+
style: {
|
|
356
|
+
position: "absolute",
|
|
357
|
+
top: 44,
|
|
358
|
+
right: 24,
|
|
359
|
+
padding: 8,
|
|
70
360
|
background: "var(--contentColor)",
|
|
71
|
-
|
|
361
|
+
border: "1px solid var(--borderFaintColor)",
|
|
362
|
+
pointerEvents: "none"
|
|
72
363
|
},
|
|
73
|
-
"aria-label": `${metric === "cpu" ? "CPU" : "Memory"} metrics-server usage`,
|
|
74
364
|
children: [
|
|
75
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
justifyContent: "space-between",
|
|
79
|
-
gap: 8
|
|
80
|
-
},
|
|
81
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: metric === "cpu" ? "CPU" : "Memory" }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status ? "Unavailable" : last ? format(last[metric]) : "Collecting…" })]
|
|
82
|
-
}),
|
|
83
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
84
|
-
viewBox: "0 0 400 128",
|
|
85
|
-
role: "img",
|
|
86
|
-
"aria-label": `${metric} usage over collected samples`,
|
|
87
|
-
style: {
|
|
88
|
-
width: "100%",
|
|
89
|
-
height: 128,
|
|
90
|
-
color: "var(--primary)"
|
|
91
|
-
},
|
|
365
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [formatMonitoringTime(selected.time), pinned ? " · Pinned" : ""] }),
|
|
366
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
367
|
+
style: { color: line.color },
|
|
92
368
|
children: [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
112
|
|
97
|
-
].map((y) => /* @__PURE__ */ require_jsx_runtime.jsx("line", {
|
|
98
|
-
x1: "8",
|
|
99
|
-
x2: "392",
|
|
100
|
-
y1: y,
|
|
101
|
-
y2: y,
|
|
102
|
-
stroke: "var(--borderFaintColor)"
|
|
103
|
-
}, y)),
|
|
104
|
-
/* @__PURE__ */ require_jsx_runtime.jsx("polyline", {
|
|
105
|
-
points,
|
|
106
|
-
fill: "none",
|
|
107
|
-
stroke: "currentColor",
|
|
108
|
-
strokeWidth: "2"
|
|
109
|
-
}),
|
|
110
|
-
samples.map((sample) => /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
111
|
-
cx: 8 + (sample.time - start) / Math.max(end - start, INTERVAL) * 384,
|
|
112
|
-
cy: 112 - sample[metric] / max * 100,
|
|
113
|
-
r: "2",
|
|
114
|
-
fill: "currentColor",
|
|
115
|
-
children: /* @__PURE__ */ require_jsx_runtime.jsxs("title", { children: [
|
|
116
|
-
new Date(sample.time).toLocaleTimeString(),
|
|
117
|
-
": ",
|
|
118
|
-
format(sample[metric])
|
|
119
|
-
] })
|
|
120
|
-
}, sample.time))
|
|
369
|
+
line.label,
|
|
370
|
+
": ",
|
|
371
|
+
line.value(selected) === void 0 ? "Unavailable" : format(line.value(selected))
|
|
121
372
|
]
|
|
122
|
-
}),
|
|
123
|
-
/* @__PURE__ */ require_jsx_runtime.
|
|
373
|
+
}, line.key)),
|
|
374
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
124
375
|
style: {
|
|
125
|
-
display: "flex",
|
|
126
|
-
justifyContent: "space-between",
|
|
127
376
|
color: "var(--textColorSecondary)",
|
|
128
|
-
|
|
377
|
+
marginTop: 4,
|
|
378
|
+
fontSize: 10
|
|
129
379
|
},
|
|
130
|
-
children:
|
|
380
|
+
children: pinned ? "Click chart or Escape to unpin" : "Click or Enter to pin"
|
|
381
|
+
})
|
|
382
|
+
]
|
|
383
|
+
}),
|
|
384
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
385
|
+
"aria-label": `${label} chart legend`,
|
|
386
|
+
style: {
|
|
387
|
+
display: "flex",
|
|
388
|
+
flexWrap: "wrap",
|
|
389
|
+
gap: "6px 16px",
|
|
390
|
+
marginBottom: 8
|
|
391
|
+
},
|
|
392
|
+
children: visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
393
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
394
|
+
style: { color: line.color },
|
|
395
|
+
children: "●"
|
|
131
396
|
}),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
397
|
+
" ",
|
|
398
|
+
line.label
|
|
399
|
+
] }, line.key))
|
|
400
|
+
}),
|
|
401
|
+
last?.resourceStatus && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
402
|
+
role: "status",
|
|
403
|
+
style: {
|
|
404
|
+
color: "var(--textColorSecondary)",
|
|
405
|
+
marginBottom: 8
|
|
406
|
+
},
|
|
407
|
+
children: last.resourceStatus
|
|
408
|
+
}),
|
|
409
|
+
(scope === "node" || status || !samples.length) && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
410
|
+
style: {
|
|
411
|
+
fontSize: 11,
|
|
412
|
+
color: "var(--textColorSecondary)",
|
|
413
|
+
display: "flex",
|
|
414
|
+
alignItems: "center",
|
|
415
|
+
gap: 4
|
|
416
|
+
},
|
|
417
|
+
role: "status",
|
|
418
|
+
"aria-label": `${scope} metrics status`,
|
|
419
|
+
children: [scope === "node" && /* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
420
|
+
"aria-hidden": "true",
|
|
421
|
+
style: { fontSize: 12 },
|
|
422
|
+
children: "*"
|
|
423
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: status || (samples.length ? "metrics-server data" : "Waiting for metrics-server data") })]
|
|
424
|
+
})
|
|
425
|
+
]
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function ResourceDonut({ metric, sample, status }) {
|
|
429
|
+
const [activeRing, setActiveRing] = require__global_external_react.useState();
|
|
430
|
+
require__global_external_react.useEffect(() => {
|
|
431
|
+
if (activeRing === void 0) return;
|
|
432
|
+
const dismiss = (event) => {
|
|
433
|
+
if (event.key !== "Escape") return;
|
|
434
|
+
event.preventDefault();
|
|
435
|
+
event.stopPropagation();
|
|
436
|
+
setActiveRing(void 0);
|
|
437
|
+
};
|
|
438
|
+
document.addEventListener("keydown", dismiss, true);
|
|
439
|
+
return () => document.removeEventListener("keydown", dismiss, true);
|
|
440
|
+
}, [activeRing]);
|
|
441
|
+
const label = metric === "cpu" ? "CPU" : "Memory";
|
|
442
|
+
const totals = sample?.resources?.[metric];
|
|
443
|
+
const format = (value) => metric === "cpu" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;
|
|
444
|
+
const available = (value) => value === void 0 ? "Unavailable" : format(value);
|
|
445
|
+
const denominator = totals?.allocatable;
|
|
446
|
+
const rings = [{
|
|
447
|
+
label: "Usage",
|
|
448
|
+
color: "#c93dce",
|
|
449
|
+
value: sample?.[metric]
|
|
450
|
+
}, {
|
|
451
|
+
label: "Requests",
|
|
452
|
+
color: "#4caf50",
|
|
453
|
+
value: totals?.requests
|
|
454
|
+
}];
|
|
455
|
+
const percentage = (value) => value !== void 0 && denominator !== void 0 && denominator > 0 ? value / denominator * 100 : void 0;
|
|
456
|
+
const usagePercent = percentage(sample?.[metric]);
|
|
457
|
+
const unused = sample?.completeUsage && denominator !== void 0 ? format(denominator - sample[metric]) : "Unavailable";
|
|
458
|
+
const unrequested = denominator !== void 0 && totals?.requests !== void 0 ? format(denominator - totals.requests) : "Unavailable";
|
|
459
|
+
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}`;
|
|
460
|
+
const hovered = activeRing === void 0 ? void 0 : rings[activeRing];
|
|
461
|
+
const tooltipId = `power-monitoring-${metric}-donut-tooltip`;
|
|
462
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
|
|
463
|
+
"aria-label": `${label} resource summary`,
|
|
464
|
+
style: {
|
|
465
|
+
padding: "var(--power-monitoring-summary-top, 16px) 16px 16px",
|
|
466
|
+
minWidth: 0,
|
|
467
|
+
background: "var(--contentColor)",
|
|
468
|
+
position: "relative",
|
|
469
|
+
overflow: "visible"
|
|
470
|
+
},
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(ResourceTitle, {
|
|
473
|
+
metric,
|
|
474
|
+
label: metric === "memory" ? "RAM" : "CPU"
|
|
475
|
+
}),
|
|
476
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("svg", {
|
|
477
|
+
"data-monitoring-donut": "",
|
|
478
|
+
viewBox: "0 0 200 200",
|
|
479
|
+
role: "img",
|
|
480
|
+
"aria-label": `${label} usage and requests relative to allocatable capacity`,
|
|
481
|
+
style: {
|
|
482
|
+
display: "block",
|
|
483
|
+
width: "100%",
|
|
484
|
+
height: 200
|
|
485
|
+
},
|
|
486
|
+
children: [
|
|
487
|
+
rings.map((ring, index) => {
|
|
488
|
+
const radius = 76 - index * 18;
|
|
489
|
+
const percent = percentage(ring.value);
|
|
490
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("g", {
|
|
491
|
+
tabIndex: 0,
|
|
492
|
+
role: "img",
|
|
493
|
+
"aria-label": `${ring.label}: ${available(ring.value)}${percent === void 0 ? "" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? ", over allocatable" : ""})`}`,
|
|
494
|
+
"aria-describedby": activeRing === index ? tooltipId : void 0,
|
|
495
|
+
onMouseEnter: () => setActiveRing(index),
|
|
496
|
+
onMouseMove: () => setActiveRing(index),
|
|
497
|
+
onMouseLeave: () => setActiveRing(void 0),
|
|
498
|
+
onFocus: () => setActiveRing(index),
|
|
499
|
+
onBlur: () => setActiveRing(void 0),
|
|
500
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
501
|
+
cx: "100",
|
|
502
|
+
cy: "100",
|
|
503
|
+
r: radius,
|
|
504
|
+
fill: "none",
|
|
505
|
+
stroke: "var(--borderFaintColor)",
|
|
506
|
+
strokeWidth: "12"
|
|
507
|
+
}), percent !== void 0 && /* @__PURE__ */ require_jsx_runtime.jsx("circle", {
|
|
508
|
+
"data-monitoring-ring": ring.label,
|
|
509
|
+
cx: "100",
|
|
510
|
+
cy: "100",
|
|
511
|
+
r: radius,
|
|
512
|
+
fill: "none",
|
|
513
|
+
stroke: ring.color,
|
|
514
|
+
strokeWidth: "12",
|
|
515
|
+
pathLength: "100",
|
|
516
|
+
strokeDasharray: `${Math.min(100, Math.max(0, percent))} 100`,
|
|
517
|
+
transform: "rotate(-90 100 100)"
|
|
518
|
+
})]
|
|
519
|
+
}, ring.label);
|
|
520
|
+
}),
|
|
521
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
522
|
+
"data-monitoring-usage-percentage": "",
|
|
523
|
+
x: "100",
|
|
524
|
+
y: "100",
|
|
525
|
+
textAnchor: "middle",
|
|
526
|
+
fill: "var(--textColorPrimary)",
|
|
527
|
+
fontSize: "26",
|
|
528
|
+
fontWeight: "600",
|
|
529
|
+
children: usagePercent === void 0 ? "N/A" : `${usagePercent.toFixed(1)}%`
|
|
530
|
+
}),
|
|
531
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("text", {
|
|
532
|
+
x: "100",
|
|
533
|
+
y: "120",
|
|
534
|
+
textAnchor: "middle",
|
|
535
|
+
fill: "var(--textColorPrimary)",
|
|
536
|
+
fontSize: "12",
|
|
537
|
+
children: "Usage"
|
|
140
538
|
})
|
|
141
539
|
]
|
|
142
|
-
},
|
|
143
|
-
|
|
540
|
+
}),
|
|
541
|
+
hovered && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
542
|
+
id: tooltipId,
|
|
543
|
+
"data-monitoring-tooltip": "",
|
|
544
|
+
role: "tooltip",
|
|
545
|
+
style: {
|
|
546
|
+
position: "absolute",
|
|
547
|
+
top: 48,
|
|
548
|
+
left: 8,
|
|
549
|
+
right: 8,
|
|
550
|
+
zIndex: 20,
|
|
551
|
+
padding: 8,
|
|
552
|
+
border: "1px solid var(--borderFaintColor)",
|
|
553
|
+
background: "var(--contentColor)",
|
|
554
|
+
pointerEvents: "none",
|
|
555
|
+
overflowWrap: "anywhere",
|
|
556
|
+
boxShadow: "0 3px 12px #0003"
|
|
557
|
+
},
|
|
558
|
+
children: [
|
|
559
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
560
|
+
style: { color: hovered.color },
|
|
561
|
+
children: [
|
|
562
|
+
hovered.label,
|
|
563
|
+
": ",
|
|
564
|
+
available(hovered.value)
|
|
565
|
+
]
|
|
566
|
+
}),
|
|
567
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", { children: percentage(hovered.value) === void 0 ? "Percentage unavailable" : `${percentage(hovered.value).toFixed(1)}% of allocatable${percentage(hovered.value) > 100 ? " (over allocatable)" : ""}` }),
|
|
568
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: ["Allocatable: ", available(denominator)] })
|
|
569
|
+
]
|
|
570
|
+
}),
|
|
571
|
+
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
572
|
+
"aria-label": `${label} resource totals`,
|
|
573
|
+
style: {
|
|
574
|
+
display: "grid",
|
|
575
|
+
gap: 6,
|
|
576
|
+
fontSize: 12
|
|
577
|
+
},
|
|
578
|
+
children: [rings.map((ring) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
579
|
+
title: `${ring.label} relative to allocatable capacity. ${ring.label === "Usage" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`,
|
|
580
|
+
children: [
|
|
581
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
582
|
+
style: { color: ring.color },
|
|
583
|
+
children: "●"
|
|
584
|
+
}),
|
|
585
|
+
" ",
|
|
586
|
+
ring.label,
|
|
587
|
+
": ",
|
|
588
|
+
available(ring.value),
|
|
589
|
+
percentage(ring.value) !== void 0 && /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
590
|
+
" (",
|
|
591
|
+
percentage(ring.value).toFixed(1),
|
|
592
|
+
"%)"
|
|
593
|
+
] })
|
|
594
|
+
]
|
|
595
|
+
}, ring.label)), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
596
|
+
title: allocationDetail,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
599
|
+
style: { color: "#5486b5" },
|
|
600
|
+
children: "●"
|
|
601
|
+
}),
|
|
602
|
+
" Allocatable: ",
|
|
603
|
+
available(denominator)
|
|
604
|
+
]
|
|
605
|
+
})]
|
|
606
|
+
}),
|
|
607
|
+
(status || sample?.resourceStatus) && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
608
|
+
role: "status",
|
|
609
|
+
style: {
|
|
610
|
+
color: "var(--textColorSecondary)",
|
|
611
|
+
fontSize: 11,
|
|
612
|
+
marginTop: 8
|
|
613
|
+
},
|
|
614
|
+
children: status || sample?.resourceStatus
|
|
615
|
+
})
|
|
616
|
+
]
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function MonitoringCharts({ samples, status }) {
|
|
620
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
621
|
+
style: { containerType: "inline-size" },
|
|
622
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
623
|
+
[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 }
|
|
624
|
+
[data-monitoring-trends], [data-monitoring-summaries] { display:contents }
|
|
625
|
+
@container (min-width: 961px) {
|
|
626
|
+
[data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }
|
|
627
|
+
}
|
|
628
|
+
@container (max-width: 960px) {
|
|
629
|
+
[data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }
|
|
630
|
+
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }
|
|
631
|
+
}
|
|
632
|
+
@container (max-width: 580px) {
|
|
633
|
+
[data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }
|
|
634
|
+
.power-ai-overview-actions + [data-testid="cluster-overview-page"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }
|
|
635
|
+
}
|
|
636
|
+
` }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
637
|
+
"data-monitoring-layout": "",
|
|
638
|
+
children: [/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
639
|
+
"data-monitoring-trends": "",
|
|
640
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(MetricChart, {
|
|
641
|
+
metric,
|
|
642
|
+
samples,
|
|
643
|
+
status
|
|
644
|
+
}, metric))
|
|
645
|
+
}), /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
646
|
+
"data-monitoring-summaries": "",
|
|
647
|
+
children: ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(ResourceDonut, {
|
|
648
|
+
metric,
|
|
649
|
+
sample: samples.at(-1),
|
|
650
|
+
status
|
|
651
|
+
}, metric))
|
|
652
|
+
})]
|
|
653
|
+
})]
|
|
144
654
|
});
|
|
145
655
|
}
|
|
146
656
|
function startMonitoring() {
|
|
@@ -156,10 +666,23 @@ function startMonitoring() {
|
|
|
156
666
|
};
|
|
157
667
|
}
|
|
158
668
|
function mountMonitoring() {
|
|
669
|
+
const suppression = document.createElement("style");
|
|
670
|
+
suppression.setAttribute("data-freelens-power-native-metrics-suppression", "");
|
|
671
|
+
suppression.textContent = `
|
|
672
|
+
[data-testid="cluster-overview-page"] > [class*="ClusterMetrics"],
|
|
673
|
+
[data-testid="cluster-overview-page"] > :has([class*="ClusterMetrics"]),
|
|
674
|
+
[data-testid="cluster-overview-page"] > :is([class*="noMetrics"], [data-testid="no-metrics-message"]):not(:has(.PieChart)),
|
|
675
|
+
[data-testid="cluster-overview-page"] > :has([class*="noMetrics"], [data-testid="no-metrics-message"], [class*="cluster-pie-charts"][class*="empty"]):not(:has(.PieChart)) {
|
|
676
|
+
display:none !important;
|
|
677
|
+
}
|
|
678
|
+
`;
|
|
679
|
+
document.head.append(suppression);
|
|
159
680
|
let host;
|
|
160
681
|
let overview = null;
|
|
161
682
|
let samples = [];
|
|
162
683
|
let status = "";
|
|
684
|
+
let renderedSamples;
|
|
685
|
+
let renderedStatus;
|
|
163
686
|
let cluster;
|
|
164
687
|
let stopped = false;
|
|
165
688
|
let pendingGeneration;
|
|
@@ -174,6 +697,8 @@ function mountMonitoring() {
|
|
|
174
697
|
require__global_external_react_dom.default.unmountComponentAtNode(host);
|
|
175
698
|
host.remove();
|
|
176
699
|
host = void 0;
|
|
700
|
+
renderedSamples = void 0;
|
|
701
|
+
renderedStatus = void 0;
|
|
177
702
|
}
|
|
178
703
|
};
|
|
179
704
|
const update = async () => {
|
|
@@ -188,26 +713,50 @@ function mountMonitoring() {
|
|
|
188
713
|
cluster = currentCluster;
|
|
189
714
|
overview = currentOverview;
|
|
190
715
|
}
|
|
191
|
-
|
|
716
|
+
const cutoff = Date.now() - 60 * INTERVAL;
|
|
717
|
+
if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);
|
|
192
718
|
if (!overview || !cluster || document.visibilityState === "hidden") return;
|
|
193
719
|
if (!host) {
|
|
194
720
|
host = document.createElement("div");
|
|
195
721
|
host.setAttribute("data-freelens-power-monitoring", "");
|
|
722
|
+
host.style.gridColumn = "1 / -1";
|
|
723
|
+
hidden.set(overview, overview.getAttribute("style"));
|
|
724
|
+
Object.assign(overview.style, {
|
|
725
|
+
gridTemplateAreas: "none",
|
|
726
|
+
gridTemplateColumns: "minmax(0, 1fr)",
|
|
727
|
+
gridTemplateRows: "none",
|
|
728
|
+
height: "auto"
|
|
729
|
+
});
|
|
196
730
|
overview.prepend(host);
|
|
197
731
|
}
|
|
198
732
|
for (const title of overview.querySelectorAll(".PieChart > .chart-title")) {
|
|
199
733
|
if (title.textContent !== "CPU" && title.textContent !== "Memory") continue;
|
|
200
734
|
const panel = title.closest(".PieChart")?.parentElement;
|
|
201
|
-
if (panel &&
|
|
202
|
-
hidden.set(panel, panel.getAttribute("style"));
|
|
735
|
+
if (panel && panel.style.display !== "none") {
|
|
736
|
+
if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute("style"));
|
|
203
737
|
panel.style.display = "none";
|
|
204
738
|
}
|
|
205
739
|
}
|
|
740
|
+
for (const child of overview.children) {
|
|
741
|
+
if (!(child instanceof HTMLElement) || child === host) continue;
|
|
742
|
+
const nativeMetrics = child.matches("[class*=\"ClusterMetrics\"]") || child.querySelector("[class*=\"ClusterMetrics\"]");
|
|
743
|
+
const unavailablePies = (child.matches("[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]") || child.querySelector("[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]")) && !child.querySelector(".PieChart");
|
|
744
|
+
if ((nativeMetrics || unavailablePies) && child.style.display !== "none") {
|
|
745
|
+
if (!hidden.has(child)) hidden.set(child, child.getAttribute("style"));
|
|
746
|
+
child.style.display = "none";
|
|
747
|
+
}
|
|
748
|
+
if (!hidden.has(child)) hidden.set(child, child.getAttribute("style"));
|
|
749
|
+
child.style.gridArea = "auto";
|
|
750
|
+
}
|
|
206
751
|
const render = () => {
|
|
207
|
-
if (host
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
752
|
+
if (host && (renderedSamples !== samples || renderedStatus !== status)) {
|
|
753
|
+
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(MonitoringCharts, {
|
|
754
|
+
samples,
|
|
755
|
+
status
|
|
756
|
+
}), host);
|
|
757
|
+
renderedSamples = samples;
|
|
758
|
+
renderedStatus = status;
|
|
759
|
+
}
|
|
211
760
|
};
|
|
212
761
|
render();
|
|
213
762
|
if (pendingGeneration === generation || Date.now() < nextSample) return;
|
|
@@ -216,9 +765,28 @@ function mountMonitoring() {
|
|
|
216
765
|
const requestGeneration = generation;
|
|
217
766
|
const isCurrent = () => !stopped && requestGeneration === generation && document.visibilityState !== "hidden" && overview?.isConnected === true && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;
|
|
218
767
|
try {
|
|
219
|
-
const
|
|
768
|
+
const [metrics, inventory, pods] = await Promise.allSettled([
|
|
769
|
+
require_extensions.Renderer.K8sApi.nodesMetricsApi.list(),
|
|
770
|
+
Promise.resolve().then(() => require_extensions.Renderer.K8sApi.nodesApi.list()),
|
|
771
|
+
Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsApi.list())
|
|
772
|
+
]);
|
|
220
773
|
if (!isCurrent()) return;
|
|
221
|
-
|
|
774
|
+
if (metrics.status !== "fulfilled") throw new Error("Metrics unavailable");
|
|
775
|
+
const sample = aggregateMetrics(metrics.value ?? []);
|
|
776
|
+
const warnings = [];
|
|
777
|
+
if (inventory.status === "fulfilled") try {
|
|
778
|
+
sample.resources = aggregateResources(inventory.value ?? [], pods.status === "fulfilled" ? pods.value ?? void 0 : void 0);
|
|
779
|
+
const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));
|
|
780
|
+
const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));
|
|
781
|
+
sample.completeUsage = expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));
|
|
782
|
+
if (!sample.completeUsage) warnings.push("Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable");
|
|
783
|
+
} catch {
|
|
784
|
+
warnings.push("Resource totals unavailable: incomplete or invalid node/pod data");
|
|
785
|
+
}
|
|
786
|
+
else warnings.push("Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable");
|
|
787
|
+
if (pods.status !== "fulfilled") warnings.push("Cluster-wide Pods unavailable or access denied; requests unavailable");
|
|
788
|
+
sample.resourceStatus = warnings.join(". ");
|
|
789
|
+
samples = [...samples, sample].slice(-60);
|
|
222
790
|
status = "";
|
|
223
791
|
} catch {
|
|
224
792
|
if (!isCurrent()) return;
|
|
@@ -228,6 +796,13 @@ function mountMonitoring() {
|
|
|
228
796
|
}
|
|
229
797
|
if (!stopped && requestGeneration === generation) render();
|
|
230
798
|
};
|
|
799
|
+
const observer = new MutationObserver(() => {
|
|
800
|
+
update();
|
|
801
|
+
});
|
|
802
|
+
observer.observe(document.body, {
|
|
803
|
+
childList: true,
|
|
804
|
+
subtree: true
|
|
805
|
+
});
|
|
231
806
|
const timer = window.setInterval(() => {
|
|
232
807
|
update();
|
|
233
808
|
}, 1e3);
|
|
@@ -236,12 +811,18 @@ function mountMonitoring() {
|
|
|
236
811
|
stopped = true;
|
|
237
812
|
generation++;
|
|
238
813
|
window.clearInterval(timer);
|
|
814
|
+
observer.disconnect();
|
|
815
|
+
suppression.remove();
|
|
239
816
|
restore();
|
|
240
817
|
samples = [];
|
|
241
818
|
};
|
|
242
819
|
}
|
|
243
820
|
//#endregion
|
|
821
|
+
exports.MetricChart = MetricChart;
|
|
244
822
|
exports.aggregateMetrics = aggregateMetrics;
|
|
823
|
+
exports.aggregateResources = aggregateResources;
|
|
824
|
+
exports.effectivePodResource = effectivePodResource;
|
|
825
|
+
exports.formatMonitoringTime = formatMonitoringTime;
|
|
245
826
|
exports.parseQuantity = parseQuantity;
|
|
246
827
|
exports.startMonitoring = startMonitoring;
|
|
247
828
|
|