procfiled
Version:
Node.js process manager for Procfile-based applications
159 lines (127 loc) • 3.9 kB
JavaScript
// Copyright IBM Corp. 2012,2016. All Rights Reserved.
// Node module: procfiled
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var prog = require('child_process');
var cons = require('./console').Console;
var _colors = require('./colors');
var colors_max = _colors.colors_max;
var colors = _colors.colors;
var os = require('os');
var platform = os.platform();
// Run a Specific Process
// - Key is a Process Name and Number
// - Proc is an object with the launch properties
//
// i.e. web=2 becomes the web.2 key
function run(key, proc, emitter) {
var file, args;
if (platform === 'win32') {
file = process.env.comspec || 'cmd.exe';
args = ['/s', '/c', proc.command];
} else {
file = '/bin/sh';
args = ['-c', proc.command];
}
var child = prog.spawn(file, args, { env: proc.env });
var killallReceived = false;
child.stdout.on('data', function(data) {
cons.log(key, proc, data.toString());
});
child.stderr.on('data', function(data) {
cons.log(key, proc, data.toString());
});
child.on('close', function(code, signal) {
if(code === 0) {
cons.info(key, proc, "Exited Successfully");
} else {
cons.error(key, proc, "Exited with exit code " + signal || code);
}
});
child.on('exit', function(code, signal) {
if (!killallReceived) {
emitter.emit('killall', signal || 'SIGINT');
}
});
emitter.on('killall', function(signal) {
// Once this process has received a killall event, don't send another
// such event to everyone. Let's assume that once is enough.
killallReceived = true;
try {
child.kill(signal);
}
catch (err) {
if (err.code === 'EPERM') {
// Means that the child runs with higher privileges than we are; we're
// not going to be able to kill it in that state. Log and do nothing.
cons.error(key, proc, "Process has become unkillable; returns EPERM.");
}
}
});
}
// Run a Specific Process Once using the ENV variables
// from the .env file
function once(input, envs, callback) {
var file, args;
var proc = {
command : input,
env : merge(merge({}, process.env), envs)
};
if (platform === 'win32') {
file = process.env.comspec || 'cmd.exe';
args = ['/s', '/c', proc.command];
} else {
file = '/bin/sh';
args = ['-c', proc.command];
}
var child = prog.spawn(file, args, { env: proc.env, stdio: 'inherit' });
child.on('close', function(code) {
callback(code);
});
}
// Figure Out What to Start Based on Procfile Processes
// And Requirements Passed as Command Line Arguments
//
// e.g. web=2,api=3 are requirements
function start(procs, requirements, envs, portarg, emitter){
var j = 0;
var k = 0;
var port = parseInt(portarg);
if(port < 1024) {
return cons.Error('Only Proxies Can Bind to Privileged Ports - '+
'Try \'sudo nf start -x %s\'', port);
}
for(var key in requirements) {
var n = parseInt(requirements[key]);
for(var i = 0; i < n; i++) {
var color_val = (j + k) % colors_max;
if (!procs[key]) {
cons.Warn("Required Key '%s' Does Not Exist in Procfile Definition", key);
continue;
}
var p = {
command : procs[key],
color : colors[color_val],
env : merge(merge({}, process.env), envs)
};
p.env.PORT = port + j + k * 100;
p.env.PROCFILED_WORKER_NAME = p.env.PROCFILED_WORKER_NAME || key + "." + (i + 1);
run(key + "." + (i + 1), p, emitter);
j++;
}
j = 0;
k++;
}
}
// Merge object b into object a
function merge(a, b) {
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
}
module.exports.start = start;
module.exports.run = run;
module.exports.once = once;