mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
fix: drain piped stderr in runServerCapture/waitForCloudInit to prevent deadlock (#1915)
PR #1903 fixed a pipe buffer deadlock in awsCli() by draining both stdout and stderr before awaiting proc.exited. The same pattern existed in runServerCapture() across 4 cloud providers and waitForCloudInit() across 3 providers. If SSH produces >64KB of stderr, the child blocks writing to the full pipe while the parent blocks waiting for exit. Fixes: hetzner, aws, digitalocean, gcp — 7 locations total. Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
parent
5458cef9f1
commit
2e79d71bd6
4 changed files with 35 additions and 6 deletions
|
|
@ -1062,7 +1062,11 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number): Promi
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
const exitCode = await proc.exited;
|
const exitCode = await proc.exited;
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
|
|
|
||||||
|
|
@ -928,7 +928,11 @@ export async function waitForCloudInit(ip?: string, _maxAttempts = 60): Promise<
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
if ((await proc.exited) === 0 && stdout.includes("done")) {
|
if ((await proc.exited) === 0 && stdout.includes("done")) {
|
||||||
logInfo("Cloud-init complete");
|
logInfo("Cloud-init complete");
|
||||||
return;
|
return;
|
||||||
|
|
@ -1011,7 +1015,11 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number, ip?: s
|
||||||
proc.kill();
|
proc.kill();
|
||||||
} catch {}
|
} catch {}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
const exitCode = await proc.exited;
|
const exitCode = await proc.exited;
|
||||||
try {
|
try {
|
||||||
proc.stdin!.end();
|
proc.stdin!.end();
|
||||||
|
|
|
||||||
|
|
@ -792,6 +792,11 @@ export async function waitForCloudInit(maxAttempts = 60): Promise<void> {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
if ((await proc.exited) === 0) {
|
if ((await proc.exited) === 0) {
|
||||||
logInfo("Startup script completed");
|
logInfo("Startup script completed");
|
||||||
return;
|
return;
|
||||||
|
|
@ -868,7 +873,11 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number): Promi
|
||||||
proc.kill();
|
proc.kill();
|
||||||
} catch {}
|
} catch {}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
const exitCode = await proc.exited;
|
const exitCode = await proc.exited;
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
|
|
|
||||||
|
|
@ -489,7 +489,11 @@ export async function waitForCloudInit(ip?: string, _maxAttempts = 60): Promise<
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
const exitCode = await proc.exited;
|
const exitCode = await proc.exited;
|
||||||
if (exitCode === 0 && stdout.includes("done")) {
|
if (exitCode === 0 && stdout.includes("done")) {
|
||||||
logInfo("Cloud-init complete");
|
logInfo("Cloud-init complete");
|
||||||
|
|
@ -576,7 +580,11 @@ export async function runServerCapture(cmd: string, timeoutSecs?: number, ip?: s
|
||||||
proc.kill();
|
proc.kill();
|
||||||
} catch {}
|
} catch {}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
const stdout = await new Response(proc.stdout).text();
|
// Drain both pipes before awaiting exit to prevent pipe buffer deadlock
|
||||||
|
const [stdout] = await Promise.all([
|
||||||
|
new Response(proc.stdout).text(),
|
||||||
|
new Response(proc.stderr).text(),
|
||||||
|
]);
|
||||||
const exitCode = await proc.exited;
|
const exitCode = await proc.exited;
|
||||||
try {
|
try {
|
||||||
proc.stdin!.end();
|
proc.stdin!.end();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue