@gtadi/k8s-node-debugger 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/health-view.js +46 -16
- package/public/style.css +7 -0
- package/src/probes.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtadi/k8s-node-debugger",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Spin up a privileged debug pod on a target Kubernetes node and inspect its network stack (iptables, resolv.conf, conntrack, routes, sockets) from a browser UI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"k8s-node-debugger": "bin/k8s-node-debugger.js"
|
package/public/health-view.js
CHANGED
|
@@ -983,7 +983,7 @@
|
|
|
983
983
|
const sections = {};
|
|
984
984
|
let cur = null;
|
|
985
985
|
for (const line of raw.split('\n')) {
|
|
986
|
-
const sec = line.match(/^=(SNAPS|MOUNTS|CRICTL)=$/);
|
|
986
|
+
const sec = line.match(/^=(SNAPS|WRITABLE|MOUNTS|CRICTL)=$/);
|
|
987
987
|
if (sec) { cur = sec[1]; sections[cur] = []; continue; }
|
|
988
988
|
if (cur && line.trim()) sections[cur].push(line);
|
|
989
989
|
}
|
|
@@ -995,7 +995,11 @@
|
|
|
995
995
|
if (m) snapKb.set(m[2], parseInt(m[1]));
|
|
996
996
|
}
|
|
997
997
|
|
|
998
|
-
// 2.
|
|
998
|
+
// 2. Writable snapshots (have a work/ dir) = container layers (running or stopped).
|
|
999
|
+
// Image cache layers do NOT have work/ dirs. This lets us classify unmapped snaps.
|
|
1000
|
+
const writableSnaps = new Set((sections.WRITABLE || []).map(l => l.trim()).filter(Boolean));
|
|
1001
|
+
|
|
1002
|
+
// 3. Build snapId → containerHash from overlay mount lines.
|
|
999
1003
|
// Mount target contains: /k8s.io/{64-char-hash}/rootfs
|
|
1000
1004
|
// Mount options contain: snapshots/NNN/ references
|
|
1001
1005
|
const snapToHash = new Map();
|
|
@@ -1008,7 +1012,7 @@
|
|
|
1008
1012
|
}
|
|
1009
1013
|
}
|
|
1010
1014
|
|
|
1011
|
-
//
|
|
1015
|
+
// 4. Parse crictl ps -a using header positions for fixed-width columns.
|
|
1012
1016
|
const hashToInfo = new Map();
|
|
1013
1017
|
const crictlLines = (sections.CRICTL || []).filter(l => l.trim());
|
|
1014
1018
|
if (crictlLines.length > 1) {
|
|
@@ -1036,7 +1040,7 @@
|
|
|
1036
1040
|
}
|
|
1037
1041
|
}
|
|
1038
1042
|
|
|
1039
|
-
//
|
|
1043
|
+
// 5. Build ranked rows: snapId sorted by KB desc → join hash → join container info
|
|
1040
1044
|
const rows = [...snapKb.entries()]
|
|
1041
1045
|
.sort((a, b) => b[1] - a[1])
|
|
1042
1046
|
.slice(0, 25)
|
|
@@ -1045,7 +1049,8 @@
|
|
|
1045
1049
|
const info = hash
|
|
1046
1050
|
? (hashToInfo.get(hash) || hashToInfo.get(hash.substring(0, 12)))
|
|
1047
1051
|
: null;
|
|
1048
|
-
|
|
1052
|
+
const isWritable = writableSnaps.has(snapId);
|
|
1053
|
+
return { snapId, kb, hash, info, isWritable };
|
|
1049
1054
|
});
|
|
1050
1055
|
|
|
1051
1056
|
const wrap = document.createElement('div');
|
|
@@ -1059,15 +1064,35 @@
|
|
|
1059
1064
|
}
|
|
1060
1065
|
|
|
1061
1066
|
const totalKb = [...snapKb.values()].reduce((a, b) => a + b, 0);
|
|
1062
|
-
const mappedCount
|
|
1067
|
+
const mappedCount = rows.filter(r => r.info).length;
|
|
1068
|
+
const stoppedCount = rows.filter(r => !r.info && r.isWritable).length;
|
|
1069
|
+
const imageCount = rows.filter(r => !r.info && !r.isWritable).length;
|
|
1063
1070
|
const maxKb = rows[0].kb || 1;
|
|
1064
1071
|
|
|
1065
1072
|
const listHtml = rows.map((r, i) => {
|
|
1066
1073
|
const pct = Math.min(Math.round(r.kb / maxKb * 100), 100);
|
|
1067
1074
|
const cls = r.kb >= maxKb * 0.5 ? 'hv-crit' : r.kb >= maxKb * 0.2 ? 'hv-warn' : 'hv-ok';
|
|
1068
1075
|
const stateCls = r.info?.state === 'Running' ? 'hv-ok' : r.info?.state ? 'hv-warn' : '';
|
|
1069
|
-
|
|
1070
|
-
|
|
1076
|
+
|
|
1077
|
+
// Determine label for unmapped snapshots using the work/ dir heuristic
|
|
1078
|
+
let podLabel, nameLabel, stateTag, typeTag = '';
|
|
1079
|
+
if (r.info) {
|
|
1080
|
+
podLabel = r.info.pod || r.info.name;
|
|
1081
|
+
nameLabel = r.info.name !== r.info.pod ? r.info.name : '';
|
|
1082
|
+
stateTag = r.info.state
|
|
1083
|
+
? `<span class="st-cont-state ${stateCls}">${h(r.info.state)}</span>` : '';
|
|
1084
|
+
} else if (r.isWritable) {
|
|
1085
|
+
podLabel = r.hash ? r.hash.substring(0, 20) + '…' : 'unknown container';
|
|
1086
|
+
nameLabel = '';
|
|
1087
|
+
stateTag = '<span class="st-cont-state hv-warn">Stopped</span>';
|
|
1088
|
+
typeTag = '<span class="st-cont-type-tag st-tag-stopped">stopped container</span>';
|
|
1089
|
+
} else {
|
|
1090
|
+
podLabel = 'image cache layer';
|
|
1091
|
+
nameLabel = '';
|
|
1092
|
+
stateTag = '';
|
|
1093
|
+
typeTag = '<span class="st-cont-type-tag st-tag-image">image layer</span>';
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1071
1096
|
return `
|
|
1072
1097
|
<div class="st-cont-row">
|
|
1073
1098
|
<div class="st-cont-header">
|
|
@@ -1076,7 +1101,8 @@
|
|
|
1076
1101
|
<span class="st-cont-pod">${h(podLabel)}</span>
|
|
1077
1102
|
${nameLabel ? `<span class="st-cont-name">${h(nameLabel)}</span>` : ''}
|
|
1078
1103
|
</div>
|
|
1079
|
-
${
|
|
1104
|
+
${stateTag}
|
|
1105
|
+
${typeTag}
|
|
1080
1106
|
<span class="st-cont-size ${cls}">${hBytes(r.kb)}</span>
|
|
1081
1107
|
</div>
|
|
1082
1108
|
<div class="hv-gauge-bar st-cont-bar">
|
|
@@ -1084,7 +1110,7 @@
|
|
|
1084
1110
|
</div>
|
|
1085
1111
|
<div class="st-cont-meta">
|
|
1086
1112
|
<span class="st-cont-meta-tag">snap #${h(r.snapId)}</span>
|
|
1087
|
-
${r.hash ? `<span class="st-cont-meta-tag">${h(r.hash.substring(0, 20))}…</span>` : '
|
|
1113
|
+
${r.hash ? `<span class="st-cont-meta-tag">${h(r.hash.substring(0, 20))}…</span>` : ''}
|
|
1088
1114
|
</div>
|
|
1089
1115
|
</div>`;
|
|
1090
1116
|
}).join('');
|
|
@@ -1096,16 +1122,20 @@
|
|
|
1096
1122
|
</div>
|
|
1097
1123
|
<div class="st-summary">
|
|
1098
1124
|
<div class="hv-grid-item">
|
|
1099
|
-
<div class="hv-grid-label">
|
|
1100
|
-
<div class="hv-grid-val">${
|
|
1125
|
+
<div class="hv-grid-label">Total snapshot storage</div>
|
|
1126
|
+
<div class="hv-grid-val">${hBytes(totalKb)}</div>
|
|
1101
1127
|
</div>
|
|
1102
1128
|
<div class="hv-grid-item">
|
|
1103
|
-
<div class="hv-grid-label">
|
|
1104
|
-
<div class="hv-grid-val">${mappedCount}
|
|
1129
|
+
<div class="hv-grid-label">Running pods (top ${rows.length})</div>
|
|
1130
|
+
<div class="hv-grid-val" style="color:var(--ok)">${mappedCount}</div>
|
|
1105
1131
|
</div>
|
|
1106
1132
|
<div class="hv-grid-item">
|
|
1107
|
-
<div class="hv-grid-label">
|
|
1108
|
-
<div class="hv-grid-val">${
|
|
1133
|
+
<div class="hv-grid-label">Stopped containers</div>
|
|
1134
|
+
<div class="hv-grid-val" style="color:#d29922">${stoppedCount}</div>
|
|
1135
|
+
</div>
|
|
1136
|
+
<div class="hv-grid-item">
|
|
1137
|
+
<div class="hv-grid-label">Image cache layers</div>
|
|
1138
|
+
<div class="hv-grid-val" style="color:var(--fg-dim)">${imageCount}</div>
|
|
1109
1139
|
</div>
|
|
1110
1140
|
</div>
|
|
1111
1141
|
<div class="st-cont-list">${listHtml}</div>`;
|
package/public/style.css
CHANGED
|
@@ -999,3 +999,10 @@ kbd {
|
|
|
999
999
|
.st-cont-meta-unmapped {
|
|
1000
1000
|
font-size: 11px; color: var(--fg-dim); font-style: italic; opacity: 0.6;
|
|
1001
1001
|
}
|
|
1002
|
+
.st-cont-type-tag {
|
|
1003
|
+
font-size: 10px; font-weight: 700; font-family: var(--mono);
|
|
1004
|
+
padding: 2px 7px; border-radius: 4px; flex-shrink: 0;
|
|
1005
|
+
text-transform: uppercase; letter-spacing: .04em;
|
|
1006
|
+
}
|
|
1007
|
+
.st-tag-stopped { background: #2b2000; color: #d29922; border: 1px solid #5a4500; }
|
|
1008
|
+
.st-tag-image { background: var(--bg-3); color: var(--fg-dim); border: 1px solid var(--border); }
|
package/src/probes.js
CHANGED
|
@@ -248,8 +248,8 @@ const PROBES = [
|
|
|
248
248
|
group: 'Storage',
|
|
249
249
|
desc: 'Ranked list of containers by disk usage. Maps containerd snapshot sizes to pod names via overlay mounts and crictl.',
|
|
250
250
|
commands: [
|
|
251
|
-
"echo '=SNAPS='; nsenter -t 1 -m -- du -d 1 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots 2>/dev/null | sort -rn |
|
|
252
|
-
"echo '=SNAPS='; du -d 1 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots 2>/dev/null | sort -rn |
|
|
251
|
+
"echo '=SNAPS='; nsenter -t 1 -m -- du -d 1 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots 2>/dev/null | sort -rn; echo '=WRITABLE='; ls -d /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/*/work 2>/dev/null | sed 's|.*/snapshots/||;s|/work||'; echo '=MOUNTS='; nsenter -t 1 -m -- mount 2>/dev/null | grep snapshots; echo '=CRICTL='; nsenter -t 1 -m -u -i -n -p -- crictl ps -a 2>/dev/null",
|
|
252
|
+
"echo '=SNAPS='; du -d 1 /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots 2>/dev/null | sort -rn; echo '=WRITABLE='; ls -d /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/*/work 2>/dev/null | sed 's|.*/snapshots/||;s|/work||'; echo '=MOUNTS='; mount 2>/dev/null | grep snapshots; echo '=CRICTL='; crictl ps -a 2>/dev/null",
|
|
253
253
|
],
|
|
254
254
|
},
|
|
255
255
|
|