@64mb/freelens-power 1.0.2 → 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.
|
@@ -197,7 +197,8 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
197
197
|
}[key],
|
|
198
198
|
value: (sample) => sample.resources?.[metric][key]
|
|
199
199
|
}))];
|
|
200
|
-
const
|
|
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;
|
|
201
202
|
const end = last?.time ?? Date.now();
|
|
202
203
|
const start = samples[0]?.time ?? end;
|
|
203
204
|
const duration = Math.max(end - start, INTERVAL);
|
|
@@ -297,7 +298,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
297
298
|
fontSize: "11",
|
|
298
299
|
children: format(max * fraction)
|
|
299
300
|
})] }, fraction)),
|
|
300
|
-
|
|
301
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsx("path", {
|
|
301
302
|
"data-monitoring-series": line.key,
|
|
302
303
|
d: samples.reduce((path, sample, index) => {
|
|
303
304
|
const value = line.value(sample);
|
|
@@ -362,7 +363,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
362
363
|
},
|
|
363
364
|
children: [
|
|
364
365
|
/* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [formatMonitoringTime(selected.time), pinned ? " · Pinned" : ""] }),
|
|
365
|
-
|
|
366
|
+
visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
366
367
|
style: { color: line.color },
|
|
367
368
|
children: [
|
|
368
369
|
line.label,
|
|
@@ -388,7 +389,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
388
389
|
gap: "6px 16px",
|
|
389
390
|
marginBottom: 8
|
|
390
391
|
},
|
|
391
|
-
children:
|
|
392
|
+
children: visibleSeries.map((line) => /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [
|
|
392
393
|
/* @__PURE__ */ require_jsx_runtime.jsx("span", {
|
|
393
394
|
style: { color: line.color },
|
|
394
395
|
children: "●"
|
|
@@ -405,7 +406,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
405
406
|
},
|
|
406
407
|
children: last.resourceStatus
|
|
407
408
|
}),
|
|
408
|
-
/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
409
|
+
(scope === "node" || status || !samples.length) && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
|
|
409
410
|
style: {
|
|
410
411
|
fontSize: 11,
|
|
411
412
|
color: "var(--textColorSecondary)",
|
|
@@ -415,7 +416,7 @@ function MetricChart({ metric, samples, status, scope = "node" }) {
|
|
|
415
416
|
},
|
|
416
417
|
role: "status",
|
|
417
418
|
"aria-label": `${scope} metrics status`,
|
|
418
|
-
children: [/* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
419
|
+
children: [scope === "node" && /* @__PURE__ */ require_jsx_runtime.jsx("sup", {
|
|
419
420
|
"aria-hidden": "true",
|
|
420
421
|
style: { fontSize: 12 },
|
|
421
422
|
children: "*"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\n\ntype ResourceValues = { requests?: Partial<Record<string, string>>; limits?: Partial<Record<string, string>> };\ntype ResourceContainer = { resources?: ResourceValues; restartPolicy?: string };\nexport type MonitoringPod = {\n spec?: {\n nodeName?: string;\n containers?: ResourceContainer[];\n initContainers?: ResourceContainer[];\n overhead?: Partial<Record<string, string>>;\n resources?: ResourceValues;\n };\n status?: { phase?: string };\n};\nexport type MonitoringNode = {\n metadata: { name: string };\n status?: { capacity?: Partial<Record<string, string>>; allocatable?: Partial<Record<string, string>> };\n};\ntype ResourceTotals = {\n capacity?: number;\n allocatable?: number;\n requests?: number;\n limits?: number;\n unbounded?: boolean;\n};\nexport type MonitoringSample = {\n time: number;\n cpu: number;\n memory: number;\n resources?: Record<\"cpu\" | \"memory\", ResourceTotals>;\n resourceStatus?: string;\n completeUsage?: boolean;\n};\n\nexport function effectivePodResource(\n pod: MonitoringPod,\n metric: \"cpu\" | \"memory\",\n kind: \"requests\" | \"limits\",\n): number {\n const read = (resources?: ResourceValues) => {\n const raw = resources?.[kind]?.[metric];\n if (raw !== undefined) return parseQuantity(raw, metric === \"memory\");\n if (kind === \"requests\" && resources?.limits?.[metric] !== undefined)\n return parseQuantity(resources.limits[metric], metric === \"memory\");\n return kind === \"requests\" ? 0 : Infinity;\n };\n const spec = pod.spec;\n if (!spec?.containers?.length) throw new Error(\"Missing pod containers\");\n let sidecars = 0;\n let initPeak = 0;\n for (const container of spec.initContainers ?? []) {\n const value = read(container.resources);\n if (container.restartPolicy === \"Always\") {\n sidecars += value;\n initPeak = Math.max(initPeak, sidecars);\n } else initPeak = Math.max(initPeak, sidecars + value);\n }\n let effective = Math.max(\n initPeak,\n sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0),\n );\n if (spec.resources?.[kind]?.[metric] !== undefined) effective = read(spec.resources);\n const overhead = spec.overhead?.[metric];\n if (overhead !== undefined && (kind === \"requests\" || effective > 0))\n effective += parseQuantity(overhead, metric === \"memory\");\n return effective;\n}\n\nexport function aggregateResources(\n nodes: MonitoringNode[],\n pods?: MonitoringPod[],\n): Record<\"cpu\" | \"memory\", ResourceTotals> {\n if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error(\"Missing node inventory\");\n const names = new Set(nodes.map((node) => node.metadata.name));\n const scheduled = pods?.filter(\n (pod) =>\n pod.spec?.nodeName &&\n names.has(pod.spec.nodeName) &&\n pod.status?.phase !== \"Succeeded\" &&\n pod.status?.phase !== \"Failed\",\n );\n const sum = (metric: \"cpu\" | \"memory\"): ResourceTotals => {\n const capacity = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const allocatable = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"requests\"), 0);\n const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"limits\"), 0);\n return {\n capacity,\n allocatable,\n requests,\n limits: limits === Infinity ? undefined : limits,\n unbounded: limits === Infinity,\n };\n };\n return { cpu: sum(\"cpu\"), memory: sum(\"memory\") };\n}\nconst INTERVAL = 30_000;\nexport const MAX_SAMPLES = 60;\n\nexport function parseQuantity(raw: string, memory = false): number {\n const match = /^(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);\n if (!match) throw new Error(\"Invalid metrics quantity\");\n const suffix = match[2] ?? \"\";\n const factors: Record<string, number> = {\n n: 1e-9,\n u: 1e-6,\n m: 1e-3,\n k: 1e3,\n K: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n E: 1e18,\n };\n const value =\n Number(match[1]) *\n (suffix.endsWith(\"i\")\n ? 1024 ** ([\"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\"].indexOf(suffix) + 1)\n : /^[eE][+-]?\\d+$/.test(suffix)\n ? 10 ** Number(suffix.slice(1))\n : (factors[suffix] ?? 1));\n if (!Number.isFinite(value) || (!memory && suffix.endsWith(\"i\"))) throw new Error(\"Invalid metrics quantity\");\n return value;\n}\n\nexport function aggregateMetrics(\n nodes: { usage: { cpu: string; memory: string }; timestamp: string }[],\n now = Date.now(),\n): MonitoringSample {\n if (!nodes.length) throw new Error(\"No node metrics available\");\n let cpu = 0;\n let memory = 0;\n for (const node of nodes) {\n const timestamp = Date.parse(node.timestamp);\n if (!Number.isFinite(timestamp) || now - timestamp > 120_000 || timestamp > now + 30_000)\n throw new Error(\"Node metrics are stale\");\n cpu += parseQuantity(node.usage.cpu);\n memory += parseQuantity(node.usage.memory, true);\n }\n return { time: now, cpu, memory };\n}\n\nexport function formatMonitoringTime(time: number): string {\n return new Date(time).toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n}\n\nfunction ResourceTitle({ metric, label }: { metric: \"cpu\" | \"memory\"; label?: string }) {\n return (\n <strong style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n <span className=\"Icon material\" aria-hidden=\"true\" style={{ \"--size\": \"18px\", fontWeight: 400 } as CSSProperties}>\n <span className=\"icon\">{metric === \"cpu\" ? \"developer_board\" : \"memory\"}</span>\n </span>\n {label ?? (metric === \"cpu\" ? \"CPU\" : \"Memory\")}\n </strong>\n );\n}\n\nexport function MetricChart({\n metric,\n samples,\n status,\n scope = \"node\",\n}: {\n metric: \"cpu\" | \"memory\";\n samples: MonitoringSample[];\n status: string;\n scope?: \"node\" | \"pod\";\n}) {\n const [selectedTime, setSelectedTime] = useState<number>();\n const [pinned, setPinned] = useState(false);\n const previousMetric = useRef(metric);\n useEffect(() => {\n if (\n previousMetric.current !== metric ||\n (selectedTime !== undefined && !samples.some((sample) => sample.time === selectedTime))\n ) {\n setPinned(false);\n setSelectedTime(undefined);\n }\n previousMetric.current = metric;\n }, [metric, samples, selectedTime]);\n useEffect(() => {\n if (!pinned) return;\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setPinned(false);\n setSelectedTime(undefined);\n }\n };\n document.addEventListener(\"keydown\", escape, true);\n return () => document.removeEventListener(\"keydown\", escape, true);\n }, [pinned]);\n const chartRef = useRef<SVGSVGElement>(null);\n const [width, setWidth] = useState(600);\n useLayoutEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n const measure = () => {\n const measured = chart.getBoundingClientRect().width;\n if (measured > 0) setWidth(measured);\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(chart);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, []);\n const plotWidth = Math.max(1, width - 112);\n const last = samples.at(-1);\n const selected = samples.find((sample) => sample.time === selectedTime);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const series = [\n { key: \"usage\", label: \"Usage\", color: \"#c93dce\", value: (sample: MonitoringSample) => sample[metric] },\n ...([\"requests\", \"allocatable\", \"capacity\"] as const).map((key) => ({\n key,\n label: { requests: \"Requests\", allocatable: \"Allocatable Capacity\", capacity: \"Capacity\" }[key],\n color: { requests: \"#4caf50\", allocatable: \"#5486b5\", capacity: \"var(--textColorSecondary)\" }[key],\n value: (sample: MonitoringSample) => sample.resources?.[metric][key],\n })),\n ];\n const max = Math.max(...samples.flatMap((sample) => series.map((line) => line.value(sample) ?? 0)), 0.001) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const duration = Math.max(end - start, INTERVAL);\n const x = (sample: MonitoringSample) => 88 + ((sample.time - start) / duration) * plotWidth;\n const y = (sample: MonitoringSample) => 208 - (sample[metric] / max) * 184;\n const selectNearest = (event: React.MouseEvent<SVGSVGElement>) => {\n const bounds = event.currentTarget.getBoundingClientRect();\n if (!bounds.width || !samples.length) return;\n const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);\n const offset = (bounds.width - width * scale) / 2;\n const position = (event.clientX - bounds.left - offset) / scale;\n const time = start + ((position - 88) / plotWidth) * duration;\n const nearest = samples.reduce((nearest, sample) =>\n Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest,\n ).time;\n setSelectedTime(nearest);\n return nearest;\n };\n return (\n <section\n aria-label={`${label} metrics-server usage`}\n style={{\n padding: \"var(--power-monitoring-trend-top, 16px) 16px 16px\",\n background: \"var(--contentColor)\",\n minWidth: 0,\n minHeight: 280,\n position: \"relative\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <ResourceTitle metric={metric} />\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n ref={chartRef}\n data-monitoring-trend=\"\"\n viewBox={`0 0 ${width} 250`}\n role=\"img\"\n tabIndex={0}\n aria-label={`${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`}\n onMouseMove={(event) => {\n if (!pinned) selectNearest(event);\n }}\n onClick={(event) => {\n if (pinned) setPinned(false);\n else if (samples.length) {\n if (selectNearest(event) === undefined && !selected) setSelectedTime(last?.time);\n setPinned(true);\n }\n }}\n onMouseLeave={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onFocus={() => {\n if (!pinned) setSelectedTime(last?.time);\n }}\n onBlur={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (samples.length) {\n if (!selected) setSelectedTime(last?.time);\n setPinned(!pinned);\n }\n return;\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const index = samples.findIndex((sample) => sample.time === selectedTime);\n setSelectedTime(\n samples[\n Math.max(\n 0,\n Math.min(\n samples.length - 1,\n (index < 0 ? samples.length - 1 : index) + (event.key === \"ArrowLeft\" ? -1 : 1),\n ),\n )\n ]?.time,\n );\n }}\n style={{ width: \"100%\", height: 250, color: \"#c93dce\", display: \"block\" }}\n >\n <title>Click to pin tooltip · click again or Escape to unpin</title>\n {[0, 0.5, 1].map((fraction) => (\n <g key={fraction}>\n <line\n x1=\"88\"\n x2={width - 24}\n y1={208 - fraction * 184}\n y2={208 - fraction * 184}\n stroke=\"var(--borderFaintColor)\"\n />\n <text x=\"80\" y={212 - fraction * 184} textAnchor=\"end\" fill=\"var(--textColorSecondary)\" fontSize=\"11\">\n {format(max * fraction)}\n </text>\n </g>\n ))}\n {series.map((line) => (\n <path\n key={line.key}\n data-monitoring-series={line.key}\n d={samples.reduce((path, sample, index) => {\n const value = line.value(sample);\n if (value === undefined) return path;\n const previous = index ? line.value(samples[index - 1]) : undefined;\n return `${path} ${previous === undefined ? \"M\" : \"L\"}${x(sample)},${208 - (value / max) * 184}`;\n }, \"\")}\n fill=\"none\"\n stroke={line.color}\n strokeWidth=\"2\"\n strokeDasharray={line.key === \"capacity\" || line.key === \"allocatable\" ? \"5 3\" : undefined}\n />\n ))}\n {samples.map((sample) => (\n <circle key={sample.time} cx={x(sample)} cy={y(sample)} r=\"2\" fill=\"currentColor\">\n <title>\n {formatMonitoringTime(sample.time)}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n {samples.length > 0 &&\n [0, 0.5, 1].map((fraction) => (\n <text\n key={fraction}\n x={88 + fraction * plotWidth}\n y=\"235\"\n textAnchor={fraction === 0 ? \"start\" : fraction === 1 ? \"end\" : \"middle\"}\n fill=\"var(--textColorSecondary)\"\n fontSize=\"11\"\n >\n {formatMonitoringTime(start + fraction * duration)}\n </text>\n ))}\n {selected && (\n <line\n data-monitoring-crosshair=\"\"\n x1={x(selected)}\n x2={x(selected)}\n y1=\"24\"\n y2=\"208\"\n stroke=\"var(--textColorSecondary)\"\n strokeDasharray=\"4 3\"\n />\n )}\n </svg>\n <style>{`\n @keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }\n [data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }\n @media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }\n `}</style>\n {selected && (\n <div\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 44,\n right: 24,\n padding: 8,\n background: \"var(--contentColor)\",\n border: \"1px solid var(--borderFaintColor)\",\n pointerEvents: \"none\",\n }}\n >\n <div>\n {formatMonitoringTime(selected.time)}\n {pinned ? \" · Pinned\" : \"\"}\n </div>\n {series.map((line) => (\n <div key={line.key} style={{ color: line.color }}>\n {line.label}: {line.value(selected) === undefined ? \"Unavailable\" : format(line.value(selected)!)}\n </div>\n ))}\n <div style={{ color: \"var(--textColorSecondary)\", marginTop: 4, fontSize: 10 }}>\n {pinned ? \"Click chart or Escape to unpin\" : \"Click or Enter to pin\"}\n </div>\n </div>\n )}\n <div\n aria-label={`${label} chart legend`}\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"6px 16px\", marginBottom: 8 }}\n >\n {series.map((line) => (\n <span key={line.key}>\n <span style={{ color: line.color }}>●</span> {line.label}\n </span>\n ))}\n </div>\n {last?.resourceStatus && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", marginBottom: 8 }}>\n {last.resourceStatus}\n </div>\n )}\n <div\n style={{ fontSize: 11, color: \"var(--textColorSecondary)\", display: \"flex\", alignItems: \"center\", gap: 4 }}\n role=\"status\"\n aria-label={`${scope} metrics status`}\n >\n <sup aria-hidden=\"true\" style={{ fontSize: 12 }}>\n *\n </sup>\n <span>{status || (samples.length ? \"metrics-server data\" : \"Waiting for metrics-server data\")}</span>\n </div>\n </section>\n );\n}\n\nfunction ResourceDonut({\n metric,\n sample,\n status,\n}: {\n metric: \"cpu\" | \"memory\";\n sample?: MonitoringSample;\n status: string;\n}) {\n const [activeRing, setActiveRing] = useState<number>();\n useEffect(() => {\n if (activeRing === undefined) return;\n const dismiss = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n setActiveRing(undefined);\n };\n document.addEventListener(\"keydown\", dismiss, true);\n return () => document.removeEventListener(\"keydown\", dismiss, true);\n }, [activeRing]);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const totals = sample?.resources?.[metric];\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const available = (value?: number) => (value === undefined ? \"Unavailable\" : format(value));\n const denominator = totals?.allocatable;\n const rings = [\n { label: \"Usage\", color: \"#c93dce\", value: sample?.[metric] },\n { label: \"Requests\", color: \"#4caf50\", value: totals?.requests },\n ];\n const percentage = (value?: number) =>\n value !== undefined && denominator !== undefined && denominator > 0 ? (value / denominator) * 100 : undefined;\n const usagePercent = percentage(sample?.[metric]);\n const unused =\n sample?.completeUsage && denominator !== undefined ? format(denominator - sample[metric]) : \"Unavailable\";\n const unrequested =\n denominator !== undefined && totals?.requests !== undefined ? format(denominator - totals.requests) : \"Unavailable\";\n const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;\n const hovered = activeRing === undefined ? undefined : rings[activeRing];\n const tooltipId = `power-monitoring-${metric}-donut-tooltip`;\n return (\n <section\n aria-label={`${label} resource summary`}\n style={{\n padding: \"var(--power-monitoring-summary-top, 16px) 16px 16px\",\n minWidth: 0,\n background: \"var(--contentColor)\",\n position: \"relative\",\n overflow: \"visible\",\n }}\n >\n <ResourceTitle metric={metric} label={metric === \"memory\" ? \"RAM\" : \"CPU\"} />\n <svg\n data-monitoring-donut=\"\"\n viewBox=\"0 0 200 200\"\n role=\"img\"\n aria-label={`${label} usage and requests relative to allocatable capacity`}\n style={{ display: \"block\", width: \"100%\", height: 200 }}\n >\n {rings.map((ring, index) => {\n const radius = 76 - index * 18;\n const percent = percentage(ring.value);\n const detail = `${ring.label}: ${available(ring.value)}${percent === undefined ? \"\" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? \", over allocatable\" : \"\"})`}`;\n return (\n <g\n key={ring.label}\n tabIndex={0}\n role=\"img\"\n aria-label={detail}\n aria-describedby={activeRing === index ? tooltipId : undefined}\n onMouseEnter={() => setActiveRing(index)}\n onMouseMove={() => setActiveRing(index)}\n onMouseLeave={() => setActiveRing(undefined)}\n onFocus={() => setActiveRing(index)}\n onBlur={() => setActiveRing(undefined)}\n >\n <circle cx=\"100\" cy=\"100\" r={radius} fill=\"none\" stroke=\"var(--borderFaintColor)\" strokeWidth=\"12\" />\n {percent !== undefined && (\n <circle\n data-monitoring-ring={ring.label}\n cx=\"100\"\n cy=\"100\"\n r={radius}\n fill=\"none\"\n stroke={ring.color}\n strokeWidth=\"12\"\n pathLength=\"100\"\n strokeDasharray={`${Math.min(100, Math.max(0, percent))} 100`}\n transform=\"rotate(-90 100 100)\"\n />\n )}\n </g>\n );\n })}\n <text\n data-monitoring-usage-percentage=\"\"\n x=\"100\"\n y=\"100\"\n textAnchor=\"middle\"\n fill=\"var(--textColorPrimary)\"\n fontSize=\"26\"\n fontWeight=\"600\"\n >\n {usagePercent === undefined ? \"N/A\" : `${usagePercent.toFixed(1)}%`}\n </text>\n <text x=\"100\" y=\"120\" textAnchor=\"middle\" fill=\"var(--textColorPrimary)\" fontSize=\"12\">\n Usage\n </text>\n </svg>\n {hovered && (\n <div\n id={tooltipId}\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 48,\n left: 8,\n right: 8,\n zIndex: 20,\n padding: 8,\n border: \"1px solid var(--borderFaintColor)\",\n background: \"var(--contentColor)\",\n pointerEvents: \"none\",\n overflowWrap: \"anywhere\",\n boxShadow: \"0 3px 12px #0003\",\n }}\n >\n <div style={{ color: hovered.color }}>\n {hovered.label}: {available(hovered.value)}\n </div>\n <div>\n {percentage(hovered.value) === undefined\n ? \"Percentage unavailable\"\n : `${percentage(hovered.value)!.toFixed(1)}% of allocatable${percentage(hovered.value)! > 100 ? \" (over allocatable)\" : \"\"}`}\n </div>\n <div>Allocatable: {available(denominator)}</div>\n </div>\n )}\n <div aria-label={`${label} resource totals`} style={{ display: \"grid\", gap: 6, fontSize: 12 }}>\n {rings.map((ring) => (\n <div\n key={ring.label}\n title={`${ring.label} relative to allocatable capacity. ${ring.label === \"Usage\" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`}\n >\n <span style={{ color: ring.color }}>●</span> {ring.label}: {available(ring.value)}\n {percentage(ring.value) !== undefined && <span> ({percentage(ring.value)!.toFixed(1)}%)</span>}\n </div>\n ))}\n <div title={allocationDetail}>\n <span style={{ color: \"#5486b5\" }}>●</span> Allocatable: {available(denominator)}\n </div>\n </div>\n {(status || sample?.resourceStatus) && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", fontSize: 11, marginTop: 8 }}>\n {status || sample?.resourceStatus}\n </div>\n )}\n </section>\n );\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n return (\n <div style={{ containerType: \"inline-size\" }}>\n <style>{`\n [data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }\n [data-monitoring-trends], [data-monitoring-summaries] { display:contents }\n @container (min-width: 961px) {\n [data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }\n }\n @container (max-width: 960px) {\n [data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }\n }\n @container (max-width: 580px) {\n [data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }\n }\n `}</style>\n <div data-monitoring-layout=\"\">\n <div data-monitoring-trends=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={metric} metric={metric} samples={samples} status={status} />\n ))}\n </div>\n <div data-monitoring-summaries=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <ResourceDonut key={metric} metric={metric} sample={samples.at(-1)} status={status} />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n };\n}\n\nfunction mountMonitoring(): () => void {\n const suppression = document.createElement(\"style\");\n suppression.setAttribute(\"data-freelens-power-native-metrics-suppression\", \"\");\n suppression.textContent = `\n [data-testid=\"cluster-overview-page\"] > [class*=\"ClusterMetrics\"],\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"ClusterMetrics\"]),\n [data-testid=\"cluster-overview-page\"] > :is([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]):not(:has(.PieChart)),\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]):not(:has(.PieChart)) {\n display:none !important;\n }\n `;\n document.head.append(suppression);\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n renderedSamples = undefined;\n renderedStatus = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n const cutoff = Date.now() - MAX_SAMPLES * INTERVAL;\n if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n host.style.gridColumn = \"1 / -1\";\n hidden.set(overview as HTMLElement, overview.getAttribute(\"style\"));\n Object.assign((overview as HTMLElement).style, {\n gridTemplateAreas: \"none\",\n gridTemplateColumns: \"minmax(0, 1fr)\",\n gridTemplateRows: \"none\",\n height: \"auto\",\n });\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && panel.style.display !== \"none\") {\n if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n // native CSS modules retain [ClusterMetrics] in the generated class name\n for (const child of overview.children) {\n if (!(child instanceof HTMLElement) || child === host) continue;\n const nativeMetrics =\n child.matches('[class*=\"ClusterMetrics\"]') || child.querySelector('[class*=\"ClusterMetrics\"]');\n const unavailablePies =\n (child.matches('[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]') ||\n child.querySelector(\n '[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]',\n )) &&\n !child.querySelector(\".PieChart\");\n if ((nativeMetrics || unavailablePies) && child.style.display !== \"none\") {\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.display = \"none\";\n }\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.gridArea = \"auto\";\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status)) {\n ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n renderedSamples = samples;\n renderedStatus = status;\n }\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const [metrics, inventory, pods] = await Promise.allSettled([\n Renderer.K8sApi.nodesMetricsApi.list(),\n Promise.resolve().then(() => Renderer.K8sApi.nodesApi.list()),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.list()),\n ]);\n if (!isCurrent()) return;\n if (metrics.status !== \"fulfilled\") throw new Error(\"Metrics unavailable\");\n const sample = aggregateMetrics(metrics.value ?? []);\n const warnings: string[] = [];\n if (inventory.status === \"fulfilled\") {\n try {\n sample.resources = aggregateResources(\n inventory.value ?? [],\n pods.status === \"fulfilled\" ? (pods.value ?? undefined) : undefined,\n );\n const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));\n const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));\n sample.completeUsage =\n expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));\n if (!sample.completeUsage)\n warnings.push(\n \"Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable\",\n );\n } catch {\n warnings.push(\"Resource totals unavailable: incomplete or invalid node/pod data\");\n }\n } else\n warnings.push(\"Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable\");\n if (pods.status !== \"fulfilled\")\n warnings.push(\"Cluster-wide Pods unavailable or access denied; requests unavailable\");\n sample.resourceStatus = warnings.join(\". \");\n samples = [...samples, sample].slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const observer = new MutationObserver(() => {\n void update();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n observer.disconnect();\n suppression.remove();\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"monitoring.js","names":[],"sources":["../../src/renderer/monitoring.tsx"],"sourcesContent":["import { Renderer } from \"@freelensapp/extensions\";\nimport { reaction } from \"mobx\";\nimport { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { MonitoringPreferencesStore } from \"../common/store/monitoring-preferences-store\";\n\ntype ResourceValues = { requests?: Partial<Record<string, string>>; limits?: Partial<Record<string, string>> };\ntype ResourceContainer = { resources?: ResourceValues; restartPolicy?: string };\nexport type MonitoringPod = {\n spec?: {\n nodeName?: string;\n containers?: ResourceContainer[];\n initContainers?: ResourceContainer[];\n overhead?: Partial<Record<string, string>>;\n resources?: ResourceValues;\n };\n status?: { phase?: string };\n};\nexport type MonitoringNode = {\n metadata: { name: string };\n status?: { capacity?: Partial<Record<string, string>>; allocatable?: Partial<Record<string, string>> };\n};\ntype ResourceTotals = {\n capacity?: number;\n allocatable?: number;\n requests?: number;\n limits?: number;\n unbounded?: boolean;\n};\nexport type MonitoringSample = {\n time: number;\n cpu: number;\n memory: number;\n resources?: Record<\"cpu\" | \"memory\", ResourceTotals>;\n resourceStatus?: string;\n completeUsage?: boolean;\n};\n\nexport function effectivePodResource(\n pod: MonitoringPod,\n metric: \"cpu\" | \"memory\",\n kind: \"requests\" | \"limits\",\n): number {\n const read = (resources?: ResourceValues) => {\n const raw = resources?.[kind]?.[metric];\n if (raw !== undefined) return parseQuantity(raw, metric === \"memory\");\n if (kind === \"requests\" && resources?.limits?.[metric] !== undefined)\n return parseQuantity(resources.limits[metric], metric === \"memory\");\n return kind === \"requests\" ? 0 : Infinity;\n };\n const spec = pod.spec;\n if (!spec?.containers?.length) throw new Error(\"Missing pod containers\");\n let sidecars = 0;\n let initPeak = 0;\n for (const container of spec.initContainers ?? []) {\n const value = read(container.resources);\n if (container.restartPolicy === \"Always\") {\n sidecars += value;\n initPeak = Math.max(initPeak, sidecars);\n } else initPeak = Math.max(initPeak, sidecars + value);\n }\n let effective = Math.max(\n initPeak,\n sidecars + spec.containers.reduce((sum, container) => sum + read(container.resources), 0),\n );\n if (spec.resources?.[kind]?.[metric] !== undefined) effective = read(spec.resources);\n const overhead = spec.overhead?.[metric];\n if (overhead !== undefined && (kind === \"requests\" || effective > 0))\n effective += parseQuantity(overhead, metric === \"memory\");\n return effective;\n}\n\nexport function aggregateResources(\n nodes: MonitoringNode[],\n pods?: MonitoringPod[],\n): Record<\"cpu\" | \"memory\", ResourceTotals> {\n if (!nodes.length || nodes.some((node) => !node.metadata?.name)) throw new Error(\"Missing node inventory\");\n const names = new Set(nodes.map((node) => node.metadata.name));\n const scheduled = pods?.filter(\n (pod) =>\n pod.spec?.nodeName &&\n names.has(pod.spec.nodeName) &&\n pod.status?.phase !== \"Succeeded\" &&\n pod.status?.phase !== \"Failed\",\n );\n const sum = (metric: \"cpu\" | \"memory\"): ResourceTotals => {\n const capacity = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.capacity?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const allocatable = nodes.reduce(\n (total, node) => total + parseQuantity(node.status?.allocatable?.[metric] ?? \"\", metric === \"memory\"),\n 0,\n );\n const requests = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"requests\"), 0);\n const limits = scheduled?.reduce((total, pod) => total + effectivePodResource(pod, metric, \"limits\"), 0);\n return {\n capacity,\n allocatable,\n requests,\n limits: limits === Infinity ? undefined : limits,\n unbounded: limits === Infinity,\n };\n };\n return { cpu: sum(\"cpu\"), memory: sum(\"memory\") };\n}\nconst INTERVAL = 30_000;\nexport const MAX_SAMPLES = 60;\n\nexport function parseQuantity(raw: string, memory = false): number {\n const match = /^(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+|n|u|m|Ki|Mi|Gi|Ti|Pi|Ei|k|K|M|G|T|P|E)?$/.exec(raw);\n if (!match) throw new Error(\"Invalid metrics quantity\");\n const suffix = match[2] ?? \"\";\n const factors: Record<string, number> = {\n n: 1e-9,\n u: 1e-6,\n m: 1e-3,\n k: 1e3,\n K: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n E: 1e18,\n };\n const value =\n Number(match[1]) *\n (suffix.endsWith(\"i\")\n ? 1024 ** ([\"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\"].indexOf(suffix) + 1)\n : /^[eE][+-]?\\d+$/.test(suffix)\n ? 10 ** Number(suffix.slice(1))\n : (factors[suffix] ?? 1));\n if (!Number.isFinite(value) || (!memory && suffix.endsWith(\"i\"))) throw new Error(\"Invalid metrics quantity\");\n return value;\n}\n\nexport function aggregateMetrics(\n nodes: { usage: { cpu: string; memory: string }; timestamp: string }[],\n now = Date.now(),\n): MonitoringSample {\n if (!nodes.length) throw new Error(\"No node metrics available\");\n let cpu = 0;\n let memory = 0;\n for (const node of nodes) {\n const timestamp = Date.parse(node.timestamp);\n if (!Number.isFinite(timestamp) || now - timestamp > 120_000 || timestamp > now + 30_000)\n throw new Error(\"Node metrics are stale\");\n cpu += parseQuantity(node.usage.cpu);\n memory += parseQuantity(node.usage.memory, true);\n }\n return { time: now, cpu, memory };\n}\n\nexport function formatMonitoringTime(time: number): string {\n return new Date(time).toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n });\n}\n\nfunction ResourceTitle({ metric, label }: { metric: \"cpu\" | \"memory\"; label?: string }) {\n return (\n <strong style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6 }}>\n <span className=\"Icon material\" aria-hidden=\"true\" style={{ \"--size\": \"18px\", fontWeight: 400 } as CSSProperties}>\n <span className=\"icon\">{metric === \"cpu\" ? \"developer_board\" : \"memory\"}</span>\n </span>\n {label ?? (metric === \"cpu\" ? \"CPU\" : \"Memory\")}\n </strong>\n );\n}\n\nexport function MetricChart({\n metric,\n samples,\n status,\n scope = \"node\",\n}: {\n metric: \"cpu\" | \"memory\";\n samples: MonitoringSample[];\n status: string;\n scope?: \"node\" | \"pod\";\n}) {\n const [selectedTime, setSelectedTime] = useState<number>();\n const [pinned, setPinned] = useState(false);\n const previousMetric = useRef(metric);\n useEffect(() => {\n if (\n previousMetric.current !== metric ||\n (selectedTime !== undefined && !samples.some((sample) => sample.time === selectedTime))\n ) {\n setPinned(false);\n setSelectedTime(undefined);\n }\n previousMetric.current = metric;\n }, [metric, samples, selectedTime]);\n useEffect(() => {\n if (!pinned) return;\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n setPinned(false);\n setSelectedTime(undefined);\n }\n };\n document.addEventListener(\"keydown\", escape, true);\n return () => document.removeEventListener(\"keydown\", escape, true);\n }, [pinned]);\n const chartRef = useRef<SVGSVGElement>(null);\n const [width, setWidth] = useState(600);\n useLayoutEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n const measure = () => {\n const measured = chart.getBoundingClientRect().width;\n if (measured > 0) setWidth(measured);\n };\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(chart);\n window.addEventListener(\"resize\", measure);\n return () => {\n observer?.disconnect();\n window.removeEventListener(\"resize\", measure);\n };\n }, []);\n const plotWidth = Math.max(1, width - 112);\n const last = samples.at(-1);\n const selected = samples.find((sample) => sample.time === selectedTime);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const series = [\n { key: \"usage\", label: \"Usage\", color: \"#c93dce\", value: (sample: MonitoringSample) => sample[metric] },\n ...([\"requests\", \"allocatable\", \"capacity\"] as const).map((key) => ({\n key,\n label: { requests: \"Requests\", allocatable: \"Allocatable Capacity\", capacity: \"Capacity\" }[key],\n color: { requests: \"#4caf50\", allocatable: \"#5486b5\", capacity: \"var(--textColorSecondary)\" }[key],\n value: (sample: MonitoringSample) => sample.resources?.[metric][key],\n })),\n ];\n const visibleSeries =\n scope === \"pod\" ? series.filter((line) => line.key === \"usage\" || line.key === \"requests\") : series;\n const max =\n Math.max(...samples.flatMap((sample) => visibleSeries.map((line) => line.value(sample) ?? 0)), 0.001) * 1.1;\n const end = last?.time ?? Date.now();\n const start = samples[0]?.time ?? end;\n const duration = Math.max(end - start, INTERVAL);\n const x = (sample: MonitoringSample) => 88 + ((sample.time - start) / duration) * plotWidth;\n const y = (sample: MonitoringSample) => 208 - (sample[metric] / max) * 184;\n const selectNearest = (event: React.MouseEvent<SVGSVGElement>) => {\n const bounds = event.currentTarget.getBoundingClientRect();\n if (!bounds.width || !samples.length) return;\n const scale = Math.min(bounds.width / width, (bounds.height || 250) / 250);\n const offset = (bounds.width - width * scale) / 2;\n const position = (event.clientX - bounds.left - offset) / scale;\n const time = start + ((position - 88) / plotWidth) * duration;\n const nearest = samples.reduce((nearest, sample) =>\n Math.abs(sample.time - time) < Math.abs(nearest.time - time) ? sample : nearest,\n ).time;\n setSelectedTime(nearest);\n return nearest;\n };\n return (\n <section\n aria-label={`${label} metrics-server usage`}\n style={{\n padding: \"var(--power-monitoring-trend-top, 16px) 16px 16px\",\n background: \"var(--contentColor)\",\n minWidth: 0,\n minHeight: 280,\n position: \"relative\",\n }}\n >\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n <ResourceTitle metric={metric} />\n <span>{status ? \"Unavailable\" : last ? format(last[metric]) : \"Collecting…\"}</span>\n </div>\n <svg\n ref={chartRef}\n data-monitoring-trend=\"\"\n viewBox={`0 0 ${width} 250`}\n role=\"img\"\n tabIndex={0}\n aria-label={`${label} usage over collected samples. Use arrow keys to inspect samples. Click or press Enter to pin the tooltip, Escape to dismiss`}\n onMouseMove={(event) => {\n if (!pinned) selectNearest(event);\n }}\n onClick={(event) => {\n if (pinned) setPinned(false);\n else if (samples.length) {\n if (selectNearest(event) === undefined && !selected) setSelectedTime(last?.time);\n setPinned(true);\n }\n }}\n onMouseLeave={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onFocus={() => {\n if (!pinned) setSelectedTime(last?.time);\n }}\n onBlur={() => {\n if (!pinned) setSelectedTime(undefined);\n }}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (samples.length) {\n if (!selected) setSelectedTime(last?.time);\n setPinned(!pinned);\n }\n return;\n }\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n const index = samples.findIndex((sample) => sample.time === selectedTime);\n setSelectedTime(\n samples[\n Math.max(\n 0,\n Math.min(\n samples.length - 1,\n (index < 0 ? samples.length - 1 : index) + (event.key === \"ArrowLeft\" ? -1 : 1),\n ),\n )\n ]?.time,\n );\n }}\n style={{ width: \"100%\", height: 250, color: \"#c93dce\", display: \"block\" }}\n >\n <title>Click to pin tooltip · click again or Escape to unpin</title>\n {[0, 0.5, 1].map((fraction) => (\n <g key={fraction}>\n <line\n x1=\"88\"\n x2={width - 24}\n y1={208 - fraction * 184}\n y2={208 - fraction * 184}\n stroke=\"var(--borderFaintColor)\"\n />\n <text x=\"80\" y={212 - fraction * 184} textAnchor=\"end\" fill=\"var(--textColorSecondary)\" fontSize=\"11\">\n {format(max * fraction)}\n </text>\n </g>\n ))}\n {visibleSeries.map((line) => (\n <path\n key={line.key}\n data-monitoring-series={line.key}\n d={samples.reduce((path, sample, index) => {\n const value = line.value(sample);\n if (value === undefined) return path;\n const previous = index ? line.value(samples[index - 1]) : undefined;\n return `${path} ${previous === undefined ? \"M\" : \"L\"}${x(sample)},${208 - (value / max) * 184}`;\n }, \"\")}\n fill=\"none\"\n stroke={line.color}\n strokeWidth=\"2\"\n strokeDasharray={line.key === \"capacity\" || line.key === \"allocatable\" ? \"5 3\" : undefined}\n />\n ))}\n {samples.map((sample) => (\n <circle key={sample.time} cx={x(sample)} cy={y(sample)} r=\"2\" fill=\"currentColor\">\n <title>\n {formatMonitoringTime(sample.time)}: {format(sample[metric])}\n </title>\n </circle>\n ))}\n {samples.length > 0 &&\n [0, 0.5, 1].map((fraction) => (\n <text\n key={fraction}\n x={88 + fraction * plotWidth}\n y=\"235\"\n textAnchor={fraction === 0 ? \"start\" : fraction === 1 ? \"end\" : \"middle\"}\n fill=\"var(--textColorSecondary)\"\n fontSize=\"11\"\n >\n {formatMonitoringTime(start + fraction * duration)}\n </text>\n ))}\n {selected && (\n <line\n data-monitoring-crosshair=\"\"\n x1={x(selected)}\n x2={x(selected)}\n y1=\"24\"\n y2=\"208\"\n stroke=\"var(--textColorSecondary)\"\n strokeDasharray=\"4 3\"\n />\n )}\n </svg>\n <style>{`\n @keyframes freelens-power-metric-tooltip-enter { from { opacity:0; transform:translateY(2px) } to { opacity:1; transform:translateY(0) } }\n [data-monitoring-tooltip] { animation:freelens-power-metric-tooltip-enter 140ms ease-out }\n @media (prefers-reduced-motion:reduce) { [data-monitoring-tooltip] { animation:none } }\n `}</style>\n {selected && (\n <div\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 44,\n right: 24,\n padding: 8,\n background: \"var(--contentColor)\",\n border: \"1px solid var(--borderFaintColor)\",\n pointerEvents: \"none\",\n }}\n >\n <div>\n {formatMonitoringTime(selected.time)}\n {pinned ? \" · Pinned\" : \"\"}\n </div>\n {visibleSeries.map((line) => (\n <div key={line.key} style={{ color: line.color }}>\n {line.label}: {line.value(selected) === undefined ? \"Unavailable\" : format(line.value(selected)!)}\n </div>\n ))}\n <div style={{ color: \"var(--textColorSecondary)\", marginTop: 4, fontSize: 10 }}>\n {pinned ? \"Click chart or Escape to unpin\" : \"Click or Enter to pin\"}\n </div>\n </div>\n )}\n <div\n aria-label={`${label} chart legend`}\n style={{ display: \"flex\", flexWrap: \"wrap\", gap: \"6px 16px\", marginBottom: 8 }}\n >\n {visibleSeries.map((line) => (\n <span key={line.key}>\n <span style={{ color: line.color }}>●</span> {line.label}\n </span>\n ))}\n </div>\n {last?.resourceStatus && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", marginBottom: 8 }}>\n {last.resourceStatus}\n </div>\n )}\n {(scope === \"node\" || status || !samples.length) && (\n <div\n style={{ fontSize: 11, color: \"var(--textColorSecondary)\", display: \"flex\", alignItems: \"center\", gap: 4 }}\n role=\"status\"\n aria-label={`${scope} metrics status`}\n >\n {scope === \"node\" && (\n <sup aria-hidden=\"true\" style={{ fontSize: 12 }}>\n *\n </sup>\n )}\n <span>{status || (samples.length ? \"metrics-server data\" : \"Waiting for metrics-server data\")}</span>\n </div>\n )}\n </section>\n );\n}\n\nfunction ResourceDonut({\n metric,\n sample,\n status,\n}: {\n metric: \"cpu\" | \"memory\";\n sample?: MonitoringSample;\n status: string;\n}) {\n const [activeRing, setActiveRing] = useState<number>();\n useEffect(() => {\n if (activeRing === undefined) return;\n const dismiss = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n setActiveRing(undefined);\n };\n document.addEventListener(\"keydown\", dismiss, true);\n return () => document.removeEventListener(\"keydown\", dismiss, true);\n }, [activeRing]);\n const label = metric === \"cpu\" ? \"CPU\" : \"Memory\";\n const totals = sample?.resources?.[metric];\n const format = (value: number) =>\n metric === \"cpu\" ? `${value.toFixed(2)} cores` : `${(value / 1024 ** 3).toFixed(2)} GiB`;\n const available = (value?: number) => (value === undefined ? \"Unavailable\" : format(value));\n const denominator = totals?.allocatable;\n const rings = [\n { label: \"Usage\", color: \"#c93dce\", value: sample?.[metric] },\n { label: \"Requests\", color: \"#4caf50\", value: totals?.requests },\n ];\n const percentage = (value?: number) =>\n value !== undefined && denominator !== undefined && denominator > 0 ? (value / denominator) * 100 : undefined;\n const usagePercent = percentage(sample?.[metric]);\n const unused =\n sample?.completeUsage && denominator !== undefined ? format(denominator - sample[metric]) : \"Unavailable\";\n const unrequested =\n denominator !== undefined && totals?.requests !== undefined ? format(denominator - totals.requests) : \"Unavailable\";\n const allocationDetail = `Allocatable: ${available(denominator)}. Schedulable capacity after system reservations, used as the denominator for both rings. Physical Capacity: ${available(totals?.capacity)}. Unused allocatable (usage): ${unused}. Unrequested: ${unrequested}`;\n const hovered = activeRing === undefined ? undefined : rings[activeRing];\n const tooltipId = `power-monitoring-${metric}-donut-tooltip`;\n return (\n <section\n aria-label={`${label} resource summary`}\n style={{\n padding: \"var(--power-monitoring-summary-top, 16px) 16px 16px\",\n minWidth: 0,\n background: \"var(--contentColor)\",\n position: \"relative\",\n overflow: \"visible\",\n }}\n >\n <ResourceTitle metric={metric} label={metric === \"memory\" ? \"RAM\" : \"CPU\"} />\n <svg\n data-monitoring-donut=\"\"\n viewBox=\"0 0 200 200\"\n role=\"img\"\n aria-label={`${label} usage and requests relative to allocatable capacity`}\n style={{ display: \"block\", width: \"100%\", height: 200 }}\n >\n {rings.map((ring, index) => {\n const radius = 76 - index * 18;\n const percent = percentage(ring.value);\n const detail = `${ring.label}: ${available(ring.value)}${percent === undefined ? \"\" : ` (${percent.toFixed(1)}% of allocatable${percent > 100 ? \", over allocatable\" : \"\"})`}`;\n return (\n <g\n key={ring.label}\n tabIndex={0}\n role=\"img\"\n aria-label={detail}\n aria-describedby={activeRing === index ? tooltipId : undefined}\n onMouseEnter={() => setActiveRing(index)}\n onMouseMove={() => setActiveRing(index)}\n onMouseLeave={() => setActiveRing(undefined)}\n onFocus={() => setActiveRing(index)}\n onBlur={() => setActiveRing(undefined)}\n >\n <circle cx=\"100\" cy=\"100\" r={radius} fill=\"none\" stroke=\"var(--borderFaintColor)\" strokeWidth=\"12\" />\n {percent !== undefined && (\n <circle\n data-monitoring-ring={ring.label}\n cx=\"100\"\n cy=\"100\"\n r={radius}\n fill=\"none\"\n stroke={ring.color}\n strokeWidth=\"12\"\n pathLength=\"100\"\n strokeDasharray={`${Math.min(100, Math.max(0, percent))} 100`}\n transform=\"rotate(-90 100 100)\"\n />\n )}\n </g>\n );\n })}\n <text\n data-monitoring-usage-percentage=\"\"\n x=\"100\"\n y=\"100\"\n textAnchor=\"middle\"\n fill=\"var(--textColorPrimary)\"\n fontSize=\"26\"\n fontWeight=\"600\"\n >\n {usagePercent === undefined ? \"N/A\" : `${usagePercent.toFixed(1)}%`}\n </text>\n <text x=\"100\" y=\"120\" textAnchor=\"middle\" fill=\"var(--textColorPrimary)\" fontSize=\"12\">\n Usage\n </text>\n </svg>\n {hovered && (\n <div\n id={tooltipId}\n data-monitoring-tooltip=\"\"\n role=\"tooltip\"\n style={{\n position: \"absolute\",\n top: 48,\n left: 8,\n right: 8,\n zIndex: 20,\n padding: 8,\n border: \"1px solid var(--borderFaintColor)\",\n background: \"var(--contentColor)\",\n pointerEvents: \"none\",\n overflowWrap: \"anywhere\",\n boxShadow: \"0 3px 12px #0003\",\n }}\n >\n <div style={{ color: hovered.color }}>\n {hovered.label}: {available(hovered.value)}\n </div>\n <div>\n {percentage(hovered.value) === undefined\n ? \"Percentage unavailable\"\n : `${percentage(hovered.value)!.toFixed(1)}% of allocatable${percentage(hovered.value)! > 100 ? \" (over allocatable)\" : \"\"}`}\n </div>\n <div>Allocatable: {available(denominator)}</div>\n </div>\n )}\n <div aria-label={`${label} resource totals`} style={{ display: \"grid\", gap: 6, fontSize: 12 }}>\n {rings.map((ring) => (\n <div\n key={ring.label}\n title={`${ring.label} relative to allocatable capacity. ${ring.label === \"Usage\" ? `Unused allocatable (usage): ${unused}` : `Unrequested: ${unrequested}`}`}\n >\n <span style={{ color: ring.color }}>●</span> {ring.label}: {available(ring.value)}\n {percentage(ring.value) !== undefined && <span> ({percentage(ring.value)!.toFixed(1)}%)</span>}\n </div>\n ))}\n <div title={allocationDetail}>\n <span style={{ color: \"#5486b5\" }}>●</span> Allocatable: {available(denominator)}\n </div>\n </div>\n {(status || sample?.resourceStatus) && (\n <div role=\"status\" style={{ color: \"var(--textColorSecondary)\", fontSize: 11, marginTop: 8 }}>\n {status || sample?.resourceStatus}\n </div>\n )}\n </section>\n );\n}\n\nfunction MonitoringCharts({ samples, status }: { samples: MonitoringSample[]; status: string }) {\n return (\n <div style={{ containerType: \"inline-size\" }}>\n <style>{`\n [data-monitoring-layout] { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr); gap:16px; align-items:stretch }\n [data-monitoring-trends], [data-monitoring-summaries] { display:contents }\n @container (min-width: 961px) {\n [data-monitoring-summaries] > section { --power-monitoring-summary-top:16px }\n }\n @container (max-width: 960px) {\n [data-monitoring-layout] { grid-template-columns:repeat(2,minmax(0,1fr)) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section { --power-monitoring-trend-top:56px }\n }\n @container (max-width: 580px) {\n [data-monitoring-layout] { grid-template-columns:minmax(0,1fr) }\n .power-ai-overview-actions + [data-testid=\"cluster-overview-page\"] [data-monitoring-trends] > section:not(:first-child) { --power-monitoring-trend-top:16px }\n }\n `}</style>\n <div data-monitoring-layout=\"\">\n <div data-monitoring-trends=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <MetricChart key={metric} metric={metric} samples={samples} status={status} />\n ))}\n </div>\n <div data-monitoring-summaries=\"\">\n {([\"cpu\", \"memory\"] as const).map((metric) => (\n <ResourceDonut key={metric} metric={metric} sample={samples.at(-1)} status={status} />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nexport function startMonitoring(): () => void {\n const preferences = MonitoringPreferencesStore.getInstanceOrCreate<MonitoringPreferencesStore>();\n let stopCurrent: (() => void) | undefined;\n const stop = reaction(\n () => preferences.enabled,\n (enabled) => {\n stopCurrent?.();\n stopCurrent = enabled ? mountMonitoring() : undefined;\n },\n { fireImmediately: true },\n );\n return () => {\n stop();\n stopCurrent?.();\n };\n}\n\nfunction mountMonitoring(): () => void {\n const suppression = document.createElement(\"style\");\n suppression.setAttribute(\"data-freelens-power-native-metrics-suppression\", \"\");\n suppression.textContent = `\n [data-testid=\"cluster-overview-page\"] > [class*=\"ClusterMetrics\"],\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"ClusterMetrics\"]),\n [data-testid=\"cluster-overview-page\"] > :is([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]):not(:has(.PieChart)),\n [data-testid=\"cluster-overview-page\"] > :has([class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]):not(:has(.PieChart)) {\n display:none !important;\n }\n `;\n document.head.append(suppression);\n let host: HTMLElement | undefined;\n let overview: Element | null = null;\n let samples: MonitoringSample[] = [];\n let status = \"\";\n let renderedSamples: MonitoringSample[] | undefined;\n let renderedStatus: string | undefined;\n let cluster: string | undefined;\n let stopped = false;\n let pendingGeneration: number | undefined;\n let generation = 0;\n let nextSample = 0;\n const hidden = new Map<HTMLElement, string | null>();\n const restore = () => {\n for (const [element, original] of hidden) {\n if (original === null) element.removeAttribute(\"style\");\n else element.setAttribute(\"style\", original);\n }\n hidden.clear();\n if (host) {\n ReactDOM.unmountComponentAtNode(host);\n host.remove();\n host = undefined;\n renderedSamples = undefined;\n renderedStatus = undefined;\n }\n };\n const update = async () => {\n const currentCluster = Renderer.Catalog.activeCluster.get()?.metadata.uid;\n const currentOverview = document.querySelector('[data-testid=\"cluster-overview-page\"]');\n if (cluster !== currentCluster || overview !== currentOverview) {\n generation++;\n restore();\n samples = [];\n status = \"\";\n nextSample = 0;\n cluster = currentCluster;\n overview = currentOverview;\n }\n const cutoff = Date.now() - MAX_SAMPLES * INTERVAL;\n if (samples.length && samples[0].time < cutoff) samples = samples.filter((sample) => sample.time >= cutoff);\n if (!overview || !cluster || document.visibilityState === \"hidden\") return;\n if (!host) {\n host = document.createElement(\"div\");\n host.setAttribute(\"data-freelens-power-monitoring\", \"\");\n host.style.gridColumn = \"1 / -1\";\n hidden.set(overview as HTMLElement, overview.getAttribute(\"style\"));\n Object.assign((overview as HTMLElement).style, {\n gridTemplateAreas: \"none\",\n gridTemplateColumns: \"minmax(0, 1fr)\",\n gridTemplateRows: \"none\",\n height: \"auto\",\n });\n overview.prepend(host);\n }\n for (const title of overview.querySelectorAll<HTMLElement>(\".PieChart > .chart-title\")) {\n if (title.textContent !== \"CPU\" && title.textContent !== \"Memory\") continue;\n const panel = title.closest(\".PieChart\")?.parentElement;\n if (panel && panel.style.display !== \"none\") {\n if (!hidden.has(panel)) hidden.set(panel, panel.getAttribute(\"style\"));\n panel.style.display = \"none\";\n }\n }\n // native CSS modules retain [ClusterMetrics] in the generated class name\n for (const child of overview.children) {\n if (!(child instanceof HTMLElement) || child === host) continue;\n const nativeMetrics =\n child.matches('[class*=\"ClusterMetrics\"]') || child.querySelector('[class*=\"ClusterMetrics\"]');\n const unavailablePies =\n (child.matches('[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"]') ||\n child.querySelector(\n '[class*=\"noMetrics\"], [data-testid=\"no-metrics-message\"], [class*=\"cluster-pie-charts\"][class*=\"empty\"]',\n )) &&\n !child.querySelector(\".PieChart\");\n if ((nativeMetrics || unavailablePies) && child.style.display !== \"none\") {\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.display = \"none\";\n }\n if (!hidden.has(child)) hidden.set(child, child.getAttribute(\"style\"));\n child.style.gridArea = \"auto\";\n }\n const render = () => {\n if (host && (renderedSamples !== samples || renderedStatus !== status)) {\n ReactDOM.render(<MonitoringCharts samples={samples} status={status} />, host);\n renderedSamples = samples;\n renderedStatus = status;\n }\n };\n render();\n if (pendingGeneration === generation || Date.now() < nextSample) return;\n pendingGeneration = generation;\n nextSample = Date.now() + INTERVAL;\n const requestGeneration = generation;\n const isCurrent = () =>\n !stopped &&\n requestGeneration === generation &&\n document.visibilityState !== \"hidden\" &&\n overview?.isConnected === true &&\n Renderer.Catalog.activeCluster.get()?.metadata.uid === cluster;\n try {\n const [metrics, inventory, pods] = await Promise.allSettled([\n Renderer.K8sApi.nodesMetricsApi.list(),\n Promise.resolve().then(() => Renderer.K8sApi.nodesApi.list()),\n Promise.resolve().then(() => Renderer.K8sApi.podsApi.list()),\n ]);\n if (!isCurrent()) return;\n if (metrics.status !== \"fulfilled\") throw new Error(\"Metrics unavailable\");\n const sample = aggregateMetrics(metrics.value ?? []);\n const warnings: string[] = [];\n if (inventory.status === \"fulfilled\") {\n try {\n sample.resources = aggregateResources(\n inventory.value ?? [],\n pods.status === \"fulfilled\" ? (pods.value ?? undefined) : undefined,\n );\n const expected = new Set((inventory.value ?? []).map((node) => node.metadata.name));\n const observed = new Set((metrics.value ?? []).map((node) => node.metadata.name));\n sample.completeUsage =\n expected.size > 0 && expected.size === observed.size && [...expected].every((name) => observed.has(name));\n if (!sample.completeUsage)\n warnings.push(\n \"Partial node metrics coverage: usage is the reported subset; unused allocatable unavailable\",\n );\n } catch {\n warnings.push(\"Resource totals unavailable: incomplete or invalid node/pod data\");\n }\n } else\n warnings.push(\"Node inventory unavailable or access denied; usage coverage unverified, capacity unavailable\");\n if (pods.status !== \"fulfilled\")\n warnings.push(\"Cluster-wide Pods unavailable or access denied; requests unavailable\");\n sample.resourceStatus = warnings.join(\". \");\n samples = [...samples, sample].slice(-MAX_SAMPLES);\n status = \"\";\n } catch {\n if (!isCurrent()) return;\n status = \"Node metrics unavailable, stale or access denied. Last collected samples remain visible.\";\n } finally {\n if (pendingGeneration === requestGeneration) pendingGeneration = undefined;\n }\n if (!stopped && requestGeneration === generation) render();\n };\n const observer = new MutationObserver(() => {\n void update();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n const timer = window.setInterval(() => {\n void update();\n }, 1000);\n void update();\n return () => {\n stopped = true;\n generation++;\n window.clearInterval(timer);\n observer.disconnect();\n suppression.remove();\n restore();\n samples = [];\n };\n}\n"],"mappings":"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"}
|
|
@@ -53,6 +53,18 @@ function startPodMonitoring() {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
function mountPodMonitoring() {
|
|
56
|
+
const nativeSuppression = document.createElement("style");
|
|
57
|
+
const controllers = ".DeploymentDetails, .ReplicaSetDetails, .DaemonSetDetails, .StatefulSetDetails, .JobDetails, .CronJobDetails, [class*=ReplicationControllerDetails]";
|
|
58
|
+
nativeSuppression.dataset.powerDetailMonitoring = "";
|
|
59
|
+
nativeSuppression.textContent = `
|
|
60
|
+
.KubeObjectDetails:has(:is(${controllers})) .ResourceMetrics,
|
|
61
|
+
.KubeObjectDetails:has(:is(${controllers})) [data-time-range]:has(+ .ResourceMetrics),
|
|
62
|
+
.KubeObjectDetails:has(.PodDetails) [data-time-range]:has(+ .ResourceMetrics > [data-power-pod-monitoring]),
|
|
63
|
+
.KubeObjectDetails .ResourceMetrics:has(> [data-power-pod-monitoring]) > .loader {
|
|
64
|
+
display: none !important;
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
document.head.append(nativeSuppression);
|
|
56
68
|
let stopped = false;
|
|
57
69
|
let generation = 0;
|
|
58
70
|
let session = "";
|
|
@@ -78,6 +90,58 @@ function mountPodMonitoring() {
|
|
|
78
90
|
graph = void 0;
|
|
79
91
|
renderedSamples = void 0;
|
|
80
92
|
};
|
|
93
|
+
let controlledMetrics;
|
|
94
|
+
const tabAttributes = /* @__PURE__ */ new Map();
|
|
95
|
+
const saveAttributes = (element, names) => {
|
|
96
|
+
if (!tabAttributes.has(element)) tabAttributes.set(element, new Map(names.map((name) => [name, element.getAttribute(name)])));
|
|
97
|
+
};
|
|
98
|
+
const unsupportedTab = (target) => {
|
|
99
|
+
const radio = target instanceof Element ? target.closest(".Radio") : null;
|
|
100
|
+
return radio && ["Network", "Filesystem"].includes(radio.querySelector(".label")?.textContent ?? "");
|
|
101
|
+
};
|
|
102
|
+
const blockUnsupported = (event) => {
|
|
103
|
+
if (!unsupportedTab(event.target)) return;
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
event.stopImmediatePropagation();
|
|
106
|
+
};
|
|
107
|
+
const tabEvents = [
|
|
108
|
+
"click",
|
|
109
|
+
"pointerdown",
|
|
110
|
+
"mousedown",
|
|
111
|
+
"keydown",
|
|
112
|
+
"change"
|
|
113
|
+
];
|
|
114
|
+
const restoreTabs = () => {
|
|
115
|
+
for (const [element, attributes] of tabAttributes) for (const [name, value] of attributes) if (value === null) element.removeAttribute(name);
|
|
116
|
+
else element.setAttribute(name, value);
|
|
117
|
+
tabAttributes.clear();
|
|
118
|
+
for (const event of tabEvents) controlledMetrics?.removeEventListener(event, blockUnsupported, true);
|
|
119
|
+
controlledMetrics = void 0;
|
|
120
|
+
};
|
|
121
|
+
const restrictTabs = (metrics) => {
|
|
122
|
+
if (controlledMetrics !== metrics) {
|
|
123
|
+
restoreTabs();
|
|
124
|
+
controlledMetrics = metrics;
|
|
125
|
+
for (const event of tabEvents) metrics.addEventListener(event, blockUnsupported, true);
|
|
126
|
+
}
|
|
127
|
+
for (const radio of metrics.querySelectorAll(".Radio")) {
|
|
128
|
+
if (!unsupportedTab(radio)) continue;
|
|
129
|
+
saveAttributes(radio, [
|
|
130
|
+
"style",
|
|
131
|
+
"hidden",
|
|
132
|
+
"aria-hidden"
|
|
133
|
+
]);
|
|
134
|
+
if (radio.style.display !== "none") radio.style.display = "none";
|
|
135
|
+
radio.hidden = true;
|
|
136
|
+
radio.setAttribute("aria-hidden", "true");
|
|
137
|
+
const input = radio.querySelector("input");
|
|
138
|
+
if (input) {
|
|
139
|
+
saveAttributes(input, ["disabled"]);
|
|
140
|
+
input.disabled = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (unsupportedTab(metrics.querySelector(".Radio.checked"))) [...metrics.querySelectorAll(".Radio")].find((radio) => radio.querySelector(".label")?.textContent === "CPU")?.click();
|
|
144
|
+
};
|
|
81
145
|
const reset = () => {
|
|
82
146
|
generation++;
|
|
83
147
|
session = "";
|
|
@@ -86,6 +150,7 @@ function mountPodMonitoring() {
|
|
|
86
150
|
status = "";
|
|
87
151
|
nextRead = 0;
|
|
88
152
|
restore();
|
|
153
|
+
restoreTabs();
|
|
89
154
|
};
|
|
90
155
|
const update = async () => {
|
|
91
156
|
if (stopped) return;
|
|
@@ -113,6 +178,8 @@ function mountPodMonitoring() {
|
|
|
113
178
|
"Network",
|
|
114
179
|
"Filesystem"
|
|
115
180
|
].every((label) => [...element.querySelectorAll(".Radio .label")].some((node) => node.textContent === label)));
|
|
181
|
+
if (metrics && visible(metrics)) restrictTabs(metrics);
|
|
182
|
+
else restoreTabs();
|
|
116
183
|
const selected = metrics?.querySelector(".Radio.checked .label")?.textContent;
|
|
117
184
|
const nativeGraph = metrics?.querySelector(":scope > .graph");
|
|
118
185
|
if (!metrics || !visible(metrics) || !nativeGraph || selected !== "CPU" && selected !== "Memory") {
|
|
@@ -205,6 +272,7 @@ function mountPodMonitoring() {
|
|
|
205
272
|
stopped = true;
|
|
206
273
|
reset();
|
|
207
274
|
window.clearInterval(timer);
|
|
275
|
+
nativeSuppression.remove();
|
|
208
276
|
};
|
|
209
277
|
}
|
|
210
278
|
//#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":"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"}
|
|
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 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(<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 nativeSuppression.remove();\n };\n}\n"],"mappings":"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"}
|