| // This can be used to measure the time to first request, |
| // including starting up our Docker container: |
| // npm install |
| // node index.js |
| // |
| // And in a different terminal: |
| // time curl -s http://localhost:9000/sling/chouc/route |
| // |
| // This proxy starts the container on the first request |
| // (for now, you need to stop it before running this utility) |
| // so the reported time is a realistic "time to first request" |
| // value. |
| // |
| // The "docker events" command also provides useful information |
| // in terms of container startup time. |
| // |
| const http = require('http'); |
| const httpProxy = require('http-proxy'); |
| const Docker = require('dockerode'); |
| const waitOn = require('wait-on'); |
| const elapsedTime = require('elapsed-time') |
| const yargs = require('yargs') |
| |
| const argv = yargs |
| .usage('$0 <cmd> [args]') |
| .help() |
| .option('listen', { |
| default: 9000, |
| describe: 'port on which to listen' |
| }) |
| .option('waitOn', { |
| default: '/index.html', |
| describe: 'path for testing server readiness' |
| }) |
| .option('image', { |
| default: 'httpd:2.4.39-alpine', |
| describe: 'Docker image to start (get it first with docker pull)' |
| }) |
| .option('dockerHostPort', { |
| default: 8080, |
| describe: 'Docker host port for our container' |
| }) |
| .option('dockerContainerPort', { |
| default: 80, |
| describe: 'Port exposed by our image' |
| }) |
| .argv |
| |
| const listenPort = argv.listen; |
| const targetUrl = `http://127.0.0.1:${argv.dockerHostPort}`; |
| const waitUrl = `${targetUrl}/${argv.waitOn}`; |
| const dockerImage = argv.image; |
| const dockerStartOptions = {}; |
| dockerStartOptions['PortBindings'] = {} |
| dockerStartOptions['PortBindings'][`${argv.dockerContainerPort}/tcp`] = |
| [{ |
| "HostIP":"0.0.0.0", |
| "HostPort": `${argv.dockerHostPort}` |
| }]; |
| |
| const proxy = httpProxy.createProxyServer({}); |
| |
| const waitOpts = { |
| resources: [ |
| waitUrl |
| ], |
| delay: 0, |
| interval: 10, |
| timeout: 30000, |
| window: 1, |
| }; |
| |
| const docker = new Docker(); |
| const actions = {}; |
| |
| const getContainer = (async imageName => { |
| const containers = await docker.listContainers(); |
| return containers.find(container => { |
| return container.Image == imageName; |
| }); |
| }); |
| |
| var server = http.createServer(async (req, res) => { |
| const et = elapsedTime.new().start(); |
| existingContainer = await getContainer(dockerImage); |
| |
| if(existingContainer) { |
| console.log(`Container is already running: ${dockerImage}(${et.getValue()})`); |
| } else { |
| actions.startedContainer = true; |
| console.log(`Starting container: ${dockerImage}(${et.getValue()})`); |
| docker.run(dockerImage, null, null, dockerStartOptions); |
| } |
| |
| // No need to wait for async call, just wait for our URL |
| console.log(`Waiting on ${waitOpts.resources[0]} (${et.getValue()})`); |
| waitOn(waitOpts).then(() =>{ |
| console.log(`Time to wait for ${waitUrl}: (${et.getValue()})`); |
| console.log(`Proxying ${req.url} (${et.getValue()})`); |
| proxy.web(req, res, { target: targetUrl }); |
| console.log(`Done proxying (${et.getValue()})`); |
| }); |
| }); |
| |
| const cleanup = async () => { |
| if(!actions.startedContainer) { |
| console.log('Did not start container, nothing to cleanup'); |
| } else { |
| const runningContainer = await getContainer(dockerImage); |
| if(runningContainer) { |
| console.log(`Killing container ${dockerImage}/${runningContainer.Id.substring(0,12)} ...`); |
| const container = await docker.getContainer(runningContainer.Id); |
| await container.kill(); |
| console.log('killed'); |
| } |
| } |
| process.exit(); |
| } |
| |
| [ |
| 'SIGINT', |
| 'SIGTERM', |
| ].forEach(signal => { |
| process.on(signal, cleanup); |
| }) |
| |
| console.log(`listening on port ${listenPort}, proxying to ${targetUrl} with ${dockerImage} on port ${argv.dockerHostPort}/${argv.dockerContainerPort}`); |
| server.listen(listenPort) |