@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtadi/k8s-node-debugger",
3
- "version": "1.1.0",
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"
@@ -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. Build snapId containerHash from overlay mount lines.
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
- // 3. Parse crictl ps -a using header positions for fixed-width columns.
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
- // 4. Build ranked rows: snapId sorted by KB desc → join hash → join container info
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
- return { snapId, kb, hash, info };
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 = rows.filter(r => r.info).length;
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
- const podLabel = r.info?.pod || (r.hash ? r.hash.substring(0, 16) + '…' : '—');
1070
- const nameLabel = r.info?.name || '';
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
- ${r.info?.state ? `<span class="st-cont-state ${stateCls}">${h(r.info.state)}</span>` : ''}
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>` : '<span class="st-cont-meta-unmapped">unmapped</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">Snapshots tracked</div>
1100
- <div class="hv-grid-val">${snapKb.size}</div>
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">Mapped to pods</div>
1104
- <div class="hv-grid-val">${mappedCount} / ${rows.length}</div>
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">Total snapshot storage</div>
1108
- <div class="hv-grid-val">${hBytes(totalKb)}</div>
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 | head -40; 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 | head -40; echo '=MOUNTS='; mount 2>/dev/null | grep snapshots; echo '=CRICTL='; crictl ps -a 2>/dev/null",
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