Skip to content

Commit

Permalink
[refactor] Significant refactor to how forever works in the rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
indexzero committed Jan 5, 2012
1 parent 2ead453 commit 8f9f0ad
Show file tree
Hide file tree
Showing 25 changed files with 392 additions and 315 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules/
node_modules/*
npm-debug.log
.*.sw[op]
test/fixtures/*.log
23 changes: 18 additions & 5 deletions lib/forever.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ forever.log.cli();
// Export `version` and important Prototypes from `lib/forever/*`
//
forever.initialized = false;
forever.plugins = utile.requireDirLazy(path.join(__dirname, '/forever/plugins'));
forever.root = path.join(process.env.HOME || '/root', '.forever');
forever.config = new nconf.File({ file: path.join(forever.root, 'config.json') });
forever.Forever = forever.Monitor = require('./forever/monitor').Monitor;
Expand Down Expand Up @@ -98,15 +99,24 @@ function getAllProcesses(callback) {

socket.connect(fullPath, function (err) {
if (err) {
return next(err);
next(err);
}

socket.data(['data'], function (data) {
socket.dataOnce(['data'], function (data) {
next(null, data);
socket.end();
});

socket.send(['data']);
});

socket.on('error', function (err) {
if (err.code === 'ECONNREFUSED') {
fs.unlink(fullPath, function () {
next();
});
}
});
}

getSockets(sockPath, function (err, sockets) {
Expand All @@ -115,7 +125,7 @@ function getAllProcesses(callback) {
}

async.map(sockets, getProcess, function (err, processes) {
callback(processes);
callback(processes.filter(Boolean));
});
});
}
Expand Down Expand Up @@ -331,12 +341,15 @@ forever.startServer = function () {
}
});

async.forEach(monitors, function (monitor, next) {
async.map(monitors, function (monitor, next) {
var worker = new forever.Worker({
monitor: monitor,
sockPath: forever.config.get('sockPath')
});
worker.start(next);

worker.start(function (err) {
return err ? next(err) : next(null, worker);
});
}, callback || function () {});
};

Expand Down
1 change: 0 additions & 1 deletion lib/forever/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ var fs = require('fs'),
path = require('path'),
flatiron = require('flatiron'),
cliff = require('cliff'),
daemon = require('daemon'),
forever = require('../forever');

var cli = exports;
Expand Down
109 changes: 62 additions & 47 deletions lib/forever/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,37 @@
*
*/

var util = require('util'),
var events = require('events'),
fs = require('fs'),
path = require('path'),
spawn = require('child_process').spawn,
winston = require('winston'),
broadway = require('broadway'),
psTree = require('ps-tree'),
winston = require('winston'),
utile = require('utile'),
forever = require('../forever');

//
// ### function Monitor (script, options)
// #### @script {string} Location of the target script to run.
// #### @options {Object} Configuration for this instance.
// Creates a new instance of forever with specified params.
// Creates a new instance of forever with specified `options`.
//
var Monitor = exports.Monitor = function (script, options) {
//
// Simple bootstrapper for attaching logger
// and watch plugins by default. Other plugins
// can be attached through `monitor.use(plugin, options)`.
//
function bootstrap(monitor) {
forever.plugins.logger.attach.call(monitor, options);

if (options.watch) {
forever.plugins.watch.attach.call(monitor, options);
}
}

var self = this;
broadway.App.call(this);

//
// Setup basic configuration options
Expand All @@ -39,6 +51,22 @@ var Monitor = exports.Monitor = function (script, options) {
this.childExists = false;
this.times = 0;

//
// Setup log files and logger for this instance.
//
this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
this.outFile = options.outFile;
this.errFile = options.errFile;
this.logger = options.logger || new (winston.Logger)({
transports: [new winston.transports.Console({ silent: this.silent })]
});

//
// Extend from the winston logger.
//
this.logger.extend(this);
this.log = fs.createWriteStream(this.logFile, { flags: 'a+', encoding: 'utf8', mode: '0666' });

//
// Setup restart timing. These options control how quickly forever restarts
// a child process as well as when to kill a "spinning" process
Expand All @@ -51,14 +79,21 @@ var Monitor = exports.Monitor = function (script, options) {
// to that command.
//
this.command = options.command || 'node';
this.options = options.options || [];
this.args = options.options || [];
this.spawnWith = options.spawnWith || {};
this.sourceDir = options.sourceDir;
this.cwd = options.cwd || null;
this.env = options.env || {};
this.hideEnv = options.hideEnv || [];
this._env = options.env || {};
this._hideEnv = {};

//
// Setup watch configuration options
//
this.watchIgnoreDotFiles = options.watchIgnoreDotFiles || true;
this.watchIgnorePatterns = options.watchIgnorePatterns || [];
this.watchDirectory = options.watchDirectory || this.sourceDir;

//
// Create a simple mapping of `this.hideEnv` to an easily indexable
// object
Expand All @@ -67,47 +102,27 @@ var Monitor = exports.Monitor = function (script, options) {
self._hideEnv[key] = true;
});

//
// Setup logger for this instance.
//
this.logger = options.logger || new (winston.Logger)({
transports: [new winston.transports.Console({ silent: this.silent })]
});

//
// Extend from the winston logger.
//
this.logger.extend(this);

if (Array.isArray(script)) {
this.command = script[0];
this.options = script.slice(1);
this.args = script.slice(1);
}
else {
this.options.unshift(script);
this.args.unshift(script);
}

if (this.sourceDir) {
this.options[0] = path.join(this.sourceDir, this.options[0]);
this.args[0] = path.join(this.sourceDir, this.args[0]);
}

//
// Last if any hooks have been passed in attach
// this instance to them
// Bootstrap this instance now that options
// have been set
//
if (options.hooks && options.hooks.length > 0) {
options.hooks.forEach(function (hook) {
if (typeof hook === 'function') {
return hook(self);
}

hook.attach(self);
});
}
broadway.App.call(this, { bootstrapper: { bootstrap: bootstrap } });
};

// Inherit from events.EventEmitter
util.inherits(Monitor, broadway.App);
utile.inherits(Monitor, broadway.App);

//
// ### function start ([restart])
Expand All @@ -128,7 +143,7 @@ Monitor.prototype.start = function (restart) {
child = this.trySpawn();
if (!child) {
process.nextTick(function () {
self.emit('error', new Error('Target script does not exist: ' + self.options[0]));
self.emit('error', new Error('Target script does not exist: ' + self.args[0]));
});
return this;
}
Expand All @@ -140,15 +155,14 @@ Monitor.prototype.start = function (restart) {
process.nextTick(function () {
self.emit(restart ? 'restart' : 'start', self, self.data);
});

child.on('exit', function (code) {
var spinning = Date.now() - self.ctime < self.minUptime;
self.warn('Forever detected script exited with code: ' + code);

function letChildDie() {
self.running = false;
self.forceStop = false;

self.emit('exit', self, spinning);
}

Expand Down Expand Up @@ -187,7 +201,7 @@ Monitor.prototype.start = function (restart) {
Monitor.prototype.trySpawn = function () {
if (this.command === 'node' || (this.checkFile && !this.childExists)) {
try {
var stats = fs.statSync(this.options[0]);
var stats = fs.statSync(this.args[0]);
this.childExists = true;
}
catch (ex) {
Expand All @@ -198,7 +212,7 @@ Monitor.prototype.trySpawn = function () {
this.spawnWith.cwd = this.cwd || this.spawnWith.cwd;
this.spawnWith.env = this._getEnv();

return spawn(this.command, this.options, this.spawnWith);
return spawn(this.command, this.args, this.spawnWith);
};

//
Expand All @@ -221,10 +235,10 @@ Monitor.prototype.__defineGetter__('data', function () {
childData = {
ctime: this.ctime,
command: this.command,
file: this.options[0],
file: this.args[0],
foreverPid: process.pid,
logFile: this.logFile,
options: this.options.slice(1),
options: this.args.slice(1),
pid: this.child.pid,
silent: this.silent,
uid: this.uid,
Expand Down Expand Up @@ -320,8 +334,10 @@ Monitor.prototype.kill = function (forceStop) {
//these should be ignored, and then we should emit that it is dead
}
});

self.emit('stop', this.childData);
}, this.killTTL);

child.on('exit', function () {
clearTimeout(timer);
});
Expand Down Expand Up @@ -351,18 +367,17 @@ Monitor.prototype._getEnv = function () {

//
// Mixin the key:value pairs from `process.env` and the custom
// environment variables in `this.env`.
// environment variables in `this._env`.
//
Object.keys(process.env).forEach(function (key) {
if (!self._hideEnv[key]) {
addKey(key, process.env);
}
});

Object.keys(this.env).forEach(function (key) {
addKey(key, self.env);
Object.keys(this._env).forEach(function (key) {
addKey(key, self._env);
});

return merged;
};

};
Loading

1 comment on commit 8f9f0ad

@mmalecki
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much win

Please sign in to comment.