@@ -279,6 +279,20 @@ function createQaGatewayChildLogCollector() {
|
279 | 279 | }; |
280 | 280 | } |
281 | 281 | |
| 282 | +function monitorQaGatewayChildSpawnError( |
| 283 | +child: ChildProcess, |
| 284 | +output: { push(chunk: Buffer): void }, |
| 285 | +) { |
| 286 | +let spawnError: unknown = null; |
| 287 | +child.once("error", (error) => { |
| 288 | +spawnError = error; |
| 289 | +output.push( |
| 290 | +Buffer.from(`[qa-lab] gateway child process error: ${formatErrorMessage(error)}\n`), |
| 291 | +); |
| 292 | +}); |
| 293 | +return () => spawnError; |
| 294 | +} |
| 295 | + |
282 | 296 | async function fetchLocalGatewayHealth(params: { |
283 | 297 | baseUrl: string; |
284 | 298 | healthPath: "/readyz" | "/healthz"; |
@@ -487,10 +501,19 @@ async function waitForGatewayReady(params: {
|
487 | 501 | exitCode: number | null; |
488 | 502 | signalCode: NodeJS.Signals | null; |
489 | 503 | }; |
| 504 | +getSpawnError?: () => unknown; |
490 | 505 | timeoutMs?: number; |
491 | 506 | }) { |
492 | 507 | const startedAt = Date.now(); |
493 | 508 | while (Date.now() - startedAt < (params.timeoutMs ?? 60_000)) { |
| 509 | +const spawnError = params.getSpawnError?.(); |
| 510 | +if (spawnError) { |
| 511 | +throw new QaSuiteInfraError( |
| 512 | +"gateway_startup_unhealthy", |
| 513 | +`gateway failed to spawn: ${formatErrorMessage(spawnError)}\n${params.logs()}`, |
| 514 | +{ cause: spawnError }, |
| 515 | +); |
| 516 | +} |
494 | 517 | if (params.child.exitCode !== null || params.child.signalCode !== null) { |
495 | 518 | throw new QaSuiteInfraError( |
496 | 519 | "gateway_startup_unhealthy", |
@@ -768,12 +791,14 @@ export async function startQaGatewayChild(params: {
|
768 | 791 | stderrLog.write(buffer); |
769 | 792 | }); |
770 | 793 | child = attemptChild; |
| 794 | +const getAttemptSpawnError = monitorQaGatewayChildSpawnError(attemptChild, output); |
771 | 795 | |
772 | 796 | try { |
773 | 797 | await waitForGatewayReady({ |
774 | 798 | baseUrl, |
775 | 799 | logs, |
776 | 800 | child: attemptChild, |
| 801 | +getSpawnError: getAttemptSpawnError, |
777 | 802 | timeoutMs: 120_000, |
778 | 803 | }); |
779 | 804 | const attemptRpcClient = await startQaGatewayRpcClient({ |
@@ -805,6 +830,7 @@ export async function startQaGatewayChild(params: {
|
805 | 830 | baseUrl, |
806 | 831 | logs, |
807 | 832 | child: attemptChild, |
| 833 | +getSpawnError: getAttemptSpawnError, |
808 | 834 | timeoutMs: QA_GATEWAY_CHILD_RPC_RETRY_HEALTH_TIMEOUT_MS, |
809 | 835 | }); |
810 | 836 | } |
@@ -871,12 +897,14 @@ export async function startQaGatewayChild(params: {
|
871 | 897 | output.push(buffer); |
872 | 898 | stderrLog.write(buffer); |
873 | 899 | }); |
| 900 | +const getNextSpawnError = monitorQaGatewayChildSpawnError(nextChild, output); |
874 | 901 | |
875 | 902 | try { |
876 | 903 | await waitForGatewayReady({ |
877 | 904 | baseUrl, |
878 | 905 | logs, |
879 | 906 | child: nextChild, |
| 907 | +getSpawnError: getNextSpawnError, |
880 | 908 | timeoutMs: 120_000, |
881 | 909 | }); |
882 | 910 | const nextRpcClient = await startQaGatewayRpcClient({ |
@@ -908,6 +936,7 @@ export async function startQaGatewayChild(params: {
|
908 | 936 | baseUrl, |
909 | 937 | logs, |
910 | 938 | child: nextChild, |
| 939 | +getSpawnError: getNextSpawnError, |
911 | 940 | timeoutMs: 15_000, |
912 | 941 | }); |
913 | 942 | } |
|