Socket
Socket
Sign inDemoInstall

cluster-service

Package Overview
Dependencies
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cluster-service - npm Package Compare versions

Comparing version 1.0.5 to 2.0.0-alpha1

examples/certs/test1-cert.pem

15

CHANGELOG.md

@@ -0,1 +1,14 @@

## v2.0.0 - TBD
Features:
* Proxy support with dynamic app versioning
* Worker process downgrade from root via `workerGid` & `workerUid`
Enhancements:
* Refactored communication between processes
* Worker processes may now trigger commands (same) and wait for response (new)
## v1.0.0 - 5/8/2014

@@ -19,3 +32,3 @@

* #57. Cannot use '--run' and '--config' together
* #58. Custom events from master & workers
* #58. Custom events from master & workers

@@ -22,0 +35,0 @@

16

cluster-service.js

@@ -13,2 +13,3 @@ var cluster = require("cluster");

exports.results = require("./lib/util").results;
exports.msgBus = require("./lib/message-bus");

@@ -60,2 +61,3 @@ exports.workerReady = require("./lib/worker-ready");

exports.netStats = require("./lib/net-stats");
exports.proxy = require('./lib/proxy');

@@ -68,6 +70,12 @@ if (

cluster.worker.module = {};
cluster.worker.env = process.env;
var workers = require("./lib/workers");
workers.demote();
// load the worker if not already loaded
// async, in case worker loads cluster-service, we need to return before
// async, in case worker loads cluster-service, we need to return before
// it's avail
setTimeout(function() {
setImmediate(function() {
cluster.worker.module = require(process.env.worker);

@@ -82,6 +90,6 @@ if (global.cservice.locals.workerReady === undefined

}
}, 10);
});
// start worker monitor to establish two-way relationship with master
require("./lib/workers").monitor();
workers.monitor();
}
var http = require('http');
var cservice = require("cluster-service");
var cservice = require("../cluster-service");

@@ -4,0 +4,0 @@ cservice.workerReady(false); // inform cservice we're not ready yet

@@ -70,11 +70,3 @@ var async = require("async"),

return function(cb) {
// kill new worker if takes too long
var newKiller = null;
var newWorker = null;
var exitListener = function() {
if (newKiller) {
clearTimeout(newKiller);
}
};
var w;
var pendingWorker = null;

@@ -89,11 +81,14 @@ if (worker.cservice.restart === false && explicitRestart === false) {

// kill new worker if takes too long
var newWorkerTimeout = null;
var isNewWorkerTerminated = false;
if (options.timeout > 0) { // start timeout if specified
newKiller = setTimeout(function() {
w = newWorker;
newWorker = null;
if (w) {
w.removeListener("exit", exitListener); // remove temp listener
w.kill("SIGKILL"); // go get'em, killer
newWorkerTimeout = setTimeout(function() {
if (pendingWorker) {
isNewWorkerTerminated = true;
pendingWorker.on('exit', function () {
cb("timed out");
});
pendingWorker.kill("SIGKILL"); // go get'em, killer
}
cb("timed out");
}, options.timeout);

@@ -103,14 +98,13 @@ }

// lets start new worker
newWorker = evt.service.newWorker(worker.cservice, function(err, newWorker){
var killer;
newWorker.removeListener("exit", exitListener); // remove temp listener
newWorker = null;
if (newKiller) { // timeout no longer needed
clearTimeout(newKiller);
pendingWorker = evt.service.newWorker(worker.cservice, function(err) {
pendingWorker = null;
if (newWorkerTimeout) { // timeout no longer needed
clearTimeout(newWorkerTimeout);
}
if (isNewWorkerTerminated) return;
// ok, lets stop old worker
killer = null;
var oldWorkerTimeout = null;
if (options.timeout > 0) { // start timeout if specified
killer = setTimeout(function() {
oldWorkerTimeout = setTimeout(function() {
worker.kill("SIGKILL"); // go get'em, killer

@@ -121,8 +115,8 @@ }, options.timeout);

worker.on("exit", function() {
if (killer) {
clearTimeout(killer);
if (oldWorkerTimeout) {
clearTimeout(oldWorkerTimeout);
}
// exit complete, fire callback
setTimeout(cb, 100); // slight delay in case other events are piled up
setImmediate(cb); // slight delay in case other events are piled up
});

@@ -129,0 +123,0 @@

@@ -31,26 +31,19 @@ /* jshint loopfunc:true */

worker.process.on(
"exit",
getExitHandler(
evt
, worker
, options.timeout > 0
? setTimeout(getKiller(worker), options.timeout)
: null
, function() {
workersToKill--;
if (workersToKill === 0) {
// no workers remain
if (evt.service.workers.length === 0) {
evt.locals.reason = "kill";
cservice.log("All workers shutdown. Exiting...".warn);
evt.service.stop(options.timeout, cb);
} else {
cb(null, "Worker shutdown"); // DONE
}
}
var killTimeout = options.timeout > 0
? setTimeout(getKiller(worker), options.timeout)
: null;
worker.on("exit", getExitHandler(evt, worker, killTimeout, function() {
workersToKill--;
if (workersToKill === 0) {
// no workers remain
if (evt.service.workers.length === 0) {
evt.locals.reason = "kill";
cservice.log("All workers shutdown. Exiting...".warn);
evt.service.stop(options.timeout, cb);
} else {
cb(null, "Worker shutdown"); // DONE
}
)
);
}
}));
require("../workers").exitGracefully(worker);

@@ -108,3 +101,2 @@ });

clearTimeout(killer);
killer = null;
}

@@ -111,0 +103,0 @@

@@ -67,36 +67,33 @@ var async = require("async"),

return function(cb) {
var pendingWorker;
// kill new worker if takes too long
var newKiller = null;
var newWorker;
var exitListener = function() {
if (newKiller) {
clearTimeout(newKiller);
}
};
var startTimeout = null;
var isWorkerTerminated = false;
if (options.timeout > 0) { // start timeout if specified
newKiller = setTimeout(function() {
if (!newWorker)
startTimeout = setTimeout(function() {
if (!pendingWorker)
return;
newWorker.removeListener("exit", exitListener); // remove temp listener
newWorker.kill("SIGKILL"); // go get'em, killer
cb("timed out");
isWorkerTerminated = true;
pendingWorker.on('exit', function () {
cb("timed out");
});
pendingWorker.kill("SIGKILL"); // go get'em, killer
}, options.timeout);
newKiller.unref();
startTimeout.unref();
}
// lets start new worker
newWorker = evt.service.newWorker(options, function(err, newWorker) {
if (newWorker) { // won't exist if failure
newWorker.removeListener("exit", exitListener); // remove temp listener
pendingWorker = evt.service.newWorker(options, function(err) {
pendingWorker = null;
if (startTimeout) { // timeout no longer needed
clearTimeout(startTimeout);
}
if (newKiller) { // timeout no longer needed
clearTimeout(newKiller);
newKiller = null;
if (!isWorkerTerminated) {
cb(err);
}
cb(err);
});
};
}
var async = require("async"),
util = require("util"),
extend = require("extend"),
cservice = require("../../cluster-service");

@@ -32,3 +31,3 @@

// use original worker options as default, by overwrite using new options
workerOptions = extend(true, {}, worker.cservice, options);
workerOptions = util._extend({}, worker.cservice, options);

@@ -82,17 +81,16 @@ tasks.push(getTask(evt, worker, workerOptions));

var pendingWorker;
// kill new worker if takes too long
var newKiller = null;
var newWorker;
var exitListener = function() {
if (newKiller) {
clearTimeout(newKiller);
}
};
var killer;
var newWorkerTimeout = null;
var isNewWorkerTerminated = false;
if (options.timeout > 0) { // start timeout if specified
newWorkerTimeout = setTimeout(function() {
if (!pendingWorker) return;
if (options.timeout > 0) { // start timeout if specified
newKiller = setTimeout(function() {
newWorker.removeListener("exit", exitListener);// remove temp listener
newWorker.kill("SIGKILL"); // go get'em, killer
cb("timed out");
isNewWorkerTerminated = true;
pendingWorker.on('exit', function () {
cb("timed out");
});
pendingWorker.kill("SIGKILL"); // go get'em, killer
}, options.timeout);

@@ -102,9 +100,8 @@ }

// lets start new worker
newWorker = evt.service.newWorker(options, function(err, newWorker) {
if (newWorker) { // won't exist if failure
newWorker.removeListener("exit", exitListener); // remove temp listener
pendingWorker = evt.service.newWorker(options, function (err) {
pendingWorker = null;
if (newWorkerTimeout) { // timeout no longer needed
clearTimeout(newWorkerTimeout);
}
if (newKiller) { // timeout no longer needed
clearTimeout(newKiller);
}
if (err) {

@@ -116,5 +113,5 @@ cb(err);

// ok, lets stop old worker
killer = null;
var oldWorkerTimeout = null;
if (options.timeout > 0) { // start timeout if specified
killer = setTimeout(function() {
oldWorkerTimeout = setTimeout(function() {
worker.kill("SIGKILL"); // go get'em, killer

@@ -125,8 +122,8 @@ }, options.timeout);

worker.on("exit", function() {
if (killer) {
clearTimeout(killer);
if (oldWorkerTimeout) {
clearTimeout(oldWorkerTimeout);
}
// exit complete, fire callback
setTimeout(cb, 250); // slight delay in case other events are piled up
setImmediate(cb); // slight delay in case other events are piled up
});

@@ -133,0 +130,0 @@

@@ -11,3 +11,4 @@ var path = require("path"),

+ ") exited, reason: "
+ (reason || cservice.locals.reason || "Unknown")).warn
+ (reason || worker.cservice.reason ||
cservice.locals.reason || "Unknown")).warn
);

@@ -14,0 +15,0 @@ cb();

@@ -1,3 +0,3 @@

var async = require("async"),
extend = require("extend");
var async = require("async");
var cservice = require("../../cluster-service");

@@ -47,7 +47,7 @@ module.exports = function(evt, cb, cmd) {

msgCb = function (msg) {
if (msg && msg.processDetails) {
processDetails = msg.processDetails;
if (msg && msg.cservice.processDetails) {
processDetails = msg.cservice.processDetails;
}
if (msg && msg.netStats) {
netStats = msg.netStats;
if (msg && msg.cservice.netStats) {
netStats = msg.cservice.netStats;
}

@@ -73,4 +73,4 @@ if (processDetails && netStats) {

worker.on("message", msgCb);
worker.process.send({cservice: "processDetails"});
worker.process.send({cservice: "netStats"});
worker.process.send(cservice.msgBus.createMessage("processDetails"));
worker.process.send(cservice.msgBus.createMessage("netStats"));
};

@@ -77,0 +77,0 @@ }

@@ -7,2 +7,3 @@ var os = require("os");

workers: {},
workerProcesses: {},
state: 0, // 0-not running, 1-starting, 2-running

@@ -14,2 +15,40 @@ isBusy: false,

net: { servers: {} },
proxy: {
configPath: undefined,
versionPath: undefined,
options: {
versionPath: undefined,
versionHeader: "x-version",
workerFilename: "worker.js",
versionPorts: "11000-12000",
nonDefaultWorkerCount: 1,
nonDefaultWorkerIdleTime: 3600,
bindings: [
/*
{
port: 80,
workerCount: 2,
redirect: 443
},
{
port: 443,
workerCount: 2,
tlsOptions: {
key: '/my/cert.key',
cert: '/my/cert.crt'
}
}
*/
]
},
versions: {
/*
'versionStr': {
port: 7112,
isDefault: false,
online: false
}
*/
}
},
options: {

@@ -33,2 +72,5 @@ host: "localhost",

commands: undefined,
proxy: undefined,
workerGid: undefined,
workerUid: undefined,
colors: {

@@ -35,0 +77,0 @@ cservice: "grey",

@@ -55,6 +55,27 @@ /* jshint loopfunc:true */

cluster.on("exit", function(worker, code, signal) {
// stop tracking
var version = cservice.locals.proxy.versions[worker.cservice.version];
if (version) {
// get all proxy workers for a specific version
var versionWorkers =
cservice.proxy.getVersionWorkers(worker.cservice.version);
// exclude our exiting worker process in case it's still returned
versionWorkers = versionWorkers.filter(function(versionWorker) {
return worker.process.pid !== versionWorker.process.pid;
});
if (versionWorkers.length === 0) {
// if no workers remain for a given version, drop the version
delete cservice.locals.proxy.versions[worker.cservice.version];
// inform proxy workers of version change
cservice.proxy.updateProxyWorkers();
}
}
delete cservice.locals.workerProcesses[worker.process.pid];
cservice.trigger("workerExit", worker);
// do not restart if there is a reason, or disabled
if (
typeof (cservice.locals.reason) === "undefined"
!(cservice.locals.reason || worker.cservice.reason)
&& worker.suicide !== true

@@ -79,9 +100,11 @@ && cservice.locals.restartOnFailure === true

cservice.locals.state = 2; // running
cservice.proxy.start({}, function() {
cservice.locals.state = 2; // running
// now that listener is ready, process queued start requests
for (i = 0; i < startRequests.length; i++) {
startRequests[i](); // execute
}
startRequests = [];
// now that listener is ready, process queued start requests
for (i = 0; i < startRequests.length; i++) {
startRequests[i](); // execute
}
startRequests = [];
});
});

@@ -97,3 +120,3 @@ } else if (cservice.locals.state === 1) { // if still starting, queue requests

workersForked = 0;
if (options.workers !== null) {

@@ -126,3 +149,3 @@ workers = typeof options.workers === "string"

}
// if no forking took place, make sure cb is invoked

@@ -158,3 +181,3 @@ if (workersForked === 0) {

}
httpserver.init(options, function(err) {

@@ -161,0 +184,0 @@ if (!err) {

@@ -22,3 +22,3 @@ var cservice = require("../cluster-service");

}
for (var i = 0; i < servers.length; i++) {

@@ -28,3 +28,3 @@ var server = servers[i];

continue; // ignore if already added
server.cservice = {

@@ -35,3 +35,3 @@ id: Math.random().toString(), // track by id

cservice.locals.net.servers[server.cservice.id] = server;
listenToNetServer(server);

@@ -55,3 +55,3 @@ netStats(server);

delete server.cservice;
stopListeningToNetServer(server);

@@ -63,3 +63,3 @@ }

var tasks = [];
for (var id in cservice.locals.net.servers) {

@@ -69,10 +69,10 @@ var server = cservice.locals.net.servers[id];

continue;
tasks.push(createWaitForReadyTask(server));
}
if (tasks.length === 0) {
return cb();
}
async.parallel(tasks, cb);

@@ -107,3 +107,3 @@ }

}
if (tasks.length === 0) {

@@ -118,16 +118,3 @@ return cb();

return function(cb) {
var tmpRequestCb = function(req, res) {
if (res.headersSent === false) {
// required to gracefully close connections on pending requests
// this logic is typically only hit under VERY high load
res.setHeader("Connection", "close");
}
};
var tmpCloseCb = function() {
server.removeListener("close", tmpCloseCb);
//server.removeListener("request", tmpRequestCb);
cb(null, true);
};
server.on("close", tmpCloseCb);
//server.on("request", tmpRequestCb);
server.once("close", function() { cb(null, true); });
server.close();

@@ -141,3 +128,3 @@ };

serverListenOld = net.Server.prototype.listen;
net.Server.prototype.listen = serverListenNew;

@@ -160,3 +147,3 @@ }

this.on("listening", serverOnListening); // ready on event
return serverListenOld.apply(this, arguments); // call original listen

@@ -163,0 +150,0 @@ }

@@ -24,3 +24,3 @@ var cservice = require("../cluster-service");

}
server.on("connection", function(connection) {

@@ -52,12 +52,11 @@ net.connections++;

process.on("message", function(msg) {
if (!msg || typeof msg.cservice !== "string") {
if (!cservice.msgBus.isValidMessage(msg)) {
return; // ignore
}
switch (msg.cservice) {
switch (msg.cservice.cmd) {
case "netStats":
process.send({
cservice: "netStats",
process.send(cservice.msgBus.createMessage("netStats", {
netStats: net
});
}));
break;

@@ -82,3 +81,3 @@ }

;
// reset

@@ -85,0 +84,0 @@ stats.lastCheck = now;

@@ -5,3 +5,3 @@ var cservice = require("../cluster-service"),

fs = require("fs"),
extend = require("extend");
util = require("util");

@@ -12,9 +12,11 @@ module.exports = exports = newWorker;

var worker;
options = extend(true, {}, {
options = util._extend(util._extend({}, {
worker: "./worker.js",
count: undefined,
restart: true,
type: 'user',
version: undefined,
cwd: undefined,
onStop: false
}, options);
}), options);
options.ready = false;

@@ -44,2 +46,17 @@ if (

options.onReady = cb;
var version;
if (options.version) {
// track workers with version
version = cservice.locals.proxy.versions[options.version];
if (!version) {
version = {
name: options.version,
port: options.PROXY_PORT,
online: false
};
cservice.locals.proxy.versions[options.version] = version;
}
}
worker = cluster.fork(options);

@@ -49,2 +66,5 @@ worker.cservice = options;

// track every worker by pid
cservice.locals.workerProcesses[worker.process.pid] = worker;
return worker;

@@ -55,3 +75,3 @@ }

var worker = this;
if (!msg || !msg.cservice || !msg.cservice.cmd) {
if (!cservice.msgBus.isValidMessage(msg)) {
return; // ignore invalid cluster-service messages

@@ -64,2 +84,10 @@ }

case "workerReady":
var version = cservice.locals.proxy.versions[worker.cservice.version];
if (version) {
// if version detected within worker, flag as online
version.online = true;
// notify proxy workers of version update
cservice.proxy.updateProxyWorkers();
}
if (worker.cservice.ready === false) {

@@ -77,2 +105,10 @@ // preserve preference between restarts, etc

if (args && args.length > 0) {
if (msg.cservice.cb === true) {
args.splice(1, 0, function(err, result) {
// forward response to worker that requested the trigger
cservice.msgBus.respondToMessage(msg, worker.process, err, result);
});
} else {
args.splice(1, 0, null); // no callback necessary
}
cservice.trigger.apply(cservice, args);

@@ -79,0 +115,0 @@ }

@@ -6,3 +6,3 @@ var cservice = require("../cluster-service"),

colors = require("colors"),
extend = require("extend");
util = require("util");

@@ -16,3 +16,3 @@ module.exports = exports = start;

if (cluster.isWorker === true) {
// ignore starts if not master. do NOT invoke masterCb, as that is
// ignore starts if not master. do NOT invoke masterCb, as that is
// reserved for master callback

@@ -38,5 +38,5 @@

var fileOptions = JSON.parse(fs.readFileSync(options.config));
options = extend(true, fileOptions, options);
options = util._extend(fileOptions, options);
}
cservice.locals.options = extend(true, cservice.locals.options, options);
cservice.locals.options = util._extend(cservice.locals.options, options);
if ("workers" in options) { // overwrite workers if provided

@@ -66,3 +66,3 @@ cservice.locals.options.workers = options.workers;

}
process.exit(1); // graceful exit
process.exit(0); // graceful exit
});

@@ -78,3 +78,3 @@ } else {

cservice.log("Startup failed, exiting...".warn);
process.exit(1); // graceful exit
process.exit(0); // graceful exit
}

@@ -98,2 +98,2 @@ }

}
}
}

@@ -13,16 +13,16 @@ var cservice = require("../cluster-service");

cservice.trigger("shutdown", function() {
if (cb) cb(null, "Shutting down...");
require("./http-server").close();
if (cservice.options.cli === true) {
process.exit(1);
}
handleWorkersExited(cb);
}, "all", timeout);
} else { // gracefully shutdown
if (cb) cb(null, "Shutting down...");
require("./http-server").close();
cservice.locals.state = 0;
if (cservice.options.cli === true) {
process.exit(1);
}
handleWorkersExited(cb);
}
}
function handleWorkersExited(cb) {
if (cb) cb(null, "Shutting down...");
require("./http-server").close();
cservice.locals.state = 0;
if (cservice.options.cli === true) {
process.exit(1);
}
}

@@ -6,7 +6,10 @@ var cservice = require("../cluster-service");

function trigger(eventName, cb) {
var args = Array.prototype.slice.call(arguments);
if (cservice.isWorker === true) {
process.send({
cservice: {
cmd: "trigger",
args: Array.prototype.slice.call(arguments)
args.splice(1, 1); // remove cb from args if it exists
cservice.msgBus.sendMessage("trigger", { args: args, cb: true },
null, function(err, result) {
// wait for response from master
if (typeof cb === "function") {
cb(err, result);
}

@@ -17,8 +20,7 @@ });

var evt = cservice.locals.events[eventName];
var args;
var i;
if (!evt) {
// invoke callback if provided instead of throwing
if (typeof arguments[1] === "function") {
arguments[1]("Event " + eventName + " not found");
if (typeof cb === "function") {
cb("Event " + eventName + " not found");
} else {

@@ -29,10 +31,5 @@ throw new Error("Event " + eventName + " not found");

args = [evt]; // event is always first arg
if (arguments.length > 1) { // grab custom args
for (i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
}
args.splice(0, 1, evt);
if (args.length < 2 || typeof args[1] !== "function") {
if (typeof cb !== "function") {
// auto-inject dummy callback if not provided

@@ -44,5 +41,4 @@ args.splice(1, 0, function DummyCallback(err, results) {

//exports.log("trigger." + eventName + ".args=" + args.length);
// invoke event callback
return evt.cb.apply(null, args);
}
var cservice = require("../cluster-service"),
cluster = require("cluster"),
util = require("util"),
messageBus = require("./message-bus"),
onWorkerStop = null;

@@ -30,3 +31,3 @@

}
onWorkerStop = options.onWorkerStop;

@@ -36,14 +37,14 @@

// allow worker to inform the master when ready to speed up initialization
process.send({
cservice: {
cmd: "workerReady",
// allow worker to inform the master when ready to speed up initialization
process.send(
messageBus.createMessage("workerReady", {
onStop: (typeof options.onWorkerStop === "function")
}
});
})
);
}
function onMessageFromMaster(msg) {
if (!msg || !msg.cservice || !msg.cservice.cmd) {
return; // ignore invalid cluster-service messages
if (!messageBus.isValidMessage(msg) ||
cservice.msgBus.processMessage(msg)) {
return;
}

@@ -50,0 +51,0 @@

@@ -7,3 +7,5 @@ var cservice = require("../cluster-service"),

exports.getByPID = getByPID;
exports.getByPIDFromCache = getByPIDFromCache;
exports.exitGracefully = exitGracefully;
exports.demote = demote;

@@ -17,7 +19,11 @@ function get() {

worker = cworkers[k];
worker.pid = worker.process.pid;
workers.push(worker);
if ((!worker.isDead || !worker.isDead())
&& worker.suicide !== true
&& worker.state !== "none") {
worker.pid = worker.process.pid;
workers.push(worker);
}
}
workers.send=send;
workers.send = send;

@@ -27,3 +33,3 @@ return workers;

// i hate O(N) lookups, but not hit hard enough to worry about optimizing at
// i hate O(N) lookups, but not hit hard enough to worry about optimizing at
// this point. freshness is more important

@@ -44,12 +50,15 @@ function getByPID(pid) {

function getByPIDFromCache(pid) {
return cservice.locals.workers[pid];
}
function monitor() {
process.on("message", function(msg) {
if (!msg || typeof msg.cservice !== "string") {
if (!cservice.msgBus.isValidMessage(msg)) {
return; // end
}
switch (msg.cservice) {
switch (msg.cservice.cmd) {
case "processDetails":
process.send({
cservice: "processDetails",
process.send(cservice.msgBus.createMessage("processDetails", {
processDetails: {

@@ -61,3 +70,3 @@ memory: process.memoryUsage(),

}
});
}));
break;

@@ -68,2 +77,28 @@ }

function demote() {
// only demote if:
// 1. process.getgid is defined (not Windows)
// 2. Running as root
// 3. workerGid is string and not a proxy worker
var gid = cservice.options.workerGid || 'nobody';
var uid = cservice.options.workerUid || 'nobody';
if (process.getgid && process.getgid() === 0) {
if ( // but do not auto-demote proxy
// workers as they require priveledged port access
cluster.worker.env.type !== "proxy" &&
typeof cservice.options.workerGid === 'string'
) {
process.setgid(gid);
process.setuid(uid);
} else {
cservice.log(
"Worker running as root. Not advised for Production." +
" Consider workerGid & workerUid options.".warn
);
}
}
}
/**

@@ -77,3 +112,3 @@ * This is shorthand for:

this.forEach(function(worker){
//worker.send.apply(worker, [].slice.apply(arguments));
worker.send.apply(worker, [].slice.apply(arguments));
});

@@ -84,3 +119,3 @@ }

// inform the worker to exit gracefully
worker.send({cservice: {cmd: "onWorkerStop"}});
worker.send(cservice.msgBus.createMessage("onWorkerStop"));
}
{
"name": "cluster-service",
"version": "1.0.5",
"version": "2.0.0-alpha1",
"author": {

@@ -20,13 +20,14 @@ "name": "Aaron Silvas",

"dependencies": {
"async": "~0.8.0",
"optimist": ">=0.6.0",
"colors": ">=0.6.2",
"extend": ">=1.1.x"
"async": "^0.9.0",
"colors": "^1.0.3",
"http-proxy": "^1.10.1",
"optimist": "^0.6.1"
},
"devDependencies": {
"mocha": "~1.12.0",
"request": ">=2.21.0",
"istanbul": "~0.1.43",
"sinon": "1.7.3",
"jshint": "2.3.x"
"6to5": "^3.6.5",
"istanbul": "^0.3.13",
"jshint": "^2.7.0",
"mocha": "^2.2.4",
"request": "^2.55.0",
"sinon": "^1.14.1"
},

@@ -33,0 +34,0 @@ "repository": {

@@ -28,4 +28,4 @@ # cluster-service

http://x.co/bpnodevid
## Getting Started

@@ -81,3 +81,3 @@

help {command}
We can also issue commands from a seperate process, or even a remote machine (assuming proper access):

@@ -100,3 +100,3 @@

cservice "server.js" --accessKey "123"
cservice "server.js" --accessKey "123"

@@ -156,5 +156,7 @@ Or via JSON config:

* `master` - An optional module to execute for the master process only, once ```start``` has been completed.
* `proxy` - Optional path to a JSON config file to enable Proxy Support.
* `workerGid` - Group ID to assign to child worker processes (recommended `nobody`).
* `workerUid` - User ID to assign to child worker processes (recommended `nobody`).
## Console & REST API

@@ -184,5 +186,5 @@

cservice --run "help" --accessKey "lksjdf982734"
## Cluster Commands

@@ -193,8 +195,8 @@

* `start workerPath [cwd] { [timeout:60] }` - Gracefully start service, one worker at a time.
* `restart all|pid { [timeout:60] }` - Gracefully restart service, waiting up to timeout before terminating workers.
* `shutdown all|pid { [timeout:60] }` - Gracefully shutdown service, waiting up to timeout before terminating workers.
* `start workerPath [cwd] { [timeout:60000] }` - Gracefully start service, one worker at a time.
* `restart all|pid { [timeout:60000] }` - Gracefully restart service, waiting up to timeout before terminating workers.
* `shutdown all|pid { [timeout:60000] }` - Gracefully shutdown service, waiting up to timeout before terminating workers.
* `exit now` - Forcefully exits the service.
* `help [cmd]` - Get help.
* `upgrade all|pid workerPath { [cwd] [timeout:60] }` - Gracefully upgrade service, one worker at a time. (continuous deployment support).
* `upgrade all|pid workerPath { [cwd] [timeout:60000] }` - Gracefully upgrade service, one worker at a time. (continuous deployment support).
* `workers` - Returns list of active worker processes.

@@ -219,8 +221,8 @@ * `health` - Returns health of service. Can be overidden by service to expose app-specific data.

};
cservice.on("test", function(evt, cb, testScript, timeout) { // we're overriding the "test" command
// arguments
// do something, no callback required (events may optionally be triggered)
};
};
// can also issue commands programatically

@@ -263,5 +265,5 @@ cservice.trigger("custom", function(err) { /* my callback */ }, "arg1value", "arg2value");

});
## Access Control

@@ -286,4 +288,71 @@

## Proxy Support
Proxy mode specifically caters to Web Servers that you want to enable automatic
versioning of your service. Any version requested (via `versionHeader`) that is
not yet loaded will automatically have a worker process spun up with the new
version, and after ready, the proxy will route to that worker.
Every version of your app *must* adhere to the `PROXY_PORT` environment
variable like so:
require("http").createServer(function(req, res) {
res.writeHead(200);
res.end("Hello world!");
}).listen(process.env.PROXY_PORT || 3000 /* port to use when not running in proxy mode */);
### Proxy Options
* `versionPath` (default: same directory as proxy JSON config) - Can override
to point to a new version folder.
* `defaultVersion` - The version (folder name) that is currently active/live.
If you do not initially set this option, making a request to the Proxy without
a `versionHeader` will result in a 404 (Not Found) since there is no active/live
version.
Upgrades will automatically update this option to the latest upgraded version.
* `versionHeader` (default: `x-version`) - HTTP Header to use when determining
non-default version to route to.
* `workerFilename` (default: `worker.js`) - Filename of worker file.
* `bindings` (default: `[{ port: 80, workerCount: 2 }]`) - An array of `Proxy Bindings`.
* `versionPorts` (default: `11000-12000`) - Reserved port range that can be used to
assign ports to different App versions via `PROXY_PORT`.
* `nonDefaultWorkerCount` (default: 1) - If a version is requested that is not
a default version, this will be the number of worker processes dedicated to
that version.
* `nonDefaultWorkerIdleTime` (default: 3600) - The number of seconds of inactivity
before a non-default version will have its workers shut down.
### Proxy Bindings
Binding options:
* `port` - Proxy port to bind to.
* `workerCount` (default: 2) - Number of worker processes to use for this
binding. Typically more than 2 is unnecessary for a proxy, and less than 2
is a potential failure point if a proxy worker ever goes down.
* `tlsOptions` - TLS Options if binding for HTTPS.
* `key` - Filename that contains the Certificate Key.
* `cert` - Filename that contains the Certificate.
* `pem` - Filename that contains the Certificate PEM if applicable.
A full list of TLS Options: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener
### Proxy Commands
Work like any other `Cluster Commands`.
* `proxy start configPath` - Start the proxy using the provided JSON config file.
* `proxy stop` - Shutdown the proxy service.
* `proxy version workerVersion workerCount` - Set a given App version to the
desired number of worker processes. If the version is not already running,
it will be started. If 2 workers for the version are already running, and you
request 4, 2 more will be started. If 4 workers for the version are already
running, and you request 2, 2 will be stopped.
* `proxy promote workerVersion workerCount` - Workers identical to the
`proxy version` command, except this will flag the version as active/live,
resulting in the Proxy Config file being updated with the new `defaultVersion`.
* `proxy info` - Fetch information about the proxy service.
## Tests & Code Coverage

@@ -297,3 +366,3 @@

Now test:
Now test:

@@ -306,3 +375,3 @@ npm test

## Change Log

@@ -312,6 +381,6 @@

## License
[MIT](https://github.com/godaddy/node-cluster-service/blob/master/LICENSE.txt)
var cservice = require("../cluster-service");
var assert = require("assert");
var httpclient = require("../lib/http-client");
var extend = require("extend");
var util = require("util");
var request = require("request");

@@ -34,3 +34,3 @@

httpclient.init(
extend(cservice.options, {accessKey: "123", silentMode: true})
util._extend(cservice.options, {accessKey: "123", silentMode: true})
);

@@ -37,0 +37,0 @@ it('Health check', function(done) {

@@ -83,7 +83,10 @@ var cservice = require("../cluster-service");

it('Stop workers', function(done) {
cservice.stop(30000, function() {
cservice.stop(30000, function(err, msg) {
assert.ok(!err, 'Error: ' + err);
assert.equal(
cservice.workers.length,
0,
"0 workers expected, but " + cservice.workers.length + " found"
"0 workers expected, but "
+ cservice.workers.length
+ " found. Message: " + msg
);

@@ -90,0 +93,0 @@ done();

@@ -128,8 +128,11 @@ var cservice = require("../cluster-service");

it('Stop workers', function(done) {
cservice.stop(30000, function() {
it('Stop workers after upgrade', function(done) {
cservice.stop(30000, function(err, msg) {
assert.ok(!err, 'Received error ' + err);
assert.equal(
cservice.workers.length,
0,
"0 workers expected, but " + cservice.workers.length + " found"
"0 workers expected, but "
+ cservice.workers.length
+ " found. Message: " + msg
);

@@ -180,7 +183,10 @@ done();

it('Stop workers', function(done) {
cservice.stop(30000, function() {
cservice.stop(30000, function(err, msg) {
assert.ok(!err, 'Received error ' + err);
assert.equal(
cservice.workers.length,
0,
"0 workers expected, but " + cservice.workers.length + " found"
"0 workers expected, but "
+ cservice.workers.length
+ " found. Message: " + msg
);

@@ -191,2 +197,2 @@ done();

});
}
}
var cservice = require("../../cluster-service");
cservice.workerReady(false);
setTimeout(function() {
cservice.workerReady();
}, 10000);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc