fix: isolate provider egress tunnel diagnostics

This commit is contained in:
Codex
2026-06-28 16:15:41 +00:00
parent d225fd1a61
commit 4a636abb73
9 changed files with 368 additions and 25 deletions
+85
View File
@@ -199,6 +199,91 @@ export async function debugSshPool(_config: UniDeskConfig, providerId: string):
};
}
export async function debugEgressProxy(_config: UniDeskConfig, providerId: string): Promise<unknown> {
const nodesResponse = await coreInternalFetch("/api/nodes");
const body = recordValue(recordValue(nodesResponse).body);
const nodes = arrayValue(body.nodes);
const node = nodes
.map((item) => recordValue(item))
.find((item) => item.providerId === providerId) ?? null;
if (node === null) {
return {
ok: false,
providerId,
degradedReason: "provider-not-found",
nodesFetch: nodesResponse,
next: { fullHealth: "bun scripts/cli.ts debug health" },
};
}
const labels = recordValue(node.labels);
const active = Number(labels.providerGatewayEgressProxyActiveTunnels ?? 0);
const pending = Number(labels.providerGatewayEgressProxyPendingTunnels ?? 0);
const opened = Number(labels.providerGatewayEgressProxyOpenedTunnels ?? 0);
const stale = Number(labels.providerGatewayEgressProxyStaleTunnels ?? 0);
const enabled = labels.providerGatewayEgressProxy === true;
const connected = labels.providerGatewayEgressProxyConnected === true;
const sshReady = Number(labels.providerGatewaySshDataPoolReady ?? 0);
const activeTunnelDetails = arrayValue(labels.providerGatewayEgressProxyActiveTunnelDetails);
const recentClosedTunnels = arrayValue(labels.providerGatewayEgressProxyRecentClosedTunnels);
const ok = enabled && connected && stale === 0;
return {
ok,
providerId,
node: {
providerId: node.providerId,
name: node.name,
status: node.status,
lastHeartbeat: node.lastHeartbeat ?? null,
updatedAt: node.updatedAt ?? null,
},
egressProxy: {
enabled,
connected,
port: labels.providerGatewayEgressProxyPort ?? null,
activeTunnels: Number.isFinite(active) ? active : 0,
pendingTunnels: Number.isFinite(pending) ? pending : 0,
openedTunnels: Number.isFinite(opened) ? opened : 0,
staleTunnels: Number.isFinite(stale) ? stale : 0,
oldestTunnelAgeMs: labels.providerGatewayEgressProxyOldestTunnelAgeMs ?? null,
policy: {
openTimeoutMs: labels.providerGatewayEgressProxyOpenTimeoutMs ?? null,
idleTimeoutMs: labels.providerGatewayEgressProxyIdleTimeoutMs ?? null,
maxTunnelAgeMs: labels.providerGatewayEgressProxyMaxTunnelAgeMs ?? null,
staleTunnelIdleMs: labels.providerGatewayEgressProxyStaleTunnelIdleMs ?? null,
maxPendingBytes: labels.providerGatewayEgressProxyMaxPendingBytes ?? null,
},
activeTunnelDetails,
activeTunnelDetailsCount: activeTunnelDetails.length,
recentClosedTunnels,
recentClosedTunnelCount: recentClosedTunnels.length,
},
controlPlaneSeparation: {
sshDataTransport: stringValue(labels.providerGatewaySshDataTransport),
sshDataPoolReady: Number.isFinite(sshReady) ? sshReady : 0,
sshDataPoolDesired: labels.providerGatewaySshDataPoolDesired ?? null,
sshDataPoolClaimed: labels.providerGatewaySshDataPoolClaimed ?? null,
note: "SSH/control readiness is reported separately from egress download tunnel activity.",
},
classification: !enabled
? "egress-proxy-disabled"
: !connected
? "egress-proxy-core-channel-disconnected"
: stale > 0
? "egress-tunnel-stale"
: active > 0
? "egress-tunnel-active"
: "egress-proxy-ready",
outputPolicy: {
redaction: "activeTunnelDetails expose target host/port and path/query-key summary only; URL credentials, headers, tokens and query values are not emitted.",
boundedRecentClosedTunnels: true,
},
next: {
smoke: `trans ${providerId} argv true`,
fullHealth: "bun scripts/cli.ts debug health",
},
};
}
async function waitForTask(taskId: string, timeoutMs: number): Promise<unknown> {
const started = Date.now();
let latest: unknown = null;
+4 -2
View File
@@ -95,6 +95,7 @@ export function rootHelp(): unknown {
{ command: "job cancel <jobId>", description: "Cancel a queued/running async job through the .state/jobs control entry and keep a terminal canceled record." },
{ command: "debug health", description: "Probe internal core, nodes, system/Docker status, frontend, provider ingress, and public boundary." },
{ command: "debug ssh-pool <providerId>", description: "Show bounded host.ssh.tcp-pool labels for one provider, including ready/claimed/desired/lastError." },
{ command: "debug egress-proxy <providerId>", description: "Show provider-gateway egress proxy tunnel counts, stale tunnel diagnosis, active target summaries, and recent closed tunnel lifecycle without URL credential leakage." },
{ command: "debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]", description: "Submit a real internal-core dispatch request for CLI debugging." },
{ command: "debug task <taskId|latest>", description: "Read a dispatched task record from internal core for CLI debugging." },
{ command: "network perf [--service code-queue --path /api/tasks/overview?limit=30 --count N --concurrency N --label before|after]", description: "Benchmark frontend -> backend-core -> provider/adapter user-service networking and report latency/proxy-mode distributions." },
@@ -539,15 +540,16 @@ function jobHelp(): unknown {
function debugHelp(): unknown {
return {
command: "debug health|ssh-pool|dispatch|task",
command: "debug health|ssh-pool|egress-proxy|dispatch|task",
output: "json",
usage: [
"bun scripts/cli.ts debug health",
"bun scripts/cli.ts debug ssh-pool <providerId>",
"bun scripts/cli.ts debug egress-proxy <providerId>",
"bun scripts/cli.ts debug dispatch [providerId] [docker.ps|provider.upgrade|host.ssh|microservice.http|echo] [--wait-ms N]",
"bun scripts/cli.ts debug task <taskId|latest>",
],
description: "Debug the real core/provider/dispatch paths. ssh-pool returns bounded host.ssh.tcp-pool labels for one provider; do not use debug commands as formal TEST.md acceptance steps.",
description: "Debug the real core/provider/dispatch paths. ssh-pool and egress-proxy are separate so SSH/control readiness is not conflated with long download tunnel activity; do not use debug commands as formal TEST.md acceptance steps.",
};
}
+14
View File
@@ -154,6 +154,20 @@ function providerGatewaySignal(debug: unknown, providerId: string): ProviderTria
providerGatewayVersion: labels.providerGatewayVersion ?? null,
hostSshConfigured: labels.hostSshConfigured ?? null,
hostSshKeyPresent: labels.hostSshKeyPresent ?? null,
sshDataPool: {
transport: labels.providerGatewaySshDataTransport ?? null,
desired: labels.providerGatewaySshDataPoolDesired ?? null,
ready: labels.providerGatewaySshDataPoolReady ?? null,
claimed: labels.providerGatewaySshDataPoolClaimed ?? null,
},
egressProxy: {
enabled: labels.providerGatewayEgressProxy ?? null,
connected: labels.providerGatewayEgressProxyConnected ?? null,
activeTunnels: labels.providerGatewayEgressProxyActiveTunnels ?? null,
pendingTunnels: labels.providerGatewayEgressProxyPendingTunnels ?? null,
staleTunnels: labels.providerGatewayEgressProxyStaleTunnels ?? null,
oldestTunnelAgeMs: labels.providerGatewayEgressProxyOldestTunnelAgeMs ?? null,
},
capabilities,
});
}