daemonize with all cli parameters

pull/35/head v1.2.0
Maciej Winnicki 2014-05-29 01:14:05 +02:00
parent c3fbf35568
commit 9ace721324
6 changed files with 249 additions and 55 deletions

View File

@ -3,43 +3,18 @@
var connect = require('connect');
var cookieParser = require('cookie');
var crypto = require('crypto');
var daemon = require('daemon');
var fs = require('fs');
var program = require('commander');
var sanitizer = require('validator').sanitize;
var socketio = require('socket.io');
var tail = require('./lib/tail');
var connectBuilder = require('./lib/connect_builder');
var program = require('./lib/options_parser');
var serverBuilder = require('./lib/server_builder');
var daemonize = require('./lib/daemonize');
program
.version(require('./package.json').version)
.usage('[options] [file ...]')
.option('-h, --host <host>', 'listening host, default 0.0.0.0', String, '0.0.0.0')
.option('-p, --port <port>', 'listening port, default 9001', Number, 9001)
.option('-n, --number <number>', 'starting lines number, default 10', Number, 10)
.option('-l, --lines <lines>', 'number on lines stored in browser, default 2000', Number, 2000)
.option('-t, --theme <theme>', 'name of the theme (default, dark)', String, 'default')
.option('-d, --daemonize', 'run as daemon')
.option('-U, --user <username>',
'Basic Authentication username, option works only along with -P option', String, false)
.option('-P, --password <password>',
'Basic Authentication password, option works only along with -U option', String, false)
.option('-k, --key <key.pem>',
'Private Key for HTTPS, option works only along with -c option',
String, false)
.option('-c, --certificate <cert.pem>',
'Certificate for HTTPS, option works only along with -k option',
String, false)
.option('--pid-path <path>',
'if run as daemon file that will store the process id, default /var/run/frontail.pid',
String, '/var/run/frontail.pid')
.option('--log-path <path>', 'if run as daemon file that will be used as a log, default /dev/null',
String, '/dev/null')
.option('--ui-hide-topbar', 'hide topbar (log file name and search box)')
.option('--ui-no-indent', 'don\'t indent log lines')
.parse(process.argv);
/**
* Parse args
*/
program.parse(process.argv);
if (program.args.length === 0) {
console.error('Arguments needed, use --help');
process.exit();
@ -56,27 +31,10 @@ var files = program.args.join(' ');
var filesNamespace = crypto.createHash('md5').update(files).digest('hex');
if (program.daemonize) {
/**
* Daemonize process
*/
var logFile = fs.openSync(program.logPath, 'a');
var args = ['-p', program.port, '-n', program.number, '-l', program.lines, '-t', program.theme];
if (doAuthorization) {
args = args.concat(['-U', program.user, '-P', program.password]);
}
args = args.concat(program.args);
var proc = daemon.daemon(
__filename,
args,
{
stdout: logFile,
stderr: logFile
}
);
fs.writeFileSync(program.pidPath, proc.pid);
daemonize(__filename, program, {
doAuthorization: doAuthorization,
doSecure: doSecure
});
} else {
/**
* HTTP(s) server setup

54
lib/daemonize.js Normal file
View File

@ -0,0 +1,54 @@
'use strict';
var daemon = require('daemon');
var fs = require('fs');
var defaultOptions = {
doAuthorization: false,
doSecure: false
};
module.exports = function (script, params, options) {
options = options || defaultOptions;
var logFile = fs.openSync(params.logPath, 'a');
var args = [
'-h', params.host,
'-p', params.port,
'-n', params.number,
'-l', params.lines,
'-t', params.theme
];
if (options.doAuthorization) {
args.push(
'-U', params.user,
'-P', params.password
);
}
if (options.doSecure) {
args.push(
'-k', params.key,
'-c', params.certificate
);
}
if (params.uiHideTopbar) {
args.push('--ui-hide-topbar');
}
if (!params.uiIndent) {
args.push('--ui-no-indent');
}
args = args.concat(params.args);
var proc = daemon.daemon(script, args, {
stdout: logFile,
stderr: logFile
});
fs.writeFileSync(params.pidPath, proc.pid);
};

32
lib/options_parser.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
var program = require('commander');
program
.version(require('../package.json').version)
.usage('[options] [file ...]')
.option('-h, --host <host>', 'listening host, default 0.0.0.0', String, '0.0.0.0')
.option('-p, --port <port>', 'listening port, default 9001', Number, 9001)
.option('-n, --number <number>', 'starting lines number, default 10', Number, 10)
.option('-l, --lines <lines>', 'number on lines stored in browser, default 2000', Number, 2000)
.option('-t, --theme <theme>', 'name of the theme (default, dark)', String, 'default')
.option('-d, --daemonize', 'run as daemon')
.option('-U, --user <username>',
'Basic Authentication username, option works only along with -P option', String, false)
.option('-P, --password <password>',
'Basic Authentication password, option works only along with -U option', String, false)
.option('-k, --key <key.pem>',
'Private Key for HTTPS, option works only along with -c option',
String, false)
.option('-c, --certificate <cert.pem>',
'Certificate for HTTPS, option works only along with -k option',
String, false)
.option('--pid-path <path>',
'if run as daemon file that will store the process id, default /var/run/frontail.pid',
String, '/var/run/frontail.pid')
.option('--log-path <path>', 'if run as daemon file that will be used as a log, default /dev/null',
String, '/dev/null')
.option('--ui-hide-topbar', 'hide topbar (log file name and search box)')
.option('--ui-no-indent', 'don\'t indent log lines');
module.exports = program;

View File

@ -1,6 +1,6 @@
{
"name": "frontail",
"version": "1.1.0",
"version": "1.2.0",
"description": "realtime log stream in the browser",
"homepage": "https://github.com/mthenw/frontail",
"author": "Maciej Winnicki <maciej.winnicki@gmail.com>",
@ -16,7 +16,7 @@
},
"devDependencies": {
"jshint": "~2.3.0",
"should": "~2.0.2",
"should": "~3.3.2",
"mocha": "~1.13.0",
"temp": "~0.6.0",
"sinon": "~1.7.3",

View File

@ -70,7 +70,7 @@ describe('connectBuilder', function () {
.expect('<head><title>/testfile</title></head>', done);
});
it('builds app that sets socket.io namespace based on files', function (done) {
it('should build app that sets socket.io namespace based on files', function (done) {
var app = connectBuilder()
.index(__dirname + '/fixtures/index_with_ns', '/testfile', 'ns', 'dark')
.build();

150
test/daemonize.js Normal file
View File

@ -0,0 +1,150 @@
'use strict';
require('should');
var daemon = require('daemon');
var optionsParser = require('../lib/options_parser');
var daemonize = require('../lib/daemonize');
var sinon = require('sinon');
var fs = require('fs');
describe('daemonize', function () {
beforeEach(function () {
sinon.stub(daemon, 'daemon');
daemon.daemon.returns({pid: 1000});
sinon.stub(fs, 'writeFileSync');
sinon.stub(fs, 'openSync');
});
afterEach(function () {
daemon.daemon.restore();
fs.writeFileSync.restore();
fs.openSync.restore();
});
describe('should daemon', function () {
it('current script', function () {
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[0].should.match('script');
});
it('with hostname', function () {
optionsParser.parse(['node', '/path/to/frontail', '-h', '127.0.0.1']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['-h', '127.0.0.1']);
});
it('with port', function () {
optionsParser.parse(['node', '/path/to/frontail', '-p', '80']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['-p', 80]);
});
it('with lines number', function () {
optionsParser.parse(['node', '/path/to/frontail', '-n', '1']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['-n', 1]);
});
it('with lines stored in browser', function () {
optionsParser.parse(['node', '/path/to/frontail', '-l', '1']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['-l', 1]);
});
it('with theme', function () {
optionsParser.parse(['node', '/path/to/frontail', '-t', 'dark']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['-t', 'dark']);
});
it('with authorization', function () {
optionsParser.parse(['node', '/path/to/frontail', '-U', 'user', '-P', 'passw0rd']);
daemonize('script', optionsParser, {doAuthorization: true});
daemon.daemon.lastCall.args[1].should.containDeep(['-U', 'user', '-P', 'passw0rd']);
});
it('without authorization if option doAuthorization not passed', function () {
optionsParser.parse(['node', '/path/to/frontail', '-U', 'user', '-P', 'passw0rd']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.not.containDeep(['-U', 'user', '-P', 'passw0rd']);
});
it('with secure connection', function () {
optionsParser.parse(['node', '/path/to/frontail', '-k', 'key.file', '-c', 'cert.file']);
daemonize('script', optionsParser, {doSecure: true});
daemon.daemon.lastCall.args[1].should.containDeep(['-k', 'key.file', '-c', 'cert.file']);
});
it('without secure connection if option doSecure not passed', function () {
optionsParser.parse(['node', '/path/to/frontail', '-k', 'key.file', '-c', 'cert.file']);
daemonize('script', optionsParser, {doSecure: true});
daemon.daemon.lastCall.args[1].should.containDeep(['-k', 'key.file', '-c', 'cert.file']);
});
it('with hide-topbar option', function () {
optionsParser.parse(['node', '/path/to/frontail', '--ui-hide-topbar']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['--ui-hide-topbar']);
});
it('with no-indent option', function () {
optionsParser.parse(['node', '/path/to/frontail', '--ui-no-indent']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['--ui-no-indent']);
});
it('with file to tail', function () {
optionsParser.parse(['node', '/path/to/frontail', '/path/to/file']);
daemonize('script', optionsParser);
daemon.daemon.lastCall.args[1].should.containDeep(['/path/to/file']);
});
});
it('should write pid to pidfile', function () {
optionsParser.parse(['node', '/path/to/frontail', '--pid-path', '/path/to/pid']);
daemonize('script', optionsParser);
fs.writeFileSync.lastCall.args[0].should.be.equal('/path/to/pid');
fs.writeFileSync.lastCall.args[1].should.be.equal(1000);
});
it('should log to file', function () {
optionsParser.parse(['node', '/path/to/frontail', '--log-path', '/path/to/log']);
fs.openSync.returns('file');
daemonize('script', optionsParser);
fs.openSync.lastCall.args[0].should.equal('/path/to/log');
fs.openSync.lastCall.args[1].should.equal('a');
daemon.daemon.lastCall.args[2].should.eql({
stdout: 'file',
stderr: 'file'
});
});
});