diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts index ed5d0296d61a45..a14f8af1ef699a 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/agent_downloads_service.ts @@ -85,9 +85,16 @@ class AgentDownloadStorage extends SettingsStorage try { const outputStream = fs.createWriteStream(newDownloadInfo.fullFilePath); - const { body } = await nodeFetch(agentDownloadUrl); - await finished(body.pipe(outputStream)); + await handleProcessInterruptions( + async () => { + const { body } = await nodeFetch(agentDownloadUrl); + await finished(body.pipe(outputStream)); + }, + () => { + fs.unlinkSync(newDownloadInfo.fullFilePath); + } + ); } catch (e) { // Try to clean up download case it failed halfway through await unlink(newDownloadInfo.fullFilePath); @@ -134,6 +141,42 @@ class AgentDownloadStorage extends SettingsStorage } } +const handleProcessInterruptions = async ( + runFn: (() => T) | (() => Promise), + /** The synchronous cleanup callback */ + cleanup: () => void +): Promise => { + const eventNames = ['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection']; + const stopListeners = () => { + for (const eventName of eventNames) { + process.off(eventName, cleanup); + } + }; + + for (const eventName of eventNames) { + process.on(eventName, cleanup); + } + + let runnerResponse: T | Promise; + + try { + runnerResponse = runFn(); + } catch (e) { + stopListeners(); + throw e; + } + + if ('finally' in runnerResponse) { + (runnerResponse as Promise).finally(() => { + stopListeners(); + }); + } else { + stopListeners(); + } + + return runnerResponse; +}; + const agentDownloadsClient = new AgentDownloadStorage(); /**