@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
|
+
const require__global_external_mobx_react = require("./_virtual/_global-external_mobx-react.js");
|
|
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");
|
|
5
|
+
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
6
|
+
const require_monitoring_collector = require("./monitoring-collector.js");
|
|
7
|
+
const require_monitoring_history_view = require("./monitoring-history-view.js");
|
|
8
|
+
const require_monitoring = require("./monitoring.js");
|
|
9
|
+
//#region src/renderer/node-monitoring.tsx
|
|
10
|
+
var NodeMonitoringCharts = require__global_external_mobx_react.observer(({ object }) => {
|
|
11
|
+
const clusterId = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
|
|
12
|
+
const name = object.metadata.name;
|
|
13
|
+
const uid = object.metadata.uid;
|
|
14
|
+
const identity = JSON.stringify([
|
|
15
|
+
clusterId,
|
|
16
|
+
name,
|
|
17
|
+
uid
|
|
18
|
+
]);
|
|
19
|
+
const [state, setState] = require__global_external_react.useState({
|
|
20
|
+
identity,
|
|
21
|
+
samples: [],
|
|
22
|
+
status: ""
|
|
23
|
+
});
|
|
24
|
+
require__global_external_react.useEffect(() => {
|
|
25
|
+
let stopped = false;
|
|
26
|
+
let pending = false;
|
|
27
|
+
const release = require_monitoring_collector.retainMonitoringCollector();
|
|
28
|
+
setState({
|
|
29
|
+
identity,
|
|
30
|
+
samples: [],
|
|
31
|
+
status: ""
|
|
32
|
+
});
|
|
33
|
+
const current = () => !stopped && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId && object.metadata.name === name && object.metadata.uid === uid;
|
|
34
|
+
const update = async () => {
|
|
35
|
+
if (pending || !clusterId || document.visibilityState === "hidden") return;
|
|
36
|
+
pending = true;
|
|
37
|
+
try {
|
|
38
|
+
const snapshot = await require_monitoring_collector.getMonitoringSnapshot();
|
|
39
|
+
if (!current() || snapshot.clusterId !== clusterId) return;
|
|
40
|
+
const node = snapshot.nodes?.find((candidate) => candidate.metadata.name === name);
|
|
41
|
+
const replaced = !!uid && !!node?.metadata.uid && node.metadata.uid !== uid;
|
|
42
|
+
const sample = replaced ? void 0 : require_monitoring_collector.getNodeSample(snapshot, name, uid);
|
|
43
|
+
setState((previous) => {
|
|
44
|
+
const retained = (previous.identity === identity && !replaced ? previous.samples : []).filter((entry) => entry.time >= Date.now() - 30 * 6e4);
|
|
45
|
+
return {
|
|
46
|
+
identity,
|
|
47
|
+
samples: sample ? [...retained.filter((entry) => entry.time !== sample.time), sample].sort((a, b) => a.time - b.time).slice(-60) : retained,
|
|
48
|
+
status: sample ? "" : "Node metrics unavailable, stale or access denied. Last collected samples remain visible."
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
52
|
+
if (current()) setState((previous) => ({
|
|
53
|
+
identity,
|
|
54
|
+
samples: previous.identity === identity ? previous.samples.filter((entry) => entry.time >= Date.now() - 30 * 6e4) : [],
|
|
55
|
+
status: "Node metrics unavailable, stale or access denied. Last collected samples remain visible."
|
|
56
|
+
}));
|
|
57
|
+
} finally {
|
|
58
|
+
pending = false;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const timer = window.setInterval(() => {
|
|
62
|
+
update();
|
|
63
|
+
}, 1e3);
|
|
64
|
+
update();
|
|
65
|
+
return () => {
|
|
66
|
+
stopped = true;
|
|
67
|
+
window.clearInterval(timer);
|
|
68
|
+
release();
|
|
69
|
+
};
|
|
70
|
+
}, [
|
|
71
|
+
clusterId,
|
|
72
|
+
identity,
|
|
73
|
+
name,
|
|
74
|
+
object,
|
|
75
|
+
uid
|
|
76
|
+
]);
|
|
77
|
+
const samples = state.identity === identity ? state.samples : [];
|
|
78
|
+
const status = state.identity === identity ? state.status : "";
|
|
79
|
+
const history = require_monitoring_history_view.useMonitoringHistory(clusterId && uid ? {
|
|
80
|
+
clusterId,
|
|
81
|
+
scope: "node",
|
|
82
|
+
uid
|
|
83
|
+
} : void 0, samples);
|
|
84
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
85
|
+
"data-power-node-monitoring": "",
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `
|
|
88
|
+
.KubeObjectDetails:has([data-power-node-monitoring]) .ResourceMetrics,
|
|
89
|
+
.KubeObjectDetails:has([data-power-node-monitoring]) [data-time-range]:has(+ .ResourceMetrics) {
|
|
90
|
+
display: none !important;
|
|
91
|
+
}
|
|
92
|
+
` }),
|
|
93
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring_history_view.MonitoringHistoryRange, {
|
|
94
|
+
native: true,
|
|
95
|
+
range: history.range,
|
|
96
|
+
onChange: history.setRange
|
|
97
|
+
}),
|
|
98
|
+
history.status && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
99
|
+
role: "status",
|
|
100
|
+
children: history.status
|
|
101
|
+
}),
|
|
102
|
+
["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring.MetricChart, {
|
|
103
|
+
metric,
|
|
104
|
+
samples: history.samples,
|
|
105
|
+
status
|
|
106
|
+
}, `${identity}:${metric}`))
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
var NodeMonitoringDetails = require__global_external_mobx_react.observer((props) => {
|
|
111
|
+
return require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate().enabled ? /* @__PURE__ */ require_jsx_runtime.jsx(NodeMonitoringCharts, { ...props }) : null;
|
|
112
|
+
});
|
|
113
|
+
//#endregion
|
|
114
|
+
exports.NodeMonitoringDetails = NodeMonitoringDetails;
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=node-monitoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-monitoring.js","names":[],"sources":["../../src/renderer/node-monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { MAX_SAMPLES, type MonitoringSample } from \"../common/monitoring-math\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport { MetricChart } from \"./monitoring\";\nimport { getMonitoringSnapshot, getNodeSample, retainMonitoringCollector } from \"./monitoring-collector\";\nimport { MonitoringHistoryRange, useMonitoringHistory } from \"./monitoring-history-view\";\n\ntype NodeProps = Renderer.Component.KubeObjectDetailsProps<Renderer.K8sApi.Node>;\n\nconst NodeMonitoringCharts = observer(({ object }: NodeProps) => {\n const clusterId = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const name = object.metadata.name;\n const uid = object.metadata.uid;\n const identity = JSON.stringify([clusterId, name, uid]);\n const [state, setState] = useState<{ identity: string; samples: MonitoringSample[]; status: string }>({\n identity,\n samples: [],\n status: \"\",\n });\n useEffect(() => {\n let stopped = false;\n let pending = false;\n const release = retainMonitoringCollector();\n setState({ identity, samples: [], status: \"\" });\n const current = () =>\n !stopped &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === clusterId &&\n object.metadata.name === name &&\n object.metadata.uid === uid;\n const update = async () => {\n if (pending || !clusterId || document.visibilityState === \"hidden\") return;\n pending = true;\n try {\n const snapshot = await getMonitoringSnapshot();\n if (!current() || snapshot.clusterId !== clusterId) return;\n const node = snapshot.nodes?.find((candidate) => candidate.metadata.name === name);\n const replaced = !!uid && !!node?.metadata.uid && node.metadata.uid !== uid;\n const sample = replaced ? undefined : getNodeSample(snapshot, name, uid);\n setState((previous) => {\n const retained = (previous.identity === identity && !replaced ? previous.samples : []).filter(\n (entry) => entry.time >= Date.now() - 30 * 60_000,\n );\n return {\n identity,\n samples: sample\n ? [...retained.filter((entry) => entry.time !== sample.time), sample]\n .sort((a, b) => a.time - b.time)\n .slice(-MAX_SAMPLES)\n : retained,\n status: sample\n ? \"\"\n : \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\",\n };\n });\n } catch {\n if (current())\n setState((previous) => ({\n identity,\n samples:\n previous.identity === identity\n ? previous.samples.filter((entry) => entry.time >= Date.now() - 30 * 60_000)\n : [],\n status: \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\",\n }));\n } finally {\n pending = false;\n }\n };\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n window.clearInterval(timer);\n release();\n };\n }, [clusterId, identity, name, object, uid]);\n const samples = state.identity === identity ? state.samples : [];\n const status = state.identity === identity ? state.status : \"\";\n const history = useMonitoringHistory(clusterId && uid ? { clusterId, scope: \"node\", uid } : undefined, samples);\n return (\n <div data-power-node-monitoring=\"\">\n <style>{`\n .KubeObjectDetails:has([data-power-node-monitoring]) .ResourceMetrics,\n .KubeObjectDetails:has([data-power-node-monitoring]) [data-time-range]:has(+ .ResourceMetrics) {\n display: none !important;\n }\n `}</style>\n <MonitoringHistoryRange native range={history.range} onChange={history.setRange} />\n {history.status && <div role=\"status\">{history.status}</div>}\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={`${identity}:${metric}`} metric={metric} samples={history.samples} status={status} />\n ))}\n </div>\n );\n});\n\nexport const NodeMonitoringDetails = observer((props: NodeProps) => {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n return preferences.enabled ? <NodeMonitoringCharts {...props} /> : null;\n});\n"],"mappings":"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"}
|
|
@@ -3,6 +3,9 @@ const require__global_external_mobx = require("./_virtual/_global-external_mobx.
|
|
|
3
3
|
const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
|
|
4
4
|
const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
|
|
5
5
|
const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
|
|
6
|
+
const require_monitoring_math = require("./src/common/monitoring-math.js");
|
|
7
|
+
const require_monitoring_collector = require("./monitoring-collector.js");
|
|
8
|
+
const require_monitoring_history_view = require("./monitoring-history-view.js");
|
|
6
9
|
const require_monitoring = require("./monitoring.js");
|
|
7
10
|
//#region src/renderer/pod-monitoring.tsx
|
|
8
11
|
var INTERVAL = 3e4;
|
|
@@ -17,8 +20,8 @@ function aggregatePodMetrics(metrics, identity, now = Date.now()) {
|
|
|
17
20
|
for (const container of metrics.containers) {
|
|
18
21
|
if (!container.name || names.has(container.name)) throw new Error("Invalid container metrics");
|
|
19
22
|
names.add(container.name);
|
|
20
|
-
cpu +=
|
|
21
|
-
memory +=
|
|
23
|
+
cpu += require_monitoring_math.parseQuantity(container.usage.cpu);
|
|
24
|
+
memory += require_monitoring_math.parseQuantity(container.usage.memory, true);
|
|
22
25
|
}
|
|
23
26
|
return {
|
|
24
27
|
time,
|
|
@@ -26,6 +29,29 @@ function aggregatePodMetrics(metrics, identity, now = Date.now()) {
|
|
|
26
29
|
memory
|
|
27
30
|
};
|
|
28
31
|
}
|
|
32
|
+
function PodMonitoringChart({ clusterId, uid, samples, status, metric }) {
|
|
33
|
+
const history = require_monitoring_history_view.useMonitoringHistory(uid ? {
|
|
34
|
+
clusterId,
|
|
35
|
+
scope: "pod",
|
|
36
|
+
uid
|
|
37
|
+
} : void 0, samples);
|
|
38
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
|
|
39
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring_history_view.MonitoringHistoryRange, {
|
|
40
|
+
range: history.range,
|
|
41
|
+
onChange: history.setRange
|
|
42
|
+
}),
|
|
43
|
+
history.status && /* @__PURE__ */ require_jsx_runtime.jsx("small", {
|
|
44
|
+
role: "status",
|
|
45
|
+
children: history.status
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring.MetricChart, {
|
|
48
|
+
metric,
|
|
49
|
+
samples: history.samples,
|
|
50
|
+
status,
|
|
51
|
+
scope: "pod"
|
|
52
|
+
})
|
|
53
|
+
] });
|
|
54
|
+
}
|
|
29
55
|
function selectedPod() {
|
|
30
56
|
const match = new URLSearchParams(window.location.search).get("kube-details")?.match(/^\/api\/v1\/namespaces\/([a-z0-9][a-z0-9.-]*)\/pods\/([a-z0-9][a-z0-9.-]*)$/);
|
|
31
57
|
if (match) return {
|
|
@@ -41,6 +67,7 @@ function visible(element) {
|
|
|
41
67
|
return true;
|
|
42
68
|
}
|
|
43
69
|
function startPodMonitoring() {
|
|
70
|
+
const release = require_monitoring_collector.retainMonitoringCollector();
|
|
44
71
|
const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
|
|
45
72
|
let dispose;
|
|
46
73
|
const stop = require__global_external_mobx.reaction(() => preferences.enabled, (enabled) => {
|
|
@@ -50,9 +77,22 @@ function startPodMonitoring() {
|
|
|
50
77
|
return () => {
|
|
51
78
|
stop();
|
|
52
79
|
dispose?.();
|
|
80
|
+
release();
|
|
53
81
|
};
|
|
54
82
|
}
|
|
55
83
|
function mountPodMonitoring() {
|
|
84
|
+
const nativeSuppression = document.createElement("style");
|
|
85
|
+
const controllers = ".DeploymentDetails, .ReplicaSetDetails, .DaemonSetDetails, .StatefulSetDetails, .JobDetails, .CronJobDetails, [class*=ReplicationControllerDetails]";
|
|
86
|
+
nativeSuppression.dataset.powerDetailMonitoring = "";
|
|
87
|
+
nativeSuppression.textContent = `
|
|
88
|
+
.KubeObjectDetails:has(:is(${controllers})) .ResourceMetrics,
|
|
89
|
+
.KubeObjectDetails:has(:is(${controllers})) [data-time-range]:has(+ .ResourceMetrics),
|
|
90
|
+
.KubeObjectDetails:has(.PodDetails) [data-time-range]:has(+ .ResourceMetrics > [data-power-pod-monitoring]),
|
|
91
|
+
.KubeObjectDetails .ResourceMetrics:has(> [data-power-pod-monitoring]) > .loader {
|
|
92
|
+
display: none !important;
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
document.head.append(nativeSuppression);
|
|
56
96
|
let stopped = false;
|
|
57
97
|
let generation = 0;
|
|
58
98
|
let session = "";
|
|
@@ -78,6 +118,58 @@ function mountPodMonitoring() {
|
|
|
78
118
|
graph = void 0;
|
|
79
119
|
renderedSamples = void 0;
|
|
80
120
|
};
|
|
121
|
+
let controlledMetrics;
|
|
122
|
+
const tabAttributes = /* @__PURE__ */ new Map();
|
|
123
|
+
const saveAttributes = (element, names) => {
|
|
124
|
+
if (!tabAttributes.has(element)) tabAttributes.set(element, new Map(names.map((name) => [name, element.getAttribute(name)])));
|
|
125
|
+
};
|
|
126
|
+
const unsupportedTab = (target) => {
|
|
127
|
+
const radio = target instanceof Element ? target.closest(".Radio") : null;
|
|
128
|
+
return radio && ["Network", "Filesystem"].includes(radio.querySelector(".label")?.textContent ?? "");
|
|
129
|
+
};
|
|
130
|
+
const blockUnsupported = (event) => {
|
|
131
|
+
if (!unsupportedTab(event.target)) return;
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
event.stopImmediatePropagation();
|
|
134
|
+
};
|
|
135
|
+
const tabEvents = [
|
|
136
|
+
"click",
|
|
137
|
+
"pointerdown",
|
|
138
|
+
"mousedown",
|
|
139
|
+
"keydown",
|
|
140
|
+
"change"
|
|
141
|
+
];
|
|
142
|
+
const restoreTabs = () => {
|
|
143
|
+
for (const [element, attributes] of tabAttributes) for (const [name, value] of attributes) if (value === null) element.removeAttribute(name);
|
|
144
|
+
else element.setAttribute(name, value);
|
|
145
|
+
tabAttributes.clear();
|
|
146
|
+
for (const event of tabEvents) controlledMetrics?.removeEventListener(event, blockUnsupported, true);
|
|
147
|
+
controlledMetrics = void 0;
|
|
148
|
+
};
|
|
149
|
+
const restrictTabs = (metrics) => {
|
|
150
|
+
if (controlledMetrics !== metrics) {
|
|
151
|
+
restoreTabs();
|
|
152
|
+
controlledMetrics = metrics;
|
|
153
|
+
for (const event of tabEvents) metrics.addEventListener(event, blockUnsupported, true);
|
|
154
|
+
}
|
|
155
|
+
for (const radio of metrics.querySelectorAll(".Radio")) {
|
|
156
|
+
if (!unsupportedTab(radio)) continue;
|
|
157
|
+
saveAttributes(radio, [
|
|
158
|
+
"style",
|
|
159
|
+
"hidden",
|
|
160
|
+
"aria-hidden"
|
|
161
|
+
]);
|
|
162
|
+
if (radio.style.display !== "none") radio.style.display = "none";
|
|
163
|
+
radio.hidden = true;
|
|
164
|
+
radio.setAttribute("aria-hidden", "true");
|
|
165
|
+
const input = radio.querySelector("input");
|
|
166
|
+
if (input) {
|
|
167
|
+
saveAttributes(input, ["disabled"]);
|
|
168
|
+
input.disabled = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (unsupportedTab(metrics.querySelector(".Radio.checked"))) [...metrics.querySelectorAll(".Radio")].find((radio) => radio.querySelector(".label")?.textContent === "CPU")?.click();
|
|
172
|
+
};
|
|
81
173
|
const reset = () => {
|
|
82
174
|
generation++;
|
|
83
175
|
session = "";
|
|
@@ -86,6 +178,7 @@ function mountPodMonitoring() {
|
|
|
86
178
|
status = "";
|
|
87
179
|
nextRead = 0;
|
|
88
180
|
restore();
|
|
181
|
+
restoreTabs();
|
|
89
182
|
};
|
|
90
183
|
const update = async () => {
|
|
91
184
|
if (stopped) return;
|
|
@@ -113,6 +206,8 @@ function mountPodMonitoring() {
|
|
|
113
206
|
"Network",
|
|
114
207
|
"Filesystem"
|
|
115
208
|
].every((label) => [...element.querySelectorAll(".Radio .label")].some((node) => node.textContent === label)));
|
|
209
|
+
if (metrics && visible(metrics)) restrictTabs(metrics);
|
|
210
|
+
else restoreTabs();
|
|
116
211
|
const selected = metrics?.querySelector(".Radio.checked .label")?.textContent;
|
|
117
212
|
const nativeGraph = metrics?.querySelector(":scope > .graph");
|
|
118
213
|
if (!metrics || !visible(metrics) || !nativeGraph || selected !== "CPU" && selected !== "Memory") {
|
|
@@ -133,11 +228,12 @@ function mountPodMonitoring() {
|
|
|
133
228
|
}
|
|
134
229
|
const render = () => {
|
|
135
230
|
if (host && (renderedSamples !== samples || renderedStatus !== status || renderedMetric !== metric)) {
|
|
136
|
-
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(
|
|
231
|
+
require__global_external_react_dom.default.render(/* @__PURE__ */ require_jsx_runtime.jsx(PodMonitoringChart, {
|
|
232
|
+
clusterId: cluster,
|
|
233
|
+
uid: resourceUid,
|
|
137
234
|
metric,
|
|
138
235
|
samples,
|
|
139
|
-
status
|
|
140
|
-
scope: "pod"
|
|
236
|
+
status
|
|
141
237
|
}), host);
|
|
142
238
|
renderedSamples = samples;
|
|
143
239
|
renderedStatus = status;
|
|
@@ -151,7 +247,9 @@ function mountPodMonitoring() {
|
|
|
151
247
|
nextRead = Date.now() + INTERVAL;
|
|
152
248
|
const isCurrent = () => !stopped && current === generation && session === key && drawer.isConnected && visible(drawer) && document.visibilityState !== "hidden" && require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster && JSON.stringify(selectedPod()) === JSON.stringify(identity);
|
|
153
249
|
try {
|
|
154
|
-
const
|
|
250
|
+
const collected = await require_monitoring_collector.getMonitoringSnapshot();
|
|
251
|
+
if (!isCurrent()) return;
|
|
252
|
+
const [usage, pod] = await Promise.allSettled([collected.podMetrics !== void 0 ? Promise.resolve(collected.podMetrics) : Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsMetricsApi.list({ namespace: identity.namespace }, { fieldSelector: `metadata.name=${identity.name}` })), collected.pods !== void 0 ? Promise.resolve(collected.pods.find((item) => item.metadata.name === identity.name && item.metadata.namespace === identity.namespace)) : Promise.resolve().then(() => require_extensions.Renderer.K8sApi.podsApi.get(identity))]);
|
|
155
253
|
if (!isCurrent()) return;
|
|
156
254
|
const currentPod = pod.status === "fulfilled" && pod.value?.metadata.name === identity.name && pod.value.metadata.namespace === identity.namespace ? pod.value : void 0;
|
|
157
255
|
if (currentPod) {
|
|
@@ -176,9 +274,9 @@ function mountPodMonitoring() {
|
|
|
176
274
|
try {
|
|
177
275
|
const spec = currentPod;
|
|
178
276
|
sample.resources = Object.fromEntries(["cpu", "memory"].map((resource) => {
|
|
179
|
-
const limits =
|
|
277
|
+
const limits = require_monitoring_math.effectivePodResource(spec, resource, "limits");
|
|
180
278
|
return [resource, {
|
|
181
|
-
requests:
|
|
279
|
+
requests: require_monitoring_math.effectivePodResource(spec, resource, "requests"),
|
|
182
280
|
limits: Number.isFinite(limits) ? limits : void 0,
|
|
183
281
|
unbounded: limits === Infinity
|
|
184
282
|
}];
|
|
@@ -205,6 +303,7 @@ function mountPodMonitoring() {
|
|
|
205
303
|
stopped = true;
|
|
206
304
|
reset();
|
|
207
305
|
window.clearInterval(timer);
|
|
306
|
+
nativeSuppression.remove();
|
|
208
307
|
};
|
|
209
308
|
}
|
|
210
309
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pod-monitoring.js","names":[],"sources":["../../src/renderer/pod-monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport {\n effectivePodResource,\n MAX_SAMPLES,\n MetricChart,\n type MonitoringPod,\n type MonitoringSample,\n parseQuantity,\n} from \"./monitoring\";\n\ntype PodIdentity = { name: string; namespace: string };\ntype PodMetrics = {\n metadata: PodIdentity & { uid?: string };\n timestamp: string;\n containers: { name: string; usage: { cpu: string; memory: string } }[];\n};\nconst INTERVAL = 30_000;\n\nexport function aggregatePodMetrics(metrics: PodMetrics, identity: PodIdentity, now = Date.now()): MonitoringSample {\n if (metrics.metadata?.name !== identity.name || metrics.metadata?.namespace !== identity.namespace)\n throw new Error(\"Pod metrics identity changed\");\n const time = Date.parse(metrics.timestamp);\n if (!Number.isFinite(time) || now - time > 120_000 || time > now + 30_000) throw new Error(\"Pod metrics are stale\");\n if (!metrics.containers?.length) throw new Error(\"Missing container metrics\");\n const names = new Set<string>();\n let cpu = 0;\n let memory = 0;\n for (const container of metrics.containers) {\n if (!container.name || names.has(container.name)) throw new Error(\"Invalid container metrics\");\n names.add(container.name);\n cpu += parseQuantity(container.usage.cpu);\n memory += parseQuantity(container.usage.memory, true);\n }\n return { time, cpu, memory };\n}\n\nfunction selectedPod(): PodIdentity | undefined {\n const details = new URLSearchParams(window.location.search).get(\"kube-details\");\n const match = details?.match(/^\\/api\\/v1\\/namespaces\\/([a-z0-9][a-z0-9.-]*)\\/pods\\/([a-z0-9][a-z0-9.-]*)$/);\n if (match) return { namespace: match[1], name: match[2] };\n return;\n}\n\nfunction visible(element: HTMLElement): boolean {\n for (let node: HTMLElement | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (\n node.hidden ||\n node.getAttribute(\"aria-hidden\") === \"true\" ||\n style.display === \"none\" ||\n style.visibility === \"hidden\"\n )\n return false;\n }\n return true;\n}\n\nexport function startPodMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let dispose: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n dispose?.();\n dispose = enabled ? mountPodMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n dispose?.();\n };\n}\n\nfunction mountPodMonitoring(): () => void {\n let stopped = false;\n let generation = 0;\n let session = \"\";\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let resourceUid: string | undefined;\n let nextRead = 0;\n let pending: number | undefined;\n let host: HTMLElement | undefined;\n let graph: HTMLElement | undefined;\n let originalStyle: string | null = null;\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let renderedMetric: string | undefined;\n const restore = () => {\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n }\n host = undefined;\n if (graph) {\n if (originalStyle === null) graph.removeAttribute(\"style\");\n else graph.setAttribute(\"style\", originalStyle);\n }\n graph = undefined;\n renderedSamples = undefined;\n };\n const reset = () => {\n generation++;\n session = \"\";\n samples = [];\n resourceUid = undefined;\n status = \"\";\n nextRead = 0;\n restore();\n };\n const update = async () => {\n if (stopped) return;\n const identity = selectedPod();\n const cluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const drawer = document.querySelector<HTMLElement>(\".KubeObjectDetails\");\n const title = drawer?.querySelector(\".drawer-title-text\");\n const titleText = title\n ? [...title.childNodes]\n .filter((node) => node.nodeType === Node.TEXT_NODE)\n .map((node) => node.textContent)\n .join(\"\")\n .trim()\n : \"\";\n if (\n !identity ||\n !cluster ||\n !drawer ||\n !visible(drawer) ||\n document.visibilityState === \"hidden\" ||\n titleText !== `Pod: ${identity.name}` ||\n !drawer.querySelector(\".PodDetails\")\n ) {\n if (session) reset();\n return;\n }\n const key = JSON.stringify([cluster, identity.namespace, identity.name]);\n if (session !== key) {\n reset();\n session = key;\n }\n const metrics = [...drawer.querySelectorAll<HTMLElement>(\".ResourceMetrics\")].find((element) =>\n [\"CPU\", \"Memory\", \"Network\", \"Filesystem\"].every((label) =>\n [...element.querySelectorAll(\".Radio .label\")].some((node) => node.textContent === label),\n ),\n );\n const selected = metrics?.querySelector(\".Radio.checked .label\")?.textContent;\n const nativeGraph = metrics?.querySelector<HTMLElement>(\":scope > .graph\");\n if (!metrics || !visible(metrics) || !nativeGraph || (selected !== \"CPU\" && selected !== \"Memory\")) {\n restore();\n return;\n }\n const retained = samples.filter((sample) => sample.time >= Date.now() - 30 * 60_000);\n if (retained.length !== samples.length) samples = retained;\n const metric = selected === \"CPU\" ? \"cpu\" : \"memory\";\n if (graph !== nativeGraph || !host?.isConnected) {\n restore();\n graph = nativeGraph;\n originalStyle = graph.getAttribute(\"style\");\n graph.style.display = \"none\";\n host = document.createElement(\"div\");\n host.dataset.powerPodMonitoring = \"\";\n graph.after(host);\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status || renderedMetric !== metric)) {\n ReactDOM.render(<MetricChart metric={metric} samples={samples} status={status} scope=\"pod\" />, host);\n renderedSamples = samples;\n renderedStatus = status;\n renderedMetric = metric;\n }\n };\n render();\n if (pending === generation || Date.now() < nextRead) return;\n const current = generation;\n pending = current;\n nextRead = Date.now() + INTERVAL;\n const isCurrent = () =>\n !stopped &&\n current === generation &&\n session === key &&\n drawer.isConnected &&\n visible(drawer) &&\n document.visibilityState !== \"hidden\" &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster &&\n JSON.stringify(selectedPod()) === JSON.stringify(identity);\n try {\n const [usage, pod] = await Promise.allSettled([\n Promise.resolve().then(() =>\n Renderer.K8sApi.podsMetricsApi.list(\n { namespace: identity.namespace },\n { fieldSelector: `metadata.name=${identity.name}` },\n ),\n ),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.get(identity)),\n ]);\n if (!isCurrent()) return;\n const currentPod =\n pod.status === \"fulfilled\" &&\n pod.value?.metadata.name === identity.name &&\n pod.value.metadata.namespace === identity.namespace\n ? pod.value\n : undefined;\n if (currentPod) {\n const nextUid = currentPod.metadata.uid;\n if (nextUid && resourceUid && nextUid !== resourceUid) samples = [];\n if (nextUid) resourceUid = nextUid;\n }\n if (usage.status !== \"fulfilled\" || !Array.isArray(usage.value)) throw new Error(\"Metrics unavailable\");\n const matches = usage.value.filter(\n (item) => item.metadata?.name === identity.name && item.metadata.namespace === identity.namespace,\n );\n if (matches.length !== 1) throw new Error(\"Metrics unavailable\");\n const podMetrics = matches[0];\n const sample = aggregatePodMetrics(podMetrics, identity);\n if (currentPod) {\n const created = currentPod.metadata.creationTimestamp;\n if (created !== undefined) {\n const creationTime = Date.parse(created);\n if (!Number.isFinite(creationTime)) throw new Error(\"Invalid pod creation time\");\n samples = samples.filter((entry) => entry.time >= creationTime);\n if (sample.time < creationTime) throw new Error(\"Pod metrics predate the current pod\");\n }\n if (podMetrics.metadata.uid && resourceUid && podMetrics.metadata.uid !== resourceUid)\n throw new Error(\"Pod metrics identity changed\");\n try {\n const spec: MonitoringPod = currentPod;\n sample.resources = Object.fromEntries(\n ([\"cpu\", \"memory\"] as const).map((resource) => {\n const limits = effectivePodResource(spec, resource, \"limits\");\n return [\n resource,\n {\n requests: effectivePodResource(spec, resource, \"requests\"),\n limits: Number.isFinite(limits) ? limits : undefined,\n unbounded: limits === Infinity,\n },\n ];\n }),\n ) as MonitoringSample[\"resources\"];\n } catch {\n sample.resourceStatus = \"Pod requests unavailable: incomplete resource data\";\n }\n } else sample.resourceStatus = \"Pod requests unavailable or access denied\";\n samples = [\n ...samples.filter((entry) => entry.time !== sample.time && entry.time >= Date.now() - 30 * 60_000),\n sample,\n ]\n .sort((a, b) => a.time - b.time)\n .slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Pod metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pending === current) pending = undefined;\n }\n if (isCurrent()) void update();\n };\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n reset();\n window.clearInterval(timer);\n };\n}\n"],"mappings":";;;;;;;AAmBA,IAAM,WAAW;AAEjB,SAAgB,oBAAoB,SAAqB,UAAuB,MAAM,KAAK,IAAI,GAAqB;CAClH,IAAI,QAAQ,UAAU,SAAS,SAAS,QAAQ,QAAQ,UAAU,cAAc,SAAS,WACvF,MAAM,IAAI,MAAM,8BAA8B;CAChD,MAAM,OAAO,KAAK,MAAM,QAAQ,SAAS;CACzC,IAAI,CAAC,OAAO,SAAS,IAAI,KAAK,MAAM,OAAO,QAAW,OAAO,MAAM,KAAQ,MAAM,IAAI,MAAM,uBAAuB;CAClH,IAAI,CAAC,QAAQ,YAAY,QAAQ,MAAM,IAAI,MAAM,2BAA2B;CAC5E,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,MAAM;CACV,IAAI,SAAS;CACb,KAAK,MAAM,aAAa,QAAQ,YAAY;EAC1C,IAAI,CAAC,UAAU,QAAQ,MAAM,IAAI,UAAU,IAAI,GAAG,MAAM,IAAI,MAAM,2BAA2B;EAC7F,MAAM,IAAI,UAAU,IAAI;EACxB,OAAO,mBAAA,cAAc,UAAU,MAAM,GAAG;EACxC,UAAU,mBAAA,cAAc,UAAU,MAAM,QAAQ,IAAI;CACtD;CACA,OAAO;EAAE;EAAM;EAAK;CAAO;AAC7B;AAEA,SAAS,cAAuC;CAE9C,MAAM,QADU,IAAI,gBAAgB,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI,cAClD,CAAA,EAAS,MAAM,6EAA6E;CAC1G,IAAI,OAAO,OAAO;EAAE,WAAW,MAAM;EAAI,MAAM,MAAM;CAAG;AAE1D;AAEA,SAAS,QAAQ,SAA+B;CAC9C,KAAK,IAAI,OAA2B,SAAS,MAAM,OAAO,KAAK,eAAe;EAC5E,MAAM,QAAQ,iBAAiB,IAAI;EACnC,IACE,KAAK,UACL,KAAK,aAAa,aAAa,MAAM,UACrC,MAAM,YAAY,UAClB,MAAM,eAAe,UAErB,OAAO;CACX;CACA,OAAO;AACT;AAEA,SAAgB,qBAAiC;CAC/C,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,IAAI;CACJ,MAAM,OAAO,8BAAA,eACL,YAAY,UACjB,YAAY;EACX,UAAU;EACV,UAAU,UAAU,mBAAmB,IAAI,KAAA;CAC7C,GACA,EAAE,iBAAiB,KAAK,CAC1B;CACA,aAAa;EACX,KAAK;EACL,UAAU;CACZ;AACF;AAEA,SAAS,qBAAiC;CACxC,IAAI,UAAU;CACd,IAAI,aAAa;CACjB,IAAI,UAAU;CACd,IAAI,UAA8B,CAAC;CACnC,IAAI,SAAS;CACb,IAAI;CACJ,IAAI,WAAW;CACf,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,gBAA+B;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,gBAAgB;EACpB,IAAI,MAAM;GACR,mCAAA,QAAS,uBAAuB,IAAI;GACpC,KAAK,OAAO;EACd;EACA,OAAO,KAAA;EACP,IAAI,OACF,IAAI,kBAAkB,MAAM,MAAM,gBAAgB,OAAO;OACpD,MAAM,aAAa,SAAS,aAAa;EAEhD,QAAQ,KAAA;EACR,kBAAkB,KAAA;CACpB;CACA,MAAM,cAAc;EAClB;EACA,UAAU;EACV,UAAU,CAAC;EACX,cAAc,KAAA;EACd,SAAS;EACT,WAAW;EACX,QAAQ;CACV;CACA,MAAM,SAAS,YAAY;EACzB,IAAI,SAAS;EACb,MAAM,WAAW,YAAY;EAC7B,MAAM,UAAU,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS;EAC/D,MAAM,SAAS,SAAS,cAA2B,oBAAoB;EACvE,MAAM,QAAQ,QAAQ,cAAc,oBAAoB;EACxD,MAAM,YAAY,QACd,CAAC,GAAG,MAAM,UAAU,CAAC,CAClB,QAAQ,SAAS,KAAK,aAAa,KAAK,SAAS,CAAC,CAClD,KAAK,SAAS,KAAK,WAAW,CAAC,CAC/B,KAAK,EAAE,CAAC,CACR,KAAK,IACR;EACJ,IACE,CAAC,YACD,CAAC,WACD,CAAC,UACD,CAAC,QAAQ,MAAM,KACf,SAAS,oBAAoB,YAC7B,cAAc,QAAQ,SAAS,UAC/B,CAAC,OAAO,cAAc,aAAa,GACnC;GACA,IAAI,SAAS,MAAM;GACnB;EACF;EACA,MAAM,MAAM,KAAK,UAAU;GAAC;GAAS,SAAS;GAAW,SAAS;EAAI,CAAC;EACvE,IAAI,YAAY,KAAK;GACnB,MAAM;GACN,UAAU;EACZ;EACA,MAAM,UAAU,CAAC,GAAG,OAAO,iBAA8B,kBAAkB,CAAC,CAAC,CAAC,MAAM,YAClF;GAAC;GAAO;GAAU;GAAW;EAAY,CAAC,CAAC,OAAO,UAChD,CAAC,GAAG,QAAQ,iBAAiB,eAAe,CAAC,CAAC,CAAC,MAAM,SAAS,KAAK,gBAAgB,KAAK,CAC1F,CACF;EACA,MAAM,WAAW,SAAS,cAAc,uBAAuB,CAAC,EAAE;EAClE,MAAM,cAAc,SAAS,cAA2B,iBAAiB;EACzE,IAAI,CAAC,WAAW,CAAC,QAAQ,OAAO,KAAK,CAAC,eAAgB,aAAa,SAAS,aAAa,UAAW;GAClG,QAAQ;GACR;EACF;EACA,MAAM,WAAW,QAAQ,QAAQ,WAAW,OAAO,QAAQ,KAAK,IAAI,IAAI,KAAK,GAAM;EACnF,IAAI,SAAS,WAAW,QAAQ,QAAQ,UAAU;EAClD,MAAM,SAAS,aAAa,QAAQ,QAAQ;EAC5C,IAAI,UAAU,eAAe,CAAC,MAAM,aAAa;GAC/C,QAAQ;GACR,QAAQ;GACR,gBAAgB,MAAM,aAAa,OAAO;GAC1C,MAAM,MAAM,UAAU;GACtB,OAAO,SAAS,cAAc,KAAK;GACnC,KAAK,QAAQ,qBAAqB;GAClC,MAAM,MAAM,IAAI;EAClB;EACA,MAAM,eAAe;GACnB,IAAI,SAAS,oBAAoB,WAAW,mBAAmB,UAAU,mBAAmB,SAAS;IACnG,mCAAA,QAAS,OAAO,oCAAA,IAAC,mBAAA,aAAD;KAAqB;KAAiB;KAAiB;KAAQ,OAAM;IAAO,CAAA,GAAG,IAAI;IACnG,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;GACnB;EACF;EACA,OAAO;EACP,IAAI,YAAY,cAAc,KAAK,IAAI,IAAI,UAAU;EACrD,MAAM,UAAU;EAChB,UAAU;EACV,WAAW,KAAK,IAAI,IAAI;EACxB,MAAM,kBACJ,CAAC,WACD,YAAY,cACZ,YAAY,OACZ,OAAO,eACP,QAAQ,MAAM,KACd,SAAS,oBAAoB,YAC7B,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS,QAAQ,WACvD,KAAK,UAAU,YAAY,CAAC,MAAM,KAAK,UAAU,QAAQ;EAC3D,IAAI;GACF,MAAM,CAAC,OAAO,OAAO,MAAM,QAAQ,WAAW,CAC5C,QAAQ,QAAQ,CAAC,CAAC,WAChB,mBAAA,SAAS,OAAO,eAAe,KAC7B,EAAE,WAAW,SAAS,UAAU,GAChC,EAAE,eAAe,iBAAiB,SAAS,OAAO,CACpD,CACF,GACA,QAAQ,QAAQ,CAAC,CAAC,WAAW,mBAAA,SAAS,OAAO,QAAQ,IAAI,QAAQ,CAAC,CACpE,CAAC;GACD,IAAI,CAAC,UAAU,GAAG;GAClB,MAAM,aACJ,IAAI,WAAW,eACf,IAAI,OAAO,SAAS,SAAS,SAAS,QACtC,IAAI,MAAM,SAAS,cAAc,SAAS,YACtC,IAAI,QACJ,KAAA;GACN,IAAI,YAAY;IACd,MAAM,UAAU,WAAW,SAAS;IACpC,IAAI,WAAW,eAAe,YAAY,aAAa,UAAU,CAAC;IAClE,IAAI,SAAS,cAAc;GAC7B;GACA,IAAI,MAAM,WAAW,eAAe,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG,MAAM,IAAI,MAAM,qBAAqB;GACtG,MAAM,UAAU,MAAM,MAAM,QACzB,SAAS,KAAK,UAAU,SAAS,SAAS,QAAQ,KAAK,SAAS,cAAc,SAAS,SAC1F;GACA,IAAI,QAAQ,WAAW,GAAG,MAAM,IAAI,MAAM,qBAAqB;GAC/D,MAAM,aAAa,QAAQ;GAC3B,MAAM,SAAS,oBAAoB,YAAY,QAAQ;GACvD,IAAI,YAAY;IACd,MAAM,UAAU,WAAW,SAAS;IACpC,IAAI,YAAY,KAAA,GAAW;KACzB,MAAM,eAAe,KAAK,MAAM,OAAO;KACvC,IAAI,CAAC,OAAO,SAAS,YAAY,GAAG,MAAM,IAAI,MAAM,2BAA2B;KAC/E,UAAU,QAAQ,QAAQ,UAAU,MAAM,QAAQ,YAAY;KAC9D,IAAI,OAAO,OAAO,cAAc,MAAM,IAAI,MAAM,qCAAqC;IACvF;IACA,IAAI,WAAW,SAAS,OAAO,eAAe,WAAW,SAAS,QAAQ,aACxE,MAAM,IAAI,MAAM,8BAA8B;IAChD,IAAI;KACF,MAAM,OAAsB;KAC5B,OAAO,YAAY,OAAO,YACvB,CAAC,OAAO,QAAQ,CAAC,CAAW,KAAK,aAAa;MAC7C,MAAM,SAAS,mBAAA,qBAAqB,MAAM,UAAU,QAAQ;MAC5D,OAAO,CACL,UACA;OACE,UAAU,mBAAA,qBAAqB,MAAM,UAAU,UAAU;OACzD,QAAQ,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;OAC3C,WAAW,WAAW;MACxB,CACF;KACF,CAAC,CACH;IACF,QAAQ;KACN,OAAO,iBAAiB;IAC1B;GACF,OAAO,OAAO,iBAAiB;GAC/B,UAAU,CACR,GAAG,QAAQ,QAAQ,UAAU,MAAM,SAAS,OAAO,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,GAAM,GACjG,MACF,CAAC,CACE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAC/B,MAAM,GAAY;GACrB,SAAS;EACX,QAAQ;GACN,IAAI,CAAC,UAAU,GAAG;GAClB,SAAS;EACX,UAAU;GACR,IAAI,YAAY,SAAS,UAAU,KAAA;EACrC;EACA,IAAI,UAAU,GAAG,OAAY;CAC/B;CACA,MAAM,QAAQ,OAAO,kBAAkB;EACrC,OAAY;CACd,GAAG,GAAI;CACP,OAAY;CACZ,aAAa;EACX,UAAU;EACV,MAAM;EACN,OAAO,cAAc,KAAK;CAC5B;AACF"}
|
|
1
|
+
{"version":3,"file":"pod-monitoring.js","names":[],"sources":["../../src/renderer/pod-monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\nimport {\n effectivePodResource,\n MAX_SAMPLES,\n MetricChart,\n type MonitoringPod,\n type MonitoringSample,\n parseQuantity,\n} from \"./monitoring\";\nimport { getMonitoringSnapshot, retainMonitoringCollector } from \"./monitoring-collector\";\nimport { MonitoringHistoryRange, useMonitoringHistory } from \"./monitoring-history-view\";\n\ntype PodIdentity = { name: string; namespace: string };\ntype PodMetrics = {\n metadata: PodIdentity & { uid?: string };\n timestamp: string;\n containers: { name: string; usage: { cpu: string; memory: string } }[];\n};\nconst INTERVAL = 30_000;\n\nexport function aggregatePodMetrics(metrics: PodMetrics, identity: PodIdentity, now = Date.now()): MonitoringSample {\n if (metrics.metadata?.name !== identity.name || metrics.metadata?.namespace !== identity.namespace)\n throw new Error(\"Pod metrics identity changed\");\n const time = Date.parse(metrics.timestamp);\n if (!Number.isFinite(time) || now - time > 120_000 || time > now + 30_000) throw new Error(\"Pod metrics are stale\");\n if (!metrics.containers?.length) throw new Error(\"Missing container metrics\");\n const names = new Set<string>();\n let cpu = 0;\n let memory = 0;\n for (const container of metrics.containers) {\n if (!container.name || names.has(container.name)) throw new Error(\"Invalid container metrics\");\n names.add(container.name);\n cpu += parseQuantity(container.usage.cpu);\n memory += parseQuantity(container.usage.memory, true);\n }\n return { time, cpu, memory };\n}\n\nfunction PodMonitoringChart({\n clusterId,\n uid,\n samples,\n status,\n metric,\n}: {\n clusterId: string;\n uid?: string;\n samples: MonitoringSample[];\n status: string;\n metric: \"cpu\" | \"memory\";\n}) {\n const history = useMonitoringHistory(uid ? { clusterId, scope: \"pod\", uid } : undefined, samples);\n return (\n <>\n <MonitoringHistoryRange range={history.range} onChange={history.setRange} />\n {history.status && <small role=\"status\">{history.status}</small>}\n <MetricChart metric={metric} samples={history.samples} status={status} scope=\"pod\" />\n </>\n );\n}\n\nfunction selectedPod(): PodIdentity | undefined {\n const details = new URLSearchParams(window.location.search).get(\"kube-details\");\n const match = details?.match(/^\\/api\\/v1\\/namespaces\\/([a-z0-9][a-z0-9.-]*)\\/pods\\/([a-z0-9][a-z0-9.-]*)$/);\n if (match) return { namespace: match[1], name: match[2] };\n return;\n}\n\nfunction visible(element: HTMLElement): boolean {\n for (let node: HTMLElement | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (\n node.hidden ||\n node.getAttribute(\"aria-hidden\") === \"true\" ||\n style.display === \"none\" ||\n style.visibility === \"hidden\"\n )\n return false;\n }\n return true;\n}\n\nexport function startPodMonitoring(): () => void {\n const release = retainMonitoringCollector();\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let dispose: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n dispose?.();\n dispose = enabled ? mountPodMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n dispose?.();\n release();\n };\n}\n\nfunction mountPodMonitoring(): () => void {\n const nativeSuppression = document.createElement(\"style\");\n const controllers =\n \".DeploymentDetails, .ReplicaSetDetails, .DaemonSetDetails, .StatefulSetDetails, .JobDetails, .CronJobDetails, [class*=ReplicationControllerDetails]\";\n nativeSuppression.dataset.powerDetailMonitoring = \"\";\n nativeSuppression.textContent = `\n .KubeObjectDetails:has(:is(${controllers})) .ResourceMetrics,\n .KubeObjectDetails:has(:is(${controllers})) [data-time-range]:has(+ .ResourceMetrics),\n .KubeObjectDetails:has(.PodDetails) [data-time-range]:has(+ .ResourceMetrics > [data-power-pod-monitoring]),\n .KubeObjectDetails .ResourceMetrics:has(> [data-power-pod-monitoring]) > .loader {\n display: none !important;\n }\n `;\n document.head.append(nativeSuppression);\n let stopped = false;\n let generation = 0;\n let session = \"\";\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let resourceUid: string | undefined;\n let nextRead = 0;\n let pending: number | undefined;\n let host: HTMLElement | undefined;\n let graph: HTMLElement | undefined;\n let originalStyle: string | null = null;\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let renderedMetric: string | undefined;\n const restore = () => {\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n }\n host = undefined;\n if (graph) {\n if (originalStyle === null) graph.removeAttribute(\"style\");\n else graph.setAttribute(\"style\", originalStyle);\n }\n graph = undefined;\n renderedSamples = undefined;\n };\n let controlledMetrics: HTMLElement | undefined;\n const tabAttributes = new Map<HTMLElement, Map<string, string | null>>();\n const saveAttributes = (element: HTMLElement, names: string[]) => {\n if (!tabAttributes.has(element))\n tabAttributes.set(element, new Map(names.map((name) => [name, element.getAttribute(name)])));\n };\n const unsupportedTab = (target: EventTarget | null) => {\n const radio = target instanceof Element ? target.closest(\".Radio\") : null;\n return radio && [\"Network\", \"Filesystem\"].includes(radio.querySelector(\".label\")?.textContent ?? \"\");\n };\n const blockUnsupported = (event: Event) => {\n if (!unsupportedTab(event.target)) return;\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n const tabEvents = [\"click\", \"pointerdown\", \"mousedown\", \"keydown\", \"change\"];\n const restoreTabs = () => {\n for (const [element, attributes] of tabAttributes) {\n for (const [name, value] of attributes) {\n if (value === null) element.removeAttribute(name);\n else element.setAttribute(name, value);\n }\n }\n tabAttributes.clear();\n for (const event of tabEvents) controlledMetrics?.removeEventListener(event, blockUnsupported, true);\n controlledMetrics = undefined;\n };\n const restrictTabs = (metrics: HTMLElement) => {\n if (controlledMetrics !== metrics) {\n restoreTabs();\n controlledMetrics = metrics;\n for (const event of tabEvents) metrics.addEventListener(event, blockUnsupported, true);\n }\n for (const radio of metrics.querySelectorAll<HTMLElement>(\".Radio\")) {\n if (!unsupportedTab(radio)) continue;\n saveAttributes(radio, [\"style\", \"hidden\", \"aria-hidden\"]);\n if (radio.style.display !== \"none\") radio.style.display = \"none\";\n radio.hidden = true;\n radio.setAttribute(\"aria-hidden\", \"true\");\n const input = radio.querySelector<HTMLInputElement>(\"input\");\n if (input) {\n saveAttributes(input, [\"disabled\"]);\n input.disabled = true;\n }\n }\n if (unsupportedTab(metrics.querySelector(\".Radio.checked\"))) {\n const cpu = [...metrics.querySelectorAll<HTMLElement>(\".Radio\")].find(\n (radio) => radio.querySelector(\".label\")?.textContent === \"CPU\",\n );\n cpu?.click();\n }\n };\n const reset = () => {\n generation++;\n session = \"\";\n samples = [];\n resourceUid = undefined;\n status = \"\";\n nextRead = 0;\n restore();\n restoreTabs();\n };\n const update = async () => {\n if (stopped) return;\n const identity = selectedPod();\n const cluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const drawer = document.querySelector<HTMLElement>(\".KubeObjectDetails\");\n const title = drawer?.querySelector(\".drawer-title-text\");\n const titleText = title\n ? [...title.childNodes]\n .filter((node) => node.nodeType === Node.TEXT_NODE)\n .map((node) => node.textContent)\n .join(\"\")\n .trim()\n : \"\";\n if (\n !identity ||\n !cluster ||\n !drawer ||\n !visible(drawer) ||\n document.visibilityState === \"hidden\" ||\n titleText !== `Pod: ${identity.name}` ||\n !drawer.querySelector(\".PodDetails\")\n ) {\n if (session) reset();\n return;\n }\n const key = JSON.stringify([cluster, identity.namespace, identity.name]);\n if (session !== key) {\n reset();\n session = key;\n }\n const metrics = [...drawer.querySelectorAll<HTMLElement>(\".ResourceMetrics\")].find((element) =>\n [\"CPU\", \"Memory\", \"Network\", \"Filesystem\"].every((label) =>\n [...element.querySelectorAll(\".Radio .label\")].some((node) => node.textContent === label),\n ),\n );\n if (metrics && visible(metrics)) restrictTabs(metrics);\n else restoreTabs();\n const selected = metrics?.querySelector(\".Radio.checked .label\")?.textContent;\n const nativeGraph = metrics?.querySelector<HTMLElement>(\":scope > .graph\");\n if (!metrics || !visible(metrics) || !nativeGraph || (selected !== \"CPU\" && selected !== \"Memory\")) {\n restore();\n return;\n }\n const retained = samples.filter((sample) => sample.time >= Date.now() - 30 * 60_000);\n if (retained.length !== samples.length) samples = retained;\n const metric = selected === \"CPU\" ? \"cpu\" : \"memory\";\n if (graph !== nativeGraph || !host?.isConnected) {\n restore();\n graph = nativeGraph;\n originalStyle = graph.getAttribute(\"style\");\n graph.style.display = \"none\";\n host = document.createElement(\"div\");\n host.dataset.powerPodMonitoring = \"\";\n graph.after(host);\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status || renderedMetric !== metric)) {\n ReactDOM.render(\n <PodMonitoringChart\n clusterId={cluster}\n uid={resourceUid}\n metric={metric}\n samples={samples}\n status={status}\n />,\n host,\n );\n renderedSamples = samples;\n renderedStatus = status;\n renderedMetric = metric;\n }\n };\n render();\n if (pending === generation || Date.now() < nextRead) return;\n const current = generation;\n pending = current;\n nextRead = Date.now() + INTERVAL;\n const isCurrent = () =>\n !stopped &&\n current === generation &&\n session === key &&\n drawer.isConnected &&\n visible(drawer) &&\n document.visibilityState !== \"hidden\" &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster &&\n JSON.stringify(selectedPod()) === JSON.stringify(identity);\n try {\n const collected = await getMonitoringSnapshot();\n if (!isCurrent()) return;\n const [usage, pod] = await Promise.allSettled([\n collected.podMetrics !== undefined\n ? Promise.resolve(collected.podMetrics)\n : Promise.resolve().then(() =>\n Renderer.K8sApi.podsMetricsApi.list(\n { namespace: identity.namespace },\n { fieldSelector: `metadata.name=${identity.name}` },\n ),\n ),\n collected.pods !== undefined\n ? Promise.resolve(\n collected.pods.find(\n (item) => item.metadata.name === identity.name && item.metadata.namespace === identity.namespace,\n ),\n )\n : Promise.resolve().then(() => Renderer.K8sApi.podsApi.get(identity)),\n ]);\n if (!isCurrent()) return;\n const currentPod =\n pod.status === \"fulfilled\" &&\n pod.value?.metadata.name === identity.name &&\n pod.value.metadata.namespace === identity.namespace\n ? pod.value\n : undefined;\n if (currentPod) {\n const nextUid = currentPod.metadata.uid;\n if (nextUid && resourceUid && nextUid !== resourceUid) samples = [];\n if (nextUid) resourceUid = nextUid;\n }\n if (usage.status !== \"fulfilled\" || !Array.isArray(usage.value)) throw new Error(\"Metrics unavailable\");\n const matches = usage.value.filter(\n (item) => item.metadata?.name === identity.name && item.metadata.namespace === identity.namespace,\n );\n if (matches.length !== 1) throw new Error(\"Metrics unavailable\");\n const podMetrics = matches[0];\n const sample = aggregatePodMetrics(podMetrics, identity);\n if (currentPod) {\n const created = currentPod.metadata.creationTimestamp;\n if (created !== undefined) {\n const creationTime = Date.parse(created);\n if (!Number.isFinite(creationTime)) throw new Error(\"Invalid pod creation time\");\n samples = samples.filter((entry) => entry.time >= creationTime);\n if (sample.time < creationTime) throw new Error(\"Pod metrics predate the current pod\");\n }\n if (podMetrics.metadata.uid && resourceUid && podMetrics.metadata.uid !== resourceUid)\n throw new Error(\"Pod metrics identity changed\");\n try {\n const spec: MonitoringPod = currentPod;\n sample.resources = Object.fromEntries(\n ([\"cpu\", \"memory\"] as const).map((resource) => {\n const limits = effectivePodResource(spec, resource, \"limits\");\n return [\n resource,\n {\n requests: effectivePodResource(spec, resource, \"requests\"),\n limits: Number.isFinite(limits) ? limits : undefined,\n unbounded: limits === Infinity,\n },\n ];\n }),\n ) as MonitoringSample[\"resources\"];\n } catch {\n sample.resourceStatus = \"Pod requests unavailable: incomplete resource data\";\n }\n } else sample.resourceStatus = \"Pod requests unavailable or access denied\";\n samples = [\n ...samples.filter((entry) => entry.time !== sample.time && entry.time >= Date.now() - 30 * 60_000),\n sample,\n ]\n .sort((a, b) => a.time - b.time)\n .slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Pod metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pending === current) pending = undefined;\n }\n if (isCurrent()) void update();\n };\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n reset();\n window.clearInterval(timer);\n nativeSuppression.remove();\n };\n}\n"],"mappings":";;;;;;;;;;AAqBA,IAAM,WAAW;AAEjB,SAAgB,oBAAoB,SAAqB,UAAuB,MAAM,KAAK,IAAI,GAAqB;CAClH,IAAI,QAAQ,UAAU,SAAS,SAAS,QAAQ,QAAQ,UAAU,cAAc,SAAS,WACvF,MAAM,IAAI,MAAM,8BAA8B;CAChD,MAAM,OAAO,KAAK,MAAM,QAAQ,SAAS;CACzC,IAAI,CAAC,OAAO,SAAS,IAAI,KAAK,MAAM,OAAO,QAAW,OAAO,MAAM,KAAQ,MAAM,IAAI,MAAM,uBAAuB;CAClH,IAAI,CAAC,QAAQ,YAAY,QAAQ,MAAM,IAAI,MAAM,2BAA2B;CAC5E,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,MAAM;CACV,IAAI,SAAS;CACb,KAAK,MAAM,aAAa,QAAQ,YAAY;EAC1C,IAAI,CAAC,UAAU,QAAQ,MAAM,IAAI,UAAU,IAAI,GAAG,MAAM,IAAI,MAAM,2BAA2B;EAC7F,MAAM,IAAI,UAAU,IAAI;EACxB,OAAO,wBAAA,cAAc,UAAU,MAAM,GAAG;EACxC,UAAU,wBAAA,cAAc,UAAU,MAAM,QAAQ,IAAI;CACtD;CACA,OAAO;EAAE;EAAM;EAAK;CAAO;AAC7B;AAEA,SAAS,mBAAmB,EAC1B,WACA,KACA,SACA,QACA,UAOC;CACD,MAAM,UAAU,gCAAA,qBAAqB,MAAM;EAAE;EAAW,OAAO;EAAO;CAAI,IAAI,KAAA,GAAW,OAAO;CAChG,OACE,oCAAA,KAAA,oBAAA,UAAA,EAAA,UAAA;EACE,oCAAA,IAAC,gCAAA,wBAAD;GAAwB,OAAO,QAAQ;GAAO,UAAU,QAAQ;EAAW,CAAA;EAC1E,QAAQ,UAAU,oCAAA,IAAC,SAAD;GAAO,MAAK;aAAU,QAAQ;EAAc,CAAA;EAC/D,oCAAA,IAAC,mBAAA,aAAD;GAAqB;GAAQ,SAAS,QAAQ;GAAiB;GAAQ,OAAM;EAAO,CAAA;CACpF,EAAA,CAAA;AAEN;AAEA,SAAS,cAAuC;CAE9C,MAAM,QADU,IAAI,gBAAgB,OAAO,SAAS,MAAM,CAAC,CAAC,IAAI,cAClD,CAAA,EAAS,MAAM,6EAA6E;CAC1G,IAAI,OAAO,OAAO;EAAE,WAAW,MAAM;EAAI,MAAM,MAAM;CAAG;AAE1D;AAEA,SAAS,QAAQ,SAA+B;CAC9C,KAAK,IAAI,OAA2B,SAAS,MAAM,OAAO,KAAK,eAAe;EAC5E,MAAM,QAAQ,iBAAiB,IAAI;EACnC,IACE,KAAK,UACL,KAAK,aAAa,aAAa,MAAM,UACrC,MAAM,YAAY,UAClB,MAAM,eAAe,UAErB,OAAO;CACX;CACA,OAAO;AACT;AAEA,SAAgB,qBAAiC;CAC/C,MAAM,UAAU,6BAAA,0BAA0B;CAC1C,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,IAAI;CACJ,MAAM,OAAO,8BAAA,eACL,YAAY,UACjB,YAAY;EACX,UAAU;EACV,UAAU,UAAU,mBAAmB,IAAI,KAAA;CAC7C,GACA,EAAE,iBAAiB,KAAK,CAC1B;CACA,aAAa;EACX,KAAK;EACL,UAAU;EACV,QAAQ;CACV;AACF;AAEA,SAAS,qBAAiC;CACxC,MAAM,oBAAoB,SAAS,cAAc,OAAO;CACxD,MAAM,cACJ;CACF,kBAAkB,QAAQ,wBAAwB;CAClD,kBAAkB,cAAc;iCACD,YAAY;iCACZ,YAAY;;;;;;CAM3C,SAAS,KAAK,OAAO,iBAAiB;CACtC,IAAI,UAAU;CACd,IAAI,aAAa;CACjB,IAAI,UAAU;CACd,IAAI,UAA8B,CAAC;CACnC,IAAI,SAAS;CACb,IAAI;CACJ,IAAI,WAAW;CACf,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,gBAA+B;CACnC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,gBAAgB;EACpB,IAAI,MAAM;GACR,mCAAA,QAAS,uBAAuB,IAAI;GACpC,KAAK,OAAO;EACd;EACA,OAAO,KAAA;EACP,IAAI,OACF,IAAI,kBAAkB,MAAM,MAAM,gBAAgB,OAAO;OACpD,MAAM,aAAa,SAAS,aAAa;EAEhD,QAAQ,KAAA;EACR,kBAAkB,KAAA;CACpB;CACA,IAAI;CACJ,MAAM,gCAAgB,IAAI,IAA6C;CACvE,MAAM,kBAAkB,SAAsB,UAAoB;EAChE,IAAI,CAAC,cAAc,IAAI,OAAO,GAC5B,cAAc,IAAI,SAAS,IAAI,IAAI,MAAM,KAAK,SAAS,CAAC,MAAM,QAAQ,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC;CAC/F;CACA,MAAM,kBAAkB,WAA+B;EACrD,MAAM,QAAQ,kBAAkB,UAAU,OAAO,QAAQ,QAAQ,IAAI;EACrE,OAAO,SAAS,CAAC,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,cAAc,QAAQ,CAAC,EAAE,eAAe,EAAE;CACrG;CACA,MAAM,oBAAoB,UAAiB;EACzC,IAAI,CAAC,eAAe,MAAM,MAAM,GAAG;EACnC,MAAM,eAAe;EACrB,MAAM,yBAAyB;CACjC;CACA,MAAM,YAAY;EAAC;EAAS;EAAe;EAAa;EAAW;CAAQ;CAC3E,MAAM,oBAAoB;EACxB,KAAK,MAAM,CAAC,SAAS,eAAe,eAClC,KAAK,MAAM,CAAC,MAAM,UAAU,YAC1B,IAAI,UAAU,MAAM,QAAQ,gBAAgB,IAAI;OAC3C,QAAQ,aAAa,MAAM,KAAK;EAGzC,cAAc,MAAM;EACpB,KAAK,MAAM,SAAS,WAAW,mBAAmB,oBAAoB,OAAO,kBAAkB,IAAI;EACnG,oBAAoB,KAAA;CACtB;CACA,MAAM,gBAAgB,YAAyB;EAC7C,IAAI,sBAAsB,SAAS;GACjC,YAAY;GACZ,oBAAoB;GACpB,KAAK,MAAM,SAAS,WAAW,QAAQ,iBAAiB,OAAO,kBAAkB,IAAI;EACvF;EACA,KAAK,MAAM,SAAS,QAAQ,iBAA8B,QAAQ,GAAG;GACnE,IAAI,CAAC,eAAe,KAAK,GAAG;GAC5B,eAAe,OAAO;IAAC;IAAS;IAAU;GAAa,CAAC;GACxD,IAAI,MAAM,MAAM,YAAY,QAAQ,MAAM,MAAM,UAAU;GAC1D,MAAM,SAAS;GACf,MAAM,aAAa,eAAe,MAAM;GACxC,MAAM,QAAQ,MAAM,cAAgC,OAAO;GAC3D,IAAI,OAAO;IACT,eAAe,OAAO,CAAC,UAAU,CAAC;IAClC,MAAM,WAAW;GACnB;EACF;EACA,IAAI,eAAe,QAAQ,cAAc,gBAAgB,CAAC,GAIxD,CAHa,GAAG,QAAQ,iBAA8B,QAAQ,CAAC,CAAC,CAAC,MAC9D,UAAU,MAAM,cAAc,QAAQ,CAAC,EAAE,gBAAgB,KAE5D,CAAA,EAAK,MAAM;CAEf;CACA,MAAM,cAAc;EAClB;EACA,UAAU;EACV,UAAU,CAAC;EACX,cAAc,KAAA;EACd,SAAS;EACT,WAAW;EACX,QAAQ;EACR,YAAY;CACd;CACA,MAAM,SAAS,YAAY;EACzB,IAAI,SAAS;EACb,MAAM,WAAW,YAAY;EAC7B,MAAM,UAAU,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS;EAC/D,MAAM,SAAS,SAAS,cAA2B,oBAAoB;EACvE,MAAM,QAAQ,QAAQ,cAAc,oBAAoB;EACxD,MAAM,YAAY,QACd,CAAC,GAAG,MAAM,UAAU,CAAC,CAClB,QAAQ,SAAS,KAAK,aAAa,KAAK,SAAS,CAAC,CAClD,KAAK,SAAS,KAAK,WAAW,CAAC,CAC/B,KAAK,EAAE,CAAC,CACR,KAAK,IACR;EACJ,IACE,CAAC,YACD,CAAC,WACD,CAAC,UACD,CAAC,QAAQ,MAAM,KACf,SAAS,oBAAoB,YAC7B,cAAc,QAAQ,SAAS,UAC/B,CAAC,OAAO,cAAc,aAAa,GACnC;GACA,IAAI,SAAS,MAAM;GACnB;EACF;EACA,MAAM,MAAM,KAAK,UAAU;GAAC;GAAS,SAAS;GAAW,SAAS;EAAI,CAAC;EACvE,IAAI,YAAY,KAAK;GACnB,MAAM;GACN,UAAU;EACZ;EACA,MAAM,UAAU,CAAC,GAAG,OAAO,iBAA8B,kBAAkB,CAAC,CAAC,CAAC,MAAM,YAClF;GAAC;GAAO;GAAU;GAAW;EAAY,CAAC,CAAC,OAAO,UAChD,CAAC,GAAG,QAAQ,iBAAiB,eAAe,CAAC,CAAC,CAAC,MAAM,SAAS,KAAK,gBAAgB,KAAK,CAC1F,CACF;EACA,IAAI,WAAW,QAAQ,OAAO,GAAG,aAAa,OAAO;OAChD,YAAY;EACjB,MAAM,WAAW,SAAS,cAAc,uBAAuB,CAAC,EAAE;EAClE,MAAM,cAAc,SAAS,cAA2B,iBAAiB;EACzE,IAAI,CAAC,WAAW,CAAC,QAAQ,OAAO,KAAK,CAAC,eAAgB,aAAa,SAAS,aAAa,UAAW;GAClG,QAAQ;GACR;EACF;EACA,MAAM,WAAW,QAAQ,QAAQ,WAAW,OAAO,QAAQ,KAAK,IAAI,IAAI,KAAK,GAAM;EACnF,IAAI,SAAS,WAAW,QAAQ,QAAQ,UAAU;EAClD,MAAM,SAAS,aAAa,QAAQ,QAAQ;EAC5C,IAAI,UAAU,eAAe,CAAC,MAAM,aAAa;GAC/C,QAAQ;GACR,QAAQ;GACR,gBAAgB,MAAM,aAAa,OAAO;GAC1C,MAAM,MAAM,UAAU;GACtB,OAAO,SAAS,cAAc,KAAK;GACnC,KAAK,QAAQ,qBAAqB;GAClC,MAAM,MAAM,IAAI;EAClB;EACA,MAAM,eAAe;GACnB,IAAI,SAAS,oBAAoB,WAAW,mBAAmB,UAAU,mBAAmB,SAAS;IACnG,mCAAA,QAAS,OACP,oCAAA,IAAC,oBAAD;KACE,WAAW;KACX,KAAK;KACG;KACC;KACD;IACT,CAAA,GACD,IACF;IACA,kBAAkB;IAClB,iBAAiB;IACjB,iBAAiB;GACnB;EACF;EACA,OAAO;EACP,IAAI,YAAY,cAAc,KAAK,IAAI,IAAI,UAAU;EACrD,MAAM,UAAU;EAChB,UAAU;EACV,WAAW,KAAK,IAAI,IAAI;EACxB,MAAM,kBACJ,CAAC,WACD,YAAY,cACZ,YAAY,OACZ,OAAO,eACP,QAAQ,MAAM,KACd,SAAS,oBAAoB,YAC7B,mBAAA,SAAS,QAAQ,cAAc,IAAI,CAAC,EAAE,SAAS,QAAQ,WACvD,KAAK,UAAU,YAAY,CAAC,MAAM,KAAK,UAAU,QAAQ;EAC3D,IAAI;GACF,MAAM,YAAY,MAAM,6BAAA,sBAAsB;GAC9C,IAAI,CAAC,UAAU,GAAG;GAClB,MAAM,CAAC,OAAO,OAAO,MAAM,QAAQ,WAAW,CAC5C,UAAU,eAAe,KAAA,IACrB,QAAQ,QAAQ,UAAU,UAAU,IACpC,QAAQ,QAAQ,CAAC,CAAC,WAChB,mBAAA,SAAS,OAAO,eAAe,KAC7B,EAAE,WAAW,SAAS,UAAU,GAChC,EAAE,eAAe,iBAAiB,SAAS,OAAO,CACpD,CACF,GACJ,UAAU,SAAS,KAAA,IACf,QAAQ,QACN,UAAU,KAAK,MACZ,SAAS,KAAK,SAAS,SAAS,SAAS,QAAQ,KAAK,SAAS,cAAc,SAAS,SACzF,CACF,IACA,QAAQ,QAAQ,CAAC,CAAC,WAAW,mBAAA,SAAS,OAAO,QAAQ,IAAI,QAAQ,CAAC,CACxE,CAAC;GACD,IAAI,CAAC,UAAU,GAAG;GAClB,MAAM,aACJ,IAAI,WAAW,eACf,IAAI,OAAO,SAAS,SAAS,SAAS,QACtC,IAAI,MAAM,SAAS,cAAc,SAAS,YACtC,IAAI,QACJ,KAAA;GACN,IAAI,YAAY;IACd,MAAM,UAAU,WAAW,SAAS;IACpC,IAAI,WAAW,eAAe,YAAY,aAAa,UAAU,CAAC;IAClE,IAAI,SAAS,cAAc;GAC7B;GACA,IAAI,MAAM,WAAW,eAAe,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG,MAAM,IAAI,MAAM,qBAAqB;GACtG,MAAM,UAAU,MAAM,MAAM,QACzB,SAAS,KAAK,UAAU,SAAS,SAAS,QAAQ,KAAK,SAAS,cAAc,SAAS,SAC1F;GACA,IAAI,QAAQ,WAAW,GAAG,MAAM,IAAI,MAAM,qBAAqB;GAC/D,MAAM,aAAa,QAAQ;GAC3B,MAAM,SAAS,oBAAoB,YAAY,QAAQ;GACvD,IAAI,YAAY;IACd,MAAM,UAAU,WAAW,SAAS;IACpC,IAAI,YAAY,KAAA,GAAW;KACzB,MAAM,eAAe,KAAK,MAAM,OAAO;KACvC,IAAI,CAAC,OAAO,SAAS,YAAY,GAAG,MAAM,IAAI,MAAM,2BAA2B;KAC/E,UAAU,QAAQ,QAAQ,UAAU,MAAM,QAAQ,YAAY;KAC9D,IAAI,OAAO,OAAO,cAAc,MAAM,IAAI,MAAM,qCAAqC;IACvF;IACA,IAAI,WAAW,SAAS,OAAO,eAAe,WAAW,SAAS,QAAQ,aACxE,MAAM,IAAI,MAAM,8BAA8B;IAChD,IAAI;KACF,MAAM,OAAsB;KAC5B,OAAO,YAAY,OAAO,YACvB,CAAC,OAAO,QAAQ,CAAC,CAAW,KAAK,aAAa;MAC7C,MAAM,SAAS,wBAAA,qBAAqB,MAAM,UAAU,QAAQ;MAC5D,OAAO,CACL,UACA;OACE,UAAU,wBAAA,qBAAqB,MAAM,UAAU,UAAU;OACzD,QAAQ,OAAO,SAAS,MAAM,IAAI,SAAS,KAAA;OAC3C,WAAW,WAAW;MACxB,CACF;KACF,CAAC,CACH;IACF,QAAQ;KACN,OAAO,iBAAiB;IAC1B;GACF,OAAO,OAAO,iBAAiB;GAC/B,UAAU,CACR,GAAG,QAAQ,QAAQ,UAAU,MAAM,SAAS,OAAO,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,GAAM,GACjG,MACF,CAAC,CACE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAC/B,MAAM,GAAY;GACrB,SAAS;EACX,QAAQ;GACN,IAAI,CAAC,UAAU,GAAG;GAClB,SAAS;EACX,UAAU;GACR,IAAI,YAAY,SAAS,UAAU,KAAA;EACrC;EACA,IAAI,UAAU,GAAG,OAAY;CAC/B;CACA,MAAM,QAAQ,OAAO,kBAAkB;EACrC,OAAY;CACd,GAAG,GAAI;CACP,OAAY;CACZ,aAAa;EACX,UAAU;EACV,MAAM;EACN,OAAO,cAAc,KAAK;EAC1B,kBAAkB,OAAO;CAC3B;AACF"}
|
|
@@ -1,20 +1,65 @@
|
|
|
1
1
|
const require_extensions = require("../_virtual/_global-external_@freelensapp/extensions.js");
|
|
2
2
|
const require__global_external_mobx_react = require("../_virtual/_global-external_mobx-react.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_jsx_runtime = require("../_virtual/_global-external_react/jsx-runtime.js");
|
|
6
|
+
const require_monitoring_history = require("../monitoring-history.js");
|
|
5
7
|
//#region src/renderer/preferences/monitoring-preferences.tsx
|
|
6
8
|
var MonitoringPreferences = require__global_external_mobx_react.observer(() => {
|
|
7
9
|
const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
10
|
+
const [status, setStatus] = require__global_external_react.useState();
|
|
11
|
+
const refresh = () => {
|
|
12
|
+
require_monitoring_history.monitoringHistory.status().then(setStatus).catch(() => setStatus({
|
|
13
|
+
available: false,
|
|
14
|
+
enabled: preferences.historyEnabled,
|
|
15
|
+
bytes: 0,
|
|
16
|
+
samples: 0,
|
|
17
|
+
error: "History service unavailable"
|
|
18
|
+
}));
|
|
19
|
+
};
|
|
20
|
+
require__global_external_react.useEffect(refresh, [preferences.historyEnabled]);
|
|
21
|
+
return /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [
|
|
22
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
|
|
23
|
+
checked: preferences.enabled,
|
|
24
|
+
onChange: (value) => {
|
|
25
|
+
preferences.enabled = value;
|
|
26
|
+
},
|
|
27
|
+
children: "Metrics-server monitoring"
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
30
|
+
className: "power-settings-hint",
|
|
31
|
+
children: "Show cluster, node and pod CPU/memory charts from metrics-server. Live samples are kept for 30 minutes. Requires permission to read the corresponding resource metrics. Requests and capacity use Kubernetes resource data."
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
|
|
34
|
+
checked: preferences.historyEnabled,
|
|
35
|
+
onChange: (value) => {
|
|
36
|
+
preferences.historyEnabled = value;
|
|
37
|
+
},
|
|
38
|
+
children: "Keep local numeric monitoring history"
|
|
39
|
+
}),
|
|
40
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
41
|
+
className: "power-settings-hint",
|
|
42
|
+
children: "Opt-in local SQLite history. Retained for up to 7 days within 100 MiB. Collection runs while monitoring is enabled and the cluster view is active. Track individual pods or workloads in Diagnostics. Resolution: 30 seconds for 2 hours, 5 minutes for 24 hours, 30 minutes for 7 days. No data is backfilled. Disabling collection preserves existing history until cleared."
|
|
43
|
+
}),
|
|
44
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("div", {
|
|
45
|
+
role: "status",
|
|
46
|
+
children: status?.error ?? (status ? `${(status.bytes / 1024 / 1024).toFixed(2)} MiB, ${status.samples} stored metric buckets` : "Checking history storage…")
|
|
47
|
+
}),
|
|
48
|
+
/* @__PURE__ */ require_jsx_runtime.jsx("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
title: "Delete locally collected metric history without changing other settings",
|
|
51
|
+
onClick: () => {
|
|
52
|
+
require_monitoring_history.monitoringHistory.clear().then(refresh).catch(() => setStatus({
|
|
53
|
+
available: false,
|
|
54
|
+
enabled: preferences.historyEnabled,
|
|
55
|
+
bytes: 0,
|
|
56
|
+
samples: 0,
|
|
57
|
+
error: "Unable to clear local history"
|
|
58
|
+
}));
|
|
59
|
+
},
|
|
60
|
+
children: "Clear local history"
|
|
61
|
+
})
|
|
62
|
+
] });
|
|
18
63
|
});
|
|
19
64
|
//#endregion
|
|
20
65
|
exports.MonitoringPreferences = MonitoringPreferences;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitoring-preferences.js","names":[],"sources":["../../../src/renderer/preferences/monitoring-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { MonitoringPreferencesStore } from \"../../common/store/monitoring-preferences-store\";\n\nexport const MonitoringPreferences = observer(() => {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n return (\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Metrics-server
|
|
1
|
+
{"version":3,"file":"monitoring-preferences.js","names":[],"sources":["../../../src/renderer/preferences/monitoring-preferences.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { observer } from \"mobx-react\";\nimport { useEffect, useState } from \"react\";\nimport { type HistoryStatus } from \"../../common/monitoring-history\";\nimport { MonitoringPreferencesStore } from \"../../common/store/monitoring-preferences-store\";\nimport { monitoringHistory } from \"../monitoring-history\";\n\nexport const MonitoringPreferences = observer(() => {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n const [status, setStatus] = useState<HistoryStatus>();\n const refresh = () => {\n void monitoringHistory\n .status()\n .then(setStatus)\n .catch(() =>\n setStatus({\n available: false,\n enabled: preferences.historyEnabled,\n bytes: 0,\n samples: 0,\n error: \"History service unavailable\",\n }),\n );\n };\n useEffect(refresh, [preferences.historyEnabled]);\n return (\n <div>\n <Renderer.Component.Switch\n checked={preferences.enabled}\n onChange={(value) => {\n preferences.enabled = value;\n }}\n >\n Metrics-server monitoring\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Show cluster, node and pod CPU/memory charts from metrics-server. Live samples are kept for 30 minutes. Requires\n permission to read the corresponding resource metrics. Requests and capacity use Kubernetes resource data.\n </div>\n <Renderer.Component.Switch\n checked={preferences.historyEnabled}\n onChange={(value) => {\n preferences.historyEnabled = value;\n }}\n >\n Keep local numeric monitoring history\n </Renderer.Component.Switch>\n <div className=\"power-settings-hint\">\n Opt-in local SQLite history. Retained for up to 7 days within 100 MiB. Collection runs while monitoring is\n enabled and the cluster view is active. Track individual pods or workloads in Diagnostics. Resolution: 30\n seconds for 2 hours, 5 minutes for 24 hours, 30 minutes for 7 days. No data is backfilled. Disabling collection\n preserves existing history until cleared.\n </div>\n <div role=\"status\">\n {status?.error ??\n (status\n ? `${(status.bytes / 1024 / 1024).toFixed(2)} MiB, ${status.samples} stored metric buckets`\n : \"Checking history storage…\")}\n </div>\n <button\n type=\"button\"\n title=\"Delete locally collected metric history without changing other settings\"\n onClick={() => {\n void monitoringHistory\n .clear()\n .then(refresh)\n .catch(() =>\n setStatus({\n available: false,\n enabled: preferences.historyEnabled,\n bytes: 0,\n samples: 0,\n error: \"Unable to clear local history\",\n }),\n );\n }}\n >\n Clear local history\n </button>\n </div>\n );\n});\n"],"mappings":";;;;;;;AAOA,IAAa,wBAAwB,oCAAA,eAAe;CAClD,MAAM,cAAc,qCAAA,2BAA2B,oBAAgD;CAC/F,MAAM,CAAC,QAAQ,aAAa,+BAAA,SAAwB;CACpD,MAAM,gBAAgB;EACpB,2BAAK,kBACF,OAAO,CAAC,CACR,KAAK,SAAS,CAAC,CACf,YACC,UAAU;GACR,WAAW;GACX,SAAS,YAAY;GACrB,OAAO;GACP,SAAS;GACT,OAAO;EACT,CAAC,CACH;CACJ;CACA,+BAAA,UAAU,SAAS,CAAC,YAAY,cAAc,CAAC;CAC/C,OACE,oCAAA,KAAC,OAAD,EAAA,UAAA;EACE,oCAAA,IAAC,mBAAA,SAAS,UAAU,QAApB;GACE,SAAS,YAAY;GACrB,WAAW,UAAU;IACnB,YAAY,UAAU;GACxB;aACD;EAE0B,CAAA;EAC3B,oCAAA,IAAC,OAAD;GAAK,WAAU;aAAsB;EAGhC,CAAA;EACL,oCAAA,IAAC,mBAAA,SAAS,UAAU,QAApB;GACE,SAAS,YAAY;GACrB,WAAW,UAAU;IACnB,YAAY,iBAAiB;GAC/B;aACD;EAE0B,CAAA;EAC3B,oCAAA,IAAC,OAAD;GAAK,WAAU;aAAsB;EAKhC,CAAA;EACL,oCAAA,IAAC,OAAD;GAAK,MAAK;aACP,QAAQ,UACN,SACG,IAAI,OAAO,QAAQ,OAAO,KAAA,CAAM,QAAQ,CAAC,EAAE,QAAQ,OAAO,QAAQ,0BAClE;EACH,CAAA;EACL,oCAAA,IAAC,UAAD;GACE,MAAK;GACL,OAAM;GACN,eAAe;IACb,2BAAK,kBACF,MAAM,CAAC,CACP,KAAK,OAAO,CAAC,CACb,YACC,UAAU;KACR,WAAW;KACX,SAAS,YAAY;KACrB,OAAO;KACP,SAAS;KACT,OAAO;IACT,CAAC,CACH;GACJ;aACD;EAEO,CAAA;CACL,EAAA,CAAA;AAET,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/common/monitoring-history.ts
|
|
2
|
+
var MONITORING_HISTORY_CHANNEL = "freelens-power:monitoring-history";
|
|
3
|
+
function validHistoryIdentity(value) {
|
|
4
|
+
return !!value && [value.clusterId, value.uid].every((id) => typeof id === "string" && /^[a-zA-Z0-9_.:-]{1,128}$/.test(id)) && [
|
|
5
|
+
"cluster",
|
|
6
|
+
"node",
|
|
7
|
+
"pod",
|
|
8
|
+
"workload"
|
|
9
|
+
].includes(value.scope);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
exports.MONITORING_HISTORY_CHANNEL = MONITORING_HISTORY_CHANNEL;
|
|
13
|
+
exports.validHistoryIdentity = validHistoryIdentity;
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=monitoring-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitoring-history.js","names":[],"sources":["../../../../src/common/monitoring-history.ts"],"sourcesContent":["export const MONITORING_HISTORY_CHANNEL = \"freelens-power:monitoring-history\";\nexport const HISTORY_RETENTION = 7 * 24 * 60 * 60 * 1000;\nexport const HISTORY_MAX_BYTES = 100 * 1024 * 1024;\nexport type HistoryIdentity = {\n clusterId: string;\n scope: \"cluster\" | \"node\" | \"pod\" | \"workload\";\n uid: string;\n};\nexport const HISTORY_METRICS = [\n \"cpu\",\n \"memory\",\n \"cpuRequests\",\n \"memoryRequests\",\n \"cpuLimits\",\n \"memoryLimits\",\n \"cpuCapacity\",\n \"memoryCapacity\",\n \"cpuAllocatable\",\n \"memoryAllocatable\",\n \"restarts\",\n] as const;\nexport type HistoryMetric = (typeof HISTORY_METRICS)[number];\nexport type HistorySample = HistoryIdentity & { time: number; values: Partial<Record<HistoryMetric, number>> };\nexport type HistoryQuery = HistoryIdentity & { from: number; to: number };\nexport type HistoryPoint = {\n time: number;\n resolution: number;\n metric: HistoryMetric;\n min: number;\n avg: number;\n max: number;\n count: number;\n gap: boolean;\n};\nexport type HistoryStatus = { available: boolean; enabled: boolean; bytes: number; samples: number; error?: string };\nexport type HistoryRequest =\n | { operation: \"status\" | \"clear\" }\n | { operation: \"append\"; samples: HistorySample[] }\n | { operation: \"query\"; query: HistoryQuery };\nexport function validHistoryIdentity(value: HistoryIdentity): boolean {\n return (\n !!value &&\n [value.clusterId, value.uid].every((id) => typeof id === \"string\" && /^[a-zA-Z0-9_.:-]{1,128}$/.test(id)) &&\n [\"cluster\", \"node\", \"pod\", \"workload\"].includes(value.scope)\n );\n}\nexport function projectHistorySamples(input: unknown, now = Date.now()): HistorySample[] {\n if (!Array.isArray(input) || input.length > 512) throw new Error(\"Invalid history samples\");\n return input.map((sample) => {\n if (\n !validHistoryIdentity(sample) ||\n !Number.isSafeInteger(sample.time) ||\n sample.time < now - HISTORY_RETENTION ||\n sample.time > now + 30_000 ||\n !sample.values ||\n typeof sample.values !== \"object\"\n )\n throw new Error(\"Invalid history sample\");\n const values: HistorySample[\"values\"] = {};\n for (const metric of HISTORY_METRICS) {\n const value = sample.values[metric];\n if (value === undefined) continue;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < 0 || value > 1e30)\n throw new Error(\"Invalid history value\");\n values[metric] = value;\n }\n return { clusterId: sample.clusterId, scope: sample.scope, uid: sample.uid, time: sample.time, values };\n });\n}\n"],"mappings":";AAAA,IAAa,6BAA6B;AAuC1C,SAAgB,qBAAqB,OAAiC;CACpE,OACE,CAAC,CAAC,SACF,CAAC,MAAM,WAAW,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,OAAO,OAAO,YAAY,2BAA2B,KAAK,EAAE,CAAC,KACxG;EAAC;EAAW;EAAQ;EAAO;CAAU,CAAC,CAAC,SAAS,MAAM,KAAK;AAE/D"}
|