From 32de9b734be1fd93a30062d188d30d8022568f7c Mon Sep 17 00:00:00 2001 From: Courey Elliott Date: Mon, 24 Jun 2024 12:37:53 -0400 Subject: [PATCH] add forked docker process (#39) Resolves #40. --- index.ts | 1 + launchSearch.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ run.ts | 50 ++++++++++++++++++++++------------------------ tsconfig.json | 4 +++- 4 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 launchSearch.ts diff --git a/index.ts b/index.ts index 4442503..01e0bf7 100644 --- a/index.ts +++ b/index.ts @@ -12,6 +12,7 @@ import { pathToFileURL } from 'url' import { launch } from './run.js' import { populate } from './data.js' import { search as getSearchClient } from '@nasa-gcn/architect-functions-search' +import './launchSearch' import { cloudformationResources as serverlessCloudformationResources, services as serverlessServices, diff --git a/launchSearch.ts b/launchSearch.ts new file mode 100644 index 0000000..484872c --- /dev/null +++ b/launchSearch.ts @@ -0,0 +1,53 @@ +/*! + * Copyright © 2023 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import Dockerode from 'dockerode' + +const [, , command, jsonifiedArgs] = process.argv + +if (command === 'launch-docker-subprocess') { + const dockerContainer = await launchDockerSearch() + + const signals = ['message', 'SIGTERM', 'SIGINT'] + signals.forEach((signal) => { + process.on(signal, async () => { + await dockerContainer.kill() + }) + }) + + await dockerContainer.wait() +} + +async function launchDockerSearch() { + const { dataDir, logsDir, engine, port, options } = JSON.parse(jsonifiedArgs) + const Image = + engine === 'elasticsearch' + ? 'elastic/elasticsearch:8.6.2' + : 'opensearchproject/opensearch:2.11.0' + console.log('Launching Docker container', Image) + const docker = new Dockerode() + + const container = await docker.createContainer({ + Env: [...options, 'path.data=/var/lib/search', 'path.logs=/var/log/search'], + HostConfig: { + AutoRemove: true, + Mounts: [ + { Source: dataDir, Target: '/var/lib/search', Type: 'bind' }, + { Source: logsDir, Target: '/var/log/search', Type: 'bind' }, + ], + PortBindings: { + [`${port}/tcp`]: [{ HostIP: '127.0.0.1', HostPort: `${port}` }], + }, + }, + Image, + }) + const stream = await container.attach({ stream: true, stderr: true }) + stream.pipe(process.stderr) + await container.start() + return container +} diff --git a/run.ts b/run.ts index 7c1934e..5011870 100644 --- a/run.ts +++ b/run.ts @@ -14,8 +14,9 @@ import { mkdirP, temp } from './paths.js' import rimraf from 'rimraf' import { spawn, untilTerminated } from './processes.js' import type { SandboxEngine } from './engines.js' -import Dockerode from 'dockerode' import { UnexpectedResolveError, neverResolve } from './promises.js' +import { fork } from 'child_process' +import { fileURLToPath } from 'url' type SearchEngineLauncherFunction = ( props: T & { @@ -63,37 +64,34 @@ const launchDocker: SearchEngineLauncherFunction = async ({ port, options, }) => { - const Image = - engine === 'elasticsearch' - ? 'elastic/elasticsearch:8.6.2' - : 'opensearchproject/opensearch:2.11.0' - console.log('Launching Docker container', Image) - const docker = new Dockerode() - const container = await docker.createContainer({ - Env: [...options, 'path.data=/var/lib/search', 'path.logs=/var/log/search'], - HostConfig: { - AutoRemove: true, - Mounts: [ - { Source: dataDir, Target: '/var/lib/search', Type: 'bind' }, - { Source: logsDir, Target: '/var/log/search', Type: 'bind' }, - ], - PortBindings: { - [`${port}/tcp`]: [{ HostIP: '127.0.0.1', HostPort: `${port}` }], - }, - }, - Image, - }) - const stream = await container.attach({ stream: true, stderr: true }) - stream.pipe(process.stderr) - await container.start() + const argv = { + dataDir, + logsDir, + engine, + port, + options, + } + // FIXME: fork accepts either a string or URL as the first argument. @types/node has defined the modulePath type as string only. + // new URL(import.meta.url) may be used in place of __filename once this has been updated. + // see: https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/69812 + const __filename = fileURLToPath(import.meta.url) + const subprocess = fork(__filename, [ + 'launch-docker-subprocess', + JSON.stringify(argv), + ]) return { async kill() { console.log('Killing Docker container') - await container.kill() + subprocess.send({ action: 'kill' }) }, async waitUntilStopped() { - await container.wait() + return new Promise((resolve) => { + subprocess.on('exit', () => { + console.log('Docker container exited') + resolve() + }) + }) }, } } diff --git a/tsconfig.json b/tsconfig.json index f9825c3..cad99b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "@tsconfig/node14/tsconfig.json", "compilerOptions": { - "resolveJsonModule": true + "resolveJsonModule": true, + "module": "esnext", + "target": "es2022" } }