diff --git a/config/agentrun.yaml b/config/agentrun.yaml index dd41f1d4..dad5dfe5 100644 --- a/config/agentrun.yaml +++ b/config/agentrun.yaml @@ -640,6 +640,7 @@ controlPlane: sessionPvcRetention: enabled: true prefixes: + - agentrun-v01-session- - agentrun-release-session- - agentrun-nc01-release-session- maxDeletePerRun: 1000 diff --git a/scripts/src/agentrun/cleanup-session-pvcs.mjs b/scripts/src/agentrun/cleanup-session-pvcs.mjs index 13a9c456..58ed0c82 100644 --- a/scripts/src/agentrun/cleanup-session-pvcs.mjs +++ b/scripts/src/agentrun/cleanup-session-pvcs.mjs @@ -4,12 +4,30 @@ function runJson(args) { return JSON.parse(execFileSync("kubectl", args, { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 })); } -function duBytes(path) { - if (!path || !path.startsWith("/var/lib/rancher/k3s/storage/")) return null; - const result = spawnSync("du", ["-sb", path], { encoding: "utf8", timeout: 8000 }); - if (result.status !== 0) return null; - const value = Number(result.stdout.trim().split(/\s+/u)[0]); - return Number.isFinite(value) ? value : null; +function duBytesByPath(paths) { + const allowed = [...new Set(paths.filter((path) => path?.startsWith("/var/lib/rancher/k3s/storage/")))]; + if (allowed.length === 0) { + return { sizes: new Map(), summary: { requested: 0, measured: 0, timedOut: false, status: 0 } }; + } + const result = spawnSync("du", ["-sb", ...allowed], { + encoding: "utf8", + timeout: 15000, + maxBuffer: 8 * 1024 * 1024, + }); + const sizes = new Map(); + for (const line of result.stdout?.split(/\r?\n/u) || []) { + const match = line.match(/^(\d+)\s+(.+)$/u); + if (match) sizes.set(match[2], Number(match[1])); + } + return { + sizes, + summary: { + requested: allowed.length, + measured: sizes.size, + timedOut: result.error?.code === "ETIMEDOUT", + status: result.status, + }, + }; } const namespace = process.env.NAMESPACE; @@ -41,8 +59,8 @@ for (const pod of podData.items || []) { } } -const candidates = []; -const protectedRows = []; +const candidateFacts = []; +const protectedFacts = []; for (const pvc of pvcData.items || []) { const name = pvc.metadata?.name || ""; const matchedPrefix = prefixes.find((prefix) => name.startsWith(prefix)); @@ -63,15 +81,24 @@ for (const pvc of pvcData.items || []) { reclaimPolicy, activeMountCount: activeMountPods.length, activeMountPods: activeMountPods.slice(0, 5), - estimatedBytes: duBytes(hostPath), + hostPath, }; if (activeMountPods.length > 0 || storageClass !== "local-path" || reclaimPolicy !== "Delete") { - protectedRows.push({ ...row, reason: activeMountPods.length > 0 ? "active-mount" : "not-local-path-delete" }); + protectedFacts.push({ ...row, reason: activeMountPods.length > 0 ? "active-mount" : "not-local-path-delete" }); } else { - candidates.push(row); + candidateFacts.push(row); } } +const sizeScan = duBytesByPath(candidateFacts.map((item) => item.hostPath)); +const candidates = candidateFacts.map(({ hostPath, ...item }) => ({ + ...item, + estimatedBytes: sizeScan.sizes.get(hostPath) ?? null, +})); +const protectedRows = protectedFacts.map(({ hostPath, ...item }) => ({ + ...item, + estimatedBytes: sizeScan.sizes.get(hostPath) ?? null, +})); candidates.sort((a, b) => (b.estimatedBytes || 0) - (a.estimatedBytes || 0)); const selected = candidates.slice(0, limit); const result = { @@ -84,9 +111,10 @@ const result = { candidatePvcCount: candidates.length, selectedPvcCount: selected.length, protectedPvcCount: protectedRows.length, + sizeScan: sizeScan.summary, estimatedReclaimBytes: selected.reduce((sum, item) => sum + (item.estimatedBytes || 0), 0), - selectedPreview: selected.slice(0, 12), - protectedPreview: protectedRows.slice(0, 12), + selectedPreview: selected.slice(0, 5), + protectedPreview: protectedRows.slice(0, 5), deletedPvcCount: 0, valuesPrinted: false, };