From 1a5313eabfd2affdb727b0985343d7b75c6a5782 Mon Sep 17 00:00:00 2001 From: acoshift Date: Thu, 11 Jun 2026 18:08:18 +0700 Subject: [PATCH 1/3] Strip shared pod-name prefix from metric chart legends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-pod metric lines are keyed by the full k8s pod name (`---`). That prefix is identical on every pod of a deployment, so in the legend it's pure noise that pushes the only distinguishing part — the pod hash — off the edge. With id-based deployment naming the prefix is also an opaque `0d` resource name the user never chose. Chart.svelte now computes the longest prefix common to every line in a panel, trimmed back to a '-' boundary so whole name segments are removed (never a hash mid-token), and shows only the remaining suffix ("Usage · x2k9p"). When lines share no '-'-bounded prefix the function returns 0 and labels are unchanged, so non-pod charts (project usage, aggregate mode) are unaffected. Also makes the mock `deployment.metrics` fixture faithful to live (non-aggregate) mode: two pods of an id-named deployment, every line keyed by full pod name — which is what exercises the new stripping. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/lib/components/Chart.svelte | 37 ++++++++++++++++++++++++++++++--- src/lib/server/mock.js | 25 +++++++++++++++------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/lib/components/Chart.svelte b/src/lib/components/Chart.svelte index 44539d1e..f0ebaa30 100644 --- a/src/lib/components/Chart.svelte +++ b/src/lib/components/Chart.svelte @@ -48,10 +48,41 @@ const fmtY = $derived(unit === 'bytes' ? formatBytes : formatNumber) + // Every pod of one deployment shares a long, identical name prefix — the k8s + // resource name and project id (`web-123-…`, or `0d456-123-…` for id-named + // deployments). That prefix is the same on every line, so in the legend it's + // pure noise that pushes the part that actually differs (the pod hash) off + // the edge. Strip the longest prefix common to all lines, trimmed back to a + // '-' boundary so whole name segments are removed (never a hash mid-token). + // This also hides the otherwise-opaque `0d` resource name. + const podPrefixLen = $derived.by(() => { + /** @type {string[]} */ + const names = [] + for (const s of series ?? []) { + for (const l of (s.lines ?? [])) names.push(l.name ?? '') + } + if (names.length < 2) return 0 + let p = names[0] + for (let i = 1; i < names.length && p; i++) { + const n = names[i] + let j = 0 + while (j < p.length && j < n.length && p[j] === n[j]) j++ + p = p.slice(0, j) + } + const cut = p.lastIndexOf('-') + return cut >= 0 ? cut + 1 : 0 + }) + + /** @param {string} name */ + function podLabel (name) { + return name.slice(podPrefixLen) || name + } + // Flatten {prefix, lines[]} into one drawable line each. A single-line series // keeps the bare prefix ("Usage"); multi-line series disambiguate with the - // line's own name ("Usage · web"). The first solid line gets the area fill so - // the panel reads as one primary trend with reference lines layered over it. + // pod's distinguishing suffix ("Usage · x2k9p"). The first solid line gets the + // area fill so the panel reads as one primary trend with reference lines + // layered over it. const flat = $derived.by(() => { let areaUsed = false /** @type {import('$lib/charts/util').LineSeries[]} */ @@ -63,7 +94,7 @@ const area = !dashed && !areaUsed if (area) areaUsed = true out.push({ - name: lines.length > 1 ? `${s.prefix} · ${l.name}` : s.prefix, + name: lines.length > 1 ? `${s.prefix} · ${podLabel(l.name)}` : s.prefix, color: resolveColor(s.color), dashed, area, diff --git a/src/lib/server/mock.js b/src/lib/server/mock.js index 77a329ef..94fb4d43 100644 --- a/src/lib/server/mock.js +++ b/src/lib/server/mock.js @@ -48,6 +48,17 @@ function metricLine (name, base) { return [{ name, points }] } +// Two pods of one id-named deployment (`0d---`), +// matching how the live (non-aggregate) `deployment.metrics` lines are keyed by +// full pod name. Lets the metrics page exercise the legend's shared-prefix +// stripping (only the trailing pod hash should show). +const MOCK_PODS = ['0d128-77-7d8f9b6c5-x2k9p', '0d128-77-7d8f9b6c5-q8m2t'] + +/** @param {number} base */ +function metricPodLines (base) { + return MOCK_PODS.map((name, i) => metricLine(name, base * (i === 0 ? 1 : 0.72))[0]) +} + /** * dailyMetricLine builds a single daily series — one [unixSeconds, value] point * per day at midnight for the trailing 30 days — for the daily usage charts. @@ -870,13 +881,13 @@ const handlers = { } })), 'deployment.metrics': () => ok({ - cpuUsage: metricLine('web', 0.3), - cpuLimit: metricLine('limit', 0.5), - memoryUsage: metricLine('web', 268435456), - memory: metricLine('allocated', 402653184), - memoryLimit: metricLine('limit', 536870912), - requests: metricLine('web', 120), - egress: metricLine('web', 1048576) + cpuUsage: metricPodLines(0.3), + cpuLimit: metricPodLines(0.5), + memoryUsage: metricPodLines(268435456), + memory: metricPodLines(402653184), + memoryLimit: metricPodLines(536870912), + requests: metricPodLines(120), + egress: metricPodLines(1048576) }), 'disk.list': () => list(disks), From 25c15193473d8ad34feb63532a2bb2f4806f2ba1 Mon Sep 17 00:00:00 2001 From: acoshift Date: Thu, 11 Jun 2026 19:17:42 +0700 Subject: [PATCH 2/3] Strip pod-name prefix on the logs and events pages too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the metrics-legend prefix stripping to the deployment logs and events pages. Pod names there are the full k8s name (`---`) — the logs chip shows it verbatim, and event messages embed it in free text. The shared `-` part is noise (and, for id-named deployments, the opaque `0d`). New `$lib/deployment/podName.js` derives the prefix from the deployment's `internalAddress` (which the API sets to exactly `-`) and strips it wherever it appears, with a `0d-` fallback so id-named pods are hidden even if internalAddress is unavailable. - logs: chip shows the `-` suffix; full name kept for the hash color and the hover title. - events: pod refs in reason/message are stripped; full text kept on hover. Container names and namespaces are left intact. Mocks made faithful so this is exercisable offline: deployment internalAddress is now the real `-` format, and the mock log/event feeds use full id-named pod names. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/lib/deployment/podName.js | 35 +++++++++++++++++++ src/lib/server/mock.js | 4 ++- .../deployment/(detail)/events/+page.svelte | 10 ++++-- .../deployment/(detail)/logs/+page.svelte | 7 +++- src/routes/api/mock-events/+server.js | 10 +++--- src/routes/api/mock-logs/+server.js | 9 ++++- 6 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/lib/deployment/podName.js diff --git a/src/lib/deployment/podName.js b/src/lib/deployment/podName.js new file mode 100644 index 00000000..bb4c6db5 --- /dev/null +++ b/src/lib/deployment/podName.js @@ -0,0 +1,35 @@ +// Pod names are `---`. The +// `-` part is the deployment's k8s service name — exactly +// what the API returns as `internalAddress` — and is identical on every pod, so +// repeating it (in the logs pod chip, or inside event messages) is pure noise +// that buries the part identifying the individual pod. For id-named deployments +// it's also the opaque `0d` resource name the user never chose. These +// helpers strip that prefix wherever it appears, leaving just the pod-specific +// suffix (`-`). + +/** @param {string} s */ +function escapeRegExp (s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} + +/** + * Build a function that strips the deployment's `--` prefix + * from any text — a bare pod name (logs) or pod references embedded in free-text + * event messages. + * + * @param {{ internalAddress?: string }} [deployment] + * @returns {(text?: string) => string} + */ +export function podPrefixStripper (deployment) { + const addr = (deployment?.internalAddress ?? '').trim() + const alts = [] + // Primary: the exact service name (`-`). Guard against + // the field ever being an IP or empty by requiring the `-` + // shape so we never build a prefix that matches arbitrary text. + if (/^[a-z0-9-]+-\d+$/i.test(addr)) alts.push(escapeRegExp(addr)) + // Fallback: any id-based prefix (`0d-`), so the opaque + // resource name is still hidden even when internalAddress is unavailable. + alts.push('0d\\d+-\\d+') + const re = new RegExp('(?:' + alts.join('|') + ')-', 'g') + return (text) => (text ?? '').replace(re, '') +} diff --git a/src/lib/server/mock.js b/src/lib/server/mock.js index 94fb4d43..3f28184d 100644 --- a/src/lib/server/mock.js +++ b/src/lib/server/mock.js @@ -174,7 +174,9 @@ function deployment (project = 'acme') { podsUrl: '', statusUrl: HEALTHY_STATUS_URL, address: '203.0.113.10', - internalAddress: '10.0.0.10', + // `-` (the in-cluster service name); id-named here so + // the logs/events pages exercise pod-name prefix stripping. + internalAddress: '0d128-77', status: 'success', action: 'deploy', allocatedPrice: 120.5, diff --git a/src/routes/(auth)/(project)/deployment/(detail)/events/+page.svelte b/src/routes/(auth)/(project)/deployment/(detail)/events/+page.svelte index e4e8a2a0..1fed42a7 100644 --- a/src/routes/(auth)/(project)/deployment/(detail)/events/+page.svelte +++ b/src/routes/(auth)/(project)/deployment/(detail)/events/+page.svelte @@ -1,10 +1,16 @@