blob: c2d68c25501bb1d354896c2be969a9b963f32e02 [file] [log] [blame]
// 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)